Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c8266b4
run rustfmt
hellow554 Feb 7, 2022
fedfdd9
remove unused imports
hellow554 Feb 7, 2022
5599b11
fix clippy::redundant_field_names
hellow554 Feb 7, 2022
1e69ddb
fix clippy::collapsible_if and clippy::collapsible_else_if
hellow554 Feb 7, 2022
84621cb
fix clippy::write_with_newline and clippy::write_literal
hellow554 Feb 7, 2022
061fcc7
fix clippy::clone_on_copy
hellow554 Feb 7, 2022
e8446ac
fix clippy::question_mark
hellow554 Feb 7, 2022
5463bcf
fix clippy::len_zero
hellow554 Feb 7, 2022
aaea207
fix clippy::needless_bool
hellow554 Feb 7, 2022
38639f8
fix clippy::into_iter_on_ref
hellow554 Feb 7, 2022
6fa7583
fix clippy::needless_lifetimes
hellow554 Feb 7, 2022
b041ec7
fix clippy::from_over_into
hellow554 Feb 7, 2022
50bf433
fix clippy::needless_return
hellow554 Feb 7, 2022
0f76e96
fix clippy::needless_borrow
hellow554 Feb 7, 2022
388d19a
fix clippy::manual_range_contains
hellow554 Feb 7, 2022
f0bc8e3
fix clippy::needless_collect
hellow554 Feb 7, 2022
454d369
fix clippy::iter_nth_zero
hellow554 Feb 7, 2022
a246fce
fix clippy::filter_next
hellow554 Feb 7, 2022
1e5bb48
fix clippy::stable_sort_primitive
hellow554 Feb 7, 2022
bd30abc
fix clippy::map_flatten
hellow554 Feb 7, 2022
441fd7c
fix clippy::option_map_unit_fn
hellow554 Feb 7, 2022
4fa57ad
fix clippy::nonminimal_bool
hellow554 Feb 7, 2022
ede868e
fix clippy::len_zero
hellow554 Feb 7, 2022
4071454
fix clippy::identity_op
hellow554 Feb 7, 2022
923bb78
fix clippy::while_let_on_iterator
hellow554 Feb 7, 2022
a6d1f7b
fix clippy::needless_range_loop
hellow554 Feb 7, 2022
71fed64
fix clippy::ptr_arg
hellow554 Feb 7, 2022
0977b52
smaller fixes
hellow554 Feb 7, 2022
ea39ce2
fix more clippy issues in tests
hellow554 Feb 7, 2022
c93c8f2
fix clippy::use_self
hellow554 Feb 7, 2022
8807b44
replace asterisk imports with specific ones
hellow554 Feb 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 49 additions & 24 deletions benches/sweep.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,63 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};

use flo_curves::geo::*;
use flo_curves::bezier::path::*;
use flo_curves::bezier::path::{BezierPathBuilder, BezierPathFactory, GraphPath, SimpleBezierPath};
use flo_curves::geo::{
sweep_self, BoundingBox, Bounds, Coord2, Coordinate, Coordinate2D, Coordinate3D,
};

use rand::prelude::*;
use std::cmp::{Ordering};
use std::cmp::Ordering;

fn sweep(n: usize) {
let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
let mut bounds = (0..n).into_iter()
let mut rng = StdRng::from_seed([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31,
]);
let mut bounds = (0..n)
.into_iter()
.map(|_| {
let x = rng.gen::<f64>() * 900.0;
let y = rng.gen::<f64>() * 900.0;
let w = rng.gen::<f64>() * 400.0;
let h = rng.gen::<f64>() * 400.0;

Bounds::from_min_max(Coord2(x, y), Coord2(x+w, y+h))
Bounds::from_min_max(Coord2(x, y), Coord2(x + w, y + h))
})
.collect::<Vec<_>>();
bounds.sort_by(|b1, b2| b1.min().x().partial_cmp(&b2.min().x()).unwrap_or(Ordering::Equal));
bounds.sort_by(|b1, b2| {
b1.min()
.x()
.partial_cmp(&b2.min().x())
.unwrap_or(Ordering::Equal)
});

let _ = sweep_self(bounds.iter()).collect::<Vec<_>>();
}

fn sweep_slow(n: usize) {
let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
let bounds = (0..n).into_iter()
let mut rng = StdRng::from_seed([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31,
]);
let bounds = (0..n)
.into_iter()
.map(|_| {
let x = rng.gen::<f64>() * 900.0;
let y = rng.gen::<f64>() * 900.0;
let w = rng.gen::<f64>() * 400.0;
let h = rng.gen::<f64>() * 400.0;

Bounds::from_min_max(Coord2(x, y), Coord2(x+w, y+h))
Bounds::from_min_max(Coord2(x, y), Coord2(x + w, y + h))
})
.collect::<Vec<_>>();

let mut slow_collisions = vec![];

for i1 in 0..bounds.len() {
for i2 in 0..i1 {
if i1 == i2 { continue; }
if i1 == i2 {
continue;
}

if bounds[i1].overlaps(&bounds[i2]) {
slow_collisions.push((&bounds[i1], &bounds[i2]));
Expand All @@ -50,9 +67,9 @@ fn sweep_slow(n: usize) {
}

fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath<Coord2, ()> {
let mut x = 100.0;
let mut y = 100.0;
let mut path_builder = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(x, y));
let mut x = 100.0;
let mut y = 100.0;
let mut path_builder = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(x, y));

for _ in 0..n {
let xo = rng.gen::<f64>() * 50.0;
Expand All @@ -64,10 +81,9 @@ fn create_graph_path(rng: &mut StdRng, n: usize) -> GraphPath<Coord2, ()> {
path_builder = path_builder.line_to(Coord2(x, y));
}

let path = path_builder.build();
let graph_path = GraphPath::from_path(&path, ());
let path = path_builder.build();

graph_path
GraphPath::from_path(&path, ())
}

fn detect_collisions(mut graph_path: GraphPath<Coord2, ()>) {
Expand All @@ -79,12 +95,19 @@ fn merge_paths(path1: GraphPath<Coord2, ()>, path2: GraphPath<Coord2, ()>) {
}

fn criterion_benchmark(c: &mut Criterion) {
let mut rng = StdRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
let graph_path = create_graph_path(&mut rng, 1000);
let merge_path = create_graph_path(&mut rng, 500);

c.bench_function("detect_collisions 1000", |b| b.iter(|| detect_collisions(black_box(graph_path.clone()))));
c.bench_function("merge_paths 1000", |b| b.iter(|| merge_paths(black_box(graph_path.clone()), black_box(merge_path.clone()))));
let mut rng = StdRng::from_seed([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31,
]);
let graph_path = create_graph_path(&mut rng, 1000);
let merge_path = create_graph_path(&mut rng, 500);

c.bench_function("detect_collisions 1000", |b| {
b.iter(|| detect_collisions(black_box(graph_path.clone())))
});
c.bench_function("merge_paths 1000", |b| {
b.iter(|| merge_paths(black_box(graph_path.clone()), black_box(merge_path.clone())))
});

c.bench_function("sweep 10", |b| b.iter(|| sweep(black_box(10))));
c.bench_function("sweep_slow 10", |b| b.iter(|| sweep_slow(black_box(10))));
Expand All @@ -93,7 +116,9 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("sweep_slow 100", |b| b.iter(|| sweep_slow(black_box(100))));

c.bench_function("sweep 1000", |b| b.iter(|| sweep(black_box(1000))));
c.bench_function("sweep_slow 1000", |b| b.iter(|| sweep_slow(black_box(1000))));
c.bench_function("sweep_slow 1000", |b| {
b.iter(|| sweep_slow(black_box(1000)))
});
}

criterion_group!(benches, criterion_benchmark);
Expand Down
140 changes: 74 additions & 66 deletions src/arc/circle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::super::bezier::*;
use super::super::bezier::path::*;
use super::super::bezier::path::BezierPathFactory;
use super::super::bezier::{BezierCurve, BezierCurveFactory, Coordinate, Coordinate2D, Curve};

use std::f64;

Expand All @@ -8,111 +8,116 @@ use std::f64;

///
/// Represents a circle in 2 dimensions
///
///
#[derive(Clone, Copy)]
pub struct Circle<Coord: Coordinate2D+Coordinate> {
pub struct Circle<Coord: Coordinate2D + Coordinate> {
/// The center of this circle
pub center: Coord,

/// The radius of this circle
pub radius: f64
pub radius: f64,
}

///
/// Represents an arc of a circle in 2 dimensions
///
///
#[derive(Clone, Copy)]
pub struct CircularArc<'a, Coord: 'a+Coordinate2D+Coordinate> {
pub struct CircularArc<'a, Coord: 'a + Coordinate2D + Coordinate> {
/// The circle that this is an arc of
circle: &'a Circle<Coord>,

/// The start point of this arc, in radians
start_radians: f64,

/// The end point of this arc, in radians
end_radians: f64
end_radians: f64,
}

impl<Coord: Coordinate2D+Coordinate> Circle<Coord> {
impl<Coord: Coordinate2D + Coordinate> Circle<Coord> {
///
/// Creates a new circle with a center and a radius
///
pub fn new(center: Coord, radius: f64) -> Circle<Coord> {
Circle {
center: center,
radius: radius
}
///
pub fn new(center: Coord, radius: f64) -> Self {
Self { center, radius }
}

///
/// Returns an object representing an arc from this circle
///
pub fn arc<'a>(&'a self, start_radians: f64, end_radians: f64) -> CircularArc<'a, Coord> {
///
pub fn arc(&self, start_radians: f64, end_radians: f64) -> CircularArc<Coord> {
CircularArc {
circle: self,
start_radians: start_radians,
end_radians: end_radians
circle: self,
start_radians,
end_radians,
}
}

///
/// Returns a set of bezier curves that approximate this circle
///
pub fn to_curves<Curve: BezierCurveFactory<Point=Coord>>(&self) -> Vec<Curve> {
///
pub fn to_curves<Curve: BezierCurveFactory<Point = Coord>>(&self) -> Vec<Curve> {
// Angles to put the curves at (we need 4 curves for a decent approximation of a circle)
let start_angle = f64::consts::PI/4.0;
let section_angle = f64::consts::PI/2.0;
let angles = [
start_angle,
start_angle + section_angle,
start_angle + section_angle*2.0,
start_angle + section_angle*3.0];

let start_angle = f64::consts::PI / 4.0;
let section_angle = f64::consts::PI / 2.0;
let angles = [
start_angle,
start_angle + section_angle,
start_angle + section_angle * 2.0,
start_angle + section_angle * 3.0,
];

// Convert the angles into curves
angles.iter()
.map(|angle| self.arc(*angle, angle+section_angle).to_bezier_curve())
angles
.iter()
.map(|angle| self.arc(*angle, angle + section_angle).to_bezier_curve())
.collect()
}

///
/// Returns a path that approximates this circle
///
pub fn to_path<P: BezierPathFactory<Point=Coord>>(&self) -> P {
///
pub fn to_path<P: BezierPathFactory<Point = Coord>>(&self) -> P {
let curves = self.to_curves::<Curve<_>>();

P::from_points(curves[0].start_point(), curves.into_iter().map(|curve| {
let (cp1, cp2) = curve.control_points();
let end_point = curve.end_point();
P::from_points(
curves[0].start_point(),
curves.into_iter().map(|curve| {
let (cp1, cp2) = curve.control_points();
let end_point = curve.end_point();

(cp1, cp2, end_point)
}))
(cp1, cp2, end_point)
}),
)
}
}

impl<'a, Coord: Coordinate2D+Coordinate> CircularArc<'a, Coord> {
impl<'a, Coord: Coordinate2D + Coordinate> CircularArc<'a, Coord> {
///
/// Converts this arc to a bezier curve
///
///
/// If this arc covers an angle > 90 degrees, the curve will
/// be very inaccurate.
///
pub fn to_bezier_curve<Curve: BezierCurveFactory<Point=Coord>>(&self) -> Curve {
///
pub fn to_bezier_curve<Curve: BezierCurveFactory<Point = Coord>>(&self) -> Curve {
// Algorithm described here: https://www.tinaja.com/glib/bezcirc2.pdf
// Curve for the unit arc with its center at (1,0)
let theta = self.end_radians - self.start_radians;
let (x0, y0) = ((theta/2.0).cos(), (theta/2.0).sin());
let (x1, y1) = ((4.0-x0)/3.0, ((1.0-x0)*(3.0-x0)/(3.0*y0)));
let (x2, y2) = (x1, -y1);
let (x3, y3) = (x0, -y0);
let theta = self.end_radians - self.start_radians;
let (x0, y0) = ((theta / 2.0).cos(), (theta / 2.0).sin());
let (x1, y1) = ((4.0 - x0) / 3.0, ((1.0 - x0) * (3.0 - x0) / (3.0 * y0)));
let (x2, y2) = (x1, -y1);
let (x3, y3) = (x0, -y0);

// Rotate so the curve starts at start_radians
fn rotate(x: f64, y: f64, theta: f64) -> (f64, f64) {
let (cos_theta, sin_theta) = (theta.cos(), theta.sin());

(x*cos_theta + y*sin_theta, x*-sin_theta + y*cos_theta)
(
x * cos_theta + y * sin_theta,
x * -sin_theta + y * cos_theta,
)
}

let angle = -(f64::consts::PI/2.0-(theta/2.0));
let angle = -(f64::consts::PI / 2.0 - (theta / 2.0));
let angle = angle + self.start_radians;

let (x0, y0) = rotate(x0, y0, angle);
Expand All @@ -122,17 +127,17 @@ impl<'a, Coord: Coordinate2D+Coordinate> CircularArc<'a, Coord> {

// Scale by radius
let radius = self.circle.radius;
let (x0, y0) = (x0*radius, y0*radius);
let (x1, y1) = (x1*radius, y1*radius);
let (x2, y2) = (x2*radius, y2*radius);
let (x3, y3) = (x3*radius, y3*radius);
let (x0, y0) = (x0 * radius, y0 * radius);
let (x1, y1) = (x1 * radius, y1 * radius);
let (x2, y2) = (x2 * radius, y2 * radius);
let (x3, y3) = (x3 * radius, y3 * radius);

// Translate by center
let center = &self.circle.center;
let (x0, y0) = (x0+center.x(), y0+center.y());
let (x1, y1) = (x1+center.x(), y1+center.y());
let (x2, y2) = (x2+center.x(), y2+center.y());
let (x3, y3) = (x3+center.x(), y3+center.y());
let (x0, y0) = (x0 + center.x(), y0 + center.y());
let (x1, y1) = (x1 + center.x(), y1 + center.y());
let (x2, y2) = (x2 + center.x(), y2 + center.y());
let (x3, y3) = (x3 + center.x(), y3 + center.y());

// Create the curve
let p0 = Coord::from_components(&[x0, y0]);
Expand All @@ -147,12 +152,15 @@ impl<'a, Coord: Coordinate2D+Coordinate> CircularArc<'a, Coord> {
#[cfg(test)]
mod test {
use super::*;
use std::f64;
use crate::{
bezier::path::{path_to_curves, SimpleBezierPath},
Coord2,
};

#[test]
fn can_convert_unit_arc() {
let circle = Circle::new(Coord2(0.0, 0.0), 1.0);
let arc = circle.arc(0.0, f64::consts::PI/2.0);
let circle = Circle::new(Coord2(0.0, 0.0), 1.0);
let arc = circle.arc(0.0, f64::consts::PI / 2.0);
let curve: Curve<_> = arc.to_bezier_curve();

assert!(curve.start_point().distance_to(&Coord2(0.0, 1.0)) < 0.01);
Expand All @@ -165,9 +173,9 @@ mod test {

for curve in circle.to_curves::<Curve<_>>() {
for t in 0..=10 {
let t = (t as f64)/10.0;
let t = (t as f64) / 10.0;
let p = curve.point_at_pos(t);
assert!((p.distance_to(&Coord2(0.0, 0.0))-1.0).abs() < 0.01);
assert!((p.distance_to(&Coord2(0.0, 0.0)) - 1.0).abs() < 0.01);
}
}
}
Expand All @@ -178,10 +186,10 @@ mod test {

for curve in path_to_curves::<_, Curve<_>>(&circle.to_path::<SimpleBezierPath>()) {
for t in 0..=10 {
let t = (t as f64)/10.0;
let t = (t as f64) / 10.0;
let p = curve.point_at_pos(t);
assert!((p.distance_to(&Coord2(5.0, 5.0))-4.0).abs() < 0.01);
assert!((p.distance_to(&Coord2(5.0, 5.0)) - 4.0).abs() < 0.01);
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/arc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//!
//! # Describing circular arcs
//!
//!
//! The `arc` module provides routines for describing circular arcs and converting them to bezier
//! curves.
//!
Expand Down
Loading