Skip to content

Commit

Permalink
Support stable Rust.
Browse files Browse the repository at this point in the history
  • Loading branch information
n3vu0r committed Mar 17, 2024
1 parent 3c9b3da commit ac1f0ec
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 94 deletions.
36 changes: 14 additions & 22 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
name: build
on:
schedule:
- cron: '0 5 * * *'
push:
merge_group:
pull_request:
push:
env:
CARGO_TERM_COLOR: always
jobs:
default:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: msrv
run: |
msrv=$(cargo metadata --no-deps --format-version 1 |
jq --raw-output '.packages[] | select(.name=="miniball") | .rust_version')
echo "MSRV=$msrv" >> $GITHUB_ENV
- name: toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
profile: minimal
toolchain: ${{ env.MSRV }}
components: rustfmt, rust-docs, clippy
override: true
- name: test
uses: actions-rs/cargo@v1
with:
command: test
run: cargo test
- name: clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --tests -- --deny clippy::pedantic
run: cargo clippy --tests --examples -- -D clippy::all -D clippy::pedantic -D clippy::nursery
- name: doc
uses: actions-rs/cargo@v1
with:
command: doc
run: cargo doc
- name: fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --check
run: cargo fmt --check
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
name = "miniball"
description = "Minimum enclosing ball"
authors = ["Rouven Spreckels <[email protected]>"]
version = "0.2.0"
version = "0.3.0"
rust-version = "1.61.0"
edition = "2021"
documentation = "https://docs.rs/miniball"
repository = "https://github.com/qu1x/miniball"
Expand Down Expand Up @@ -31,8 +32,7 @@ include = [
]

[dependencies]
arrayvec = "0.7.2"
nalgebra = { version = "0.32.2", features = ["rand"] }
nalgebra = { version = "0.32.4", features = ["rand"] }
stacker = "0.1.15"

[profile.test]
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@ Minimum enclosing ball.
[![Documentation][]](https://docs.rs/miniball)
[![Downloads][]](https://crates.io/crates/miniball)
[![Version][]](https://crates.io/crates/miniball)
[![Rust][]](https://www.rust-lang.org)
[![Rust][]](https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html)
[![License][]](https://mozilla.org/MPL)

[Build]: https://github.com/qu1x/miniball/actions/workflows/build.yml/badge.svg
[Documentation]: https://docs.rs/miniball/badge.svg
[Downloads]: https://img.shields.io/crates/d/miniball.svg
[Version]: https://img.shields.io/crates/v/miniball.svg
[Rust]: https://img.shields.io/badge/rust-nightly-orange.svg
[Rust]: https://img.shields.io/badge/rust-v1.61.0-brightgreen.svg
[License]: https://img.shields.io/crates/l/miniball

**NOTE**: This crate requires nightly Rust.

* Finds circumscribed *n*-ball of set of bounds.
* Finds minimum *n*-ball enclosing set of points.

Expand All @@ -33,7 +31,7 @@ See the [release history] to keep track of the development.

## License

Copyright © 2022 Rouven Spreckels <[email protected]>
Copyright © 2022-2024 Rouven Spreckels <[email protected]>

Licensed under the terms of the [`MPL-2.0`](LICENSES/MPL-2.0).

Expand Down
5 changes: 5 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Version 0.3.0 (2024-03-17)

* Replace `Point` with `OPoint` supporting static as well as dynamic dimensions.
* Replace `ArrayVec<T, { D + 1 }>` with `OVec<T, DimNameSum<D, U1>>` supporting stabe Rust.

# Version 0.2.0 (2023-04-01)

* Update dependencies.
Expand Down
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
hard_tabs = true
format_code_in_doc_comments = true
#format_code_in_doc_comments = true
50 changes: 34 additions & 16 deletions src/ball.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,63 @@
// Copyright © 2022 Rouven Spreckels <[email protected]>
// Copyright © 2022-2024 Rouven Spreckels <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use super::Enclosing;
use nalgebra::{distance_squared, Point, RealField, SMatrix, SVector};
use nalgebra::{
base::allocator::Allocator, DefaultAllocator, DimNameAdd, DimNameSum, OMatrix, OPoint, OVector,
RealField, U1,
};

/// Ball over real field `R` of dimension `D` with center and radius squared.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ball<R: RealField, const D: usize> {
#[derive(Debug, Clone, PartialEq)]
pub struct Ball<R: RealField, D: DimNameAdd<U1>>
where
DefaultAllocator: Allocator<R, D>,
{
/// Ball's center.
pub center: Point<R, D>,
pub center: OPoint<R, D>,
/// Ball's radius squared.
pub radius_squared: R,
}

impl<R: RealField, const D: usize> Enclosing<R, D> for Ball<R, D> {
impl<R: RealField + Copy, D: DimNameAdd<U1>> Copy for Ball<R, D>
where
OPoint<R, D>: Copy,
DefaultAllocator: Allocator<R, D>,
{
}

impl<R: RealField, D: DimNameAdd<U1>> Enclosing<R, D> for Ball<R, D>
where
DefaultAllocator:
Allocator<R, D> + Allocator<R, D, D> + Allocator<OPoint<R, D>, DimNameSum<D, U1>>,
<DefaultAllocator as Allocator<OPoint<R, D>, DimNameSum<D, U1>>>::Buffer: Default,
{
#[inline]
fn contains(&self, point: &Point<R, D>) -> bool {
distance_squared(&self.center, point) <= self.radius_squared
fn contains(&self, point: &OPoint<R, D>) -> bool {
(point - &self.center).norm_squared() <= self.radius_squared
}
fn with_bounds(bounds: &[Point<R, D>]) -> Option<Self> {
let length = bounds.len().checked_sub(1).filter(|&length| length <= D)?;
let points = SMatrix::<R, D, D>::from_fn(|row, column| {
fn with_bounds(bounds: &[OPoint<R, D>]) -> Option<Self> {
let length = bounds.len().checked_sub(1).filter(|&len| len <= D::USIZE)?;
let points = OMatrix::<R, D, D>::from_fn(|row, column| {
if column < length {
bounds[column + 1].coords[row].clone() - bounds[0].coords[row].clone()
} else {
R::zero()
}
});
let points = points.view((0, 0), (D, length));
let matrix = SMatrix::<R, D, D>::from_fn(|row, column| {
let points = points.view((0, 0), (D::USIZE, length));
let matrix = OMatrix::<R, D, D>::from_fn(|row, column| {
if row < length && column < length {
points.column(row).dot(&points.column(column)) * (R::one() + R::one())
} else {
R::zero()
}
});
let matrix = matrix.view((0, 0), (length, length));
let vector = SVector::<R, D>::from_fn(|row, _column| {
let vector = OVector::<R, D>::from_fn(|row, _column| {
if row < length {
points.column(row).norm_squared()
} else {
Expand All @@ -49,11 +67,11 @@ impl<R: RealField, const D: usize> Enclosing<R, D> for Ball<R, D> {
let vector = vector.view((0, 0), (length, 1));
matrix.try_inverse().map(|matrix| {
let vector = matrix * vector;
let mut center = SVector::<R, D>::zeros();
let mut center = OVector::<R, D>::zeros();
for point in 0..length {
center += points.column(point) * vector[point].clone();
}
Ball {
Self {
center: &bounds[0] + &center,
radius_squared: center.norm_squared(),
}
Expand Down
22 changes: 11 additions & 11 deletions src/deque.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2022 Rouven Spreckels <[email protected]>
// Copyright © 2022-2024 Rouven Spreckels <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
Expand Down Expand Up @@ -32,49 +32,49 @@ pub trait Deque<T> {
impl<T> Deque<T> for VecDeque<T> {
#[inline]
fn len(&self) -> usize {
VecDeque::len(self)
Self::len(self)
}

#[inline]
fn pop_front(&mut self) -> Option<T> {
VecDeque::pop_front(self)
Self::pop_front(self)
}
#[inline]
fn pop_back(&mut self) -> Option<T> {
VecDeque::pop_back(self)
Self::pop_back(self)
}

#[inline]
fn push_front(&mut self, value: T) {
VecDeque::push_front(self, value);
Self::push_front(self, value);
}
#[inline]
fn push_back(&mut self, value: T) {
VecDeque::push_back(self, value);
Self::push_back(self, value);
}
}

impl<T> Deque<T> for LinkedList<T> {
#[inline]
fn len(&self) -> usize {
LinkedList::len(self)
Self::len(self)
}

#[inline]
fn pop_front(&mut self) -> Option<T> {
LinkedList::pop_front(self)
Self::pop_front(self)
}
#[inline]
fn pop_back(&mut self) -> Option<T> {
LinkedList::pop_back(self)
Self::pop_back(self)
}

#[inline]
fn push_front(&mut self, value: T) {
LinkedList::push_front(self, value);
Self::push_front(self, value);
}
#[inline]
fn push_back(&mut self, value: T) {
LinkedList::push_back(self, value);
Self::push_back(self, value);
}
}
49 changes: 26 additions & 23 deletions src/enclosing.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
// Copyright © 2022 Rouven Spreckels <[email protected]>
// Copyright © 2022-2024 Rouven Spreckels <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use super::Deque;
use arrayvec::ArrayVec;
use nalgebra::{Point, RealField};
use super::{Deque, OVec};
use nalgebra::{
base::allocator::Allocator, DefaultAllocator, DimNameAdd, DimNameSum, OPoint, RealField, U1,
};
use stacker::maybe_grow;
use std::mem::size_of;

/// Minimum enclosing ball.
pub trait Enclosing<R: RealField, const D: usize>: Clone {
pub trait Enclosing<R: RealField, D: DimNameAdd<U1>>
where
Self: Clone,
DefaultAllocator: Allocator<R, D> + Allocator<OPoint<R, D>, DimNameSum<D, U1>>,
<DefaultAllocator as Allocator<OPoint<R, D>, DimNameSum<D, U1>>>::Buffer: Default,
{
#[doc(hidden)]
/// Guaranteed stack size per recursion step.
const RED_ZONE: usize = 32 * 1_024 + (8 * D + 2 * D.pow(2)) * size_of::<Point<R, D>>();
const RED_ZONE: usize =
32 * 1_024 + (8 * D::USIZE + 2 * D::USIZE.pow(2)) * size_of::<OPoint<R, D>>();
#[doc(hidden)]
/// New stack space to allocate if within [`Self::RED_ZONE`].
const STACK_SIZE: usize = Self::RED_ZONE * 1_024;

/// Whether ball contains `point`.
#[must_use]
fn contains(&self, point: &Point<R, D>) -> bool;
fn contains(&self, point: &OPoint<R, D>) -> bool;
/// Returns circumscribed ball with all `bounds` on surface or `None` if it does not exist.
///
/// # Example
Expand Down Expand Up @@ -52,7 +59,7 @@ pub trait Enclosing<R: RealField, const D: usize>: Clone {
/// assert_eq!(radius_squared, 3.0);
/// ```
#[must_use]
fn with_bounds(bounds: &[Point<R, D>]) -> Option<Self>;
fn with_bounds(bounds: &[OPoint<R, D>]) -> Option<Self>;

/// Returns minimum ball enclosing `points`.
///
Expand Down Expand Up @@ -123,13 +130,13 @@ pub trait Enclosing<R: RealField, const D: usize>: Clone {
/// ```
#[must_use]
#[inline]
fn enclosing_points(points: &mut impl Deque<Point<R, D>>) -> Self
where
ArrayVec<Point<R, D>, { D + 1 }>:,
{
fn enclosing_points(points: &mut impl Deque<OPoint<R, D>>) -> Self {
maybe_grow(Self::RED_ZONE, Self::STACK_SIZE, || {
Self::enclosing_points_with_bounds(points, &mut ArrayVec::new())
.expect("Empty point set")
Self::enclosing_points_with_bounds(
points,
&mut OVec::<OPoint<R, D>, DimNameSum<D, U1>>::new(),
)
.expect("Empty point set")
})
}
/// Returns minimum ball enclosing `points` with `bounds`.
Expand All @@ -138,20 +145,16 @@ pub trait Enclosing<R: RealField, const D: usize>: Clone {
#[doc(hidden)]
#[must_use]
fn enclosing_points_with_bounds(
points: &mut impl Deque<Point<R, D>>,
bounds: &mut ArrayVec<Point<R, D>, { D + 1 }>,
points: &mut impl Deque<OPoint<R, D>>,
bounds: &mut OVec<OPoint<R, D>, DimNameSum<D, U1>>,
) -> Option<Self> {
// Take point from back.
if let Some(point) = points.pop_back()
&& !bounds.is_full()
{
if let Some(point) = points.pop_back().filter(|_| !bounds.is_full()) {
let ball = maybe_grow(Self::RED_ZONE, Self::STACK_SIZE, || {
// Branch with one point less.
Self::enclosing_points_with_bounds(points, bounds)
});
if let Some(ball) = ball
&& ball.contains(&point)
{
if let Some(ball) = ball.filter(|ball| ball.contains(&point)) {
// Move point to back.
points.push_back(point);
Some(ball)
Expand All @@ -168,7 +171,7 @@ pub trait Enclosing<R: RealField, const D: usize>: Clone {
}
} else {
// Circumscribed ball with bounds.
Self::with_bounds(bounds)
Self::with_bounds(bounds.as_slice())
}
}
}
Loading

0 comments on commit ac1f0ec

Please sign in to comment.