Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curve curve intersection tylers #219

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6e0ec0f
Borrow Lyon’s implementation of curve-curve intersection
simoncozens Sep 16, 2021
f52accd
Make clippy less sad
simoncozens Sep 16, 2021
4aeaeeb
Squashed in curve-curve-intersections-point-along:
tylers-epilog Feb 1, 2022
d5d0be8
Squashed in curve-curve-intersection-cubic-quad-line:
tylers-epilog Feb 1, 2022
1375912
Squashed in curve-curve-intersection-overlap:
tylers-epilog Feb 7, 2022
dc5b41a
Squashed in curve-curve-intersection-perpendicular-fatline:
tylers-epilog Feb 24, 2022
f4cf191
Split KEEP_ENDPOINT_INTERSECTIONS into individual flags for each endp…
tylers-epilog Mar 2, 2022
84400ef
Added ability to define how much accuracy is needed
tylers-epilog Mar 3, 2022
7398c23
Removed real comparisons, only point comparisons remailn
tylers-epilog Mar 3, 2022
cb5a672
Reduced arclen epsilon to improve performance
tylers-epilog Mar 4, 2022
6e993f9
Merge branch 'master' into curve-curve-intersection-tylers
tylers-epilog Apr 1, 2022
fc0bdaf
Fixed merge with master
tylers-epilog Apr 7, 2022
1a2cb70
Fixed formatting
tylers-epilog Apr 7, 2022
c23c4ef
Moved fn baseline to ParamCurve
tylers-epilog Apr 7, 2022
5bfbf1e
Organized code to reduce ParamCurveBezierClipping spaghetti
tylers-epilog Apr 7, 2022
9f1c40f
Added disclaimer for curve intersection functions
tylers-epilog Apr 7, 2022
531d538
Fixed running tests in release mode
tylers-epilog Apr 7, 2022
5a6ac30
Fixed clippy issues
tylers-epilog Apr 19, 2022
7ddb666
Replaced vec object with splice
tylers-epilog Apr 20, 2022
64db1a8
Merge branch 'master' into curve-curve-intersection-tylers
raphlinus Aug 12, 2022
099da3e
Addressed pull request comments
tylers-epilog Aug 23, 2022
ba180f7
Cargo clippy and fmt fixes
tylers-epilog Aug 23, 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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
# of contributors, see the revision history in source control.
Raph Levien
Nicolas Silva
Epilog Laser (Tyler Scott)
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ features = ["mint", "schemars", "serde"]

[dependencies]
arrayvec = "0.7.1"
bitflags = "1.3"

[dependencies.mint]
version = "0.5.1"
Expand Down
21 changes: 16 additions & 5 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,8 @@ pub fn solve_quadratic(c0: f64, c1: f64, c2: f64) -> ArrayVec<f64, 2> {
let sc1 = c1 * c2.recip();
if !sc0.is_finite() || !sc1.is_finite() {
// c2 is zero or very small, treat as linear eqn
let root = -c0 / c1;
if root.is_finite() {
for root in solve_linear(c0, c1) {
result.push(root);
} else if c0 == 0.0 && c1 == 0.0 {
// Degenerate case
result.push(0.0);
}
return result;
}
Expand Down Expand Up @@ -149,6 +145,21 @@ pub fn solve_quadratic(c0: f64, c1: f64, c2: f64) -> ArrayVec<f64, 2> {
result
}

/// Find real roots of linear equation.
///
/// Return values of x for which c0 + c1 x = 0.
pub fn solve_linear(c0: f64, c1: f64) -> ArrayVec<[f64; 1]> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type is slightly odd. Is there a particular reason you didn't use Option<f64>? I'm sympathetic to the desire to be more parallel to the other cases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have it like this so that it's compatible with the return types in solve_t_for_x and solve_t_for_y. It also makes it so that it follows the same format as solve_quadratic and solve_cubic. I don't have any issues with changing it to use Option<f64>, but I'd like your thoughts on my reasoning first.

let mut result = ArrayVec::new();
let root = -c0 / c1;
if root.is_finite() {
result.push(root);
} else if c0 == 0.0 && c1 == 0.0 {
// Degenerate case
result.push(0.0);
}
result
}

/// Solve an arbitrary function for a zero-crossing.
///
/// This uses the [ITP method], as described in the paper
Expand Down
43 changes: 39 additions & 4 deletions src/cubicbez.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use crate::MAX_EXTREMA;
use crate::{Line, QuadSpline, Vec2};
use arrayvec::ArrayVec;

use crate::common::solve_quadratic;
use crate::common::GAUSS_LEGENDRE_COEFFS_9;
use crate::common::{solve_quadratic};
use crate::{
Affine, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveCurvature,
ParamCurveDeriv, ParamCurveExtrema, ParamCurveNearest, PathEl, Point, QuadBez, Rect, Shape,
Affine, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveBezierClipping,
ParamCurveCurvature, ParamCurveDeriv, ParamCurveExtrema, ParamCurveNearest, PathEl, Point,
QuadBez, Rect, Shape,
};

const MAX_SPLINE_SPLIT: usize = 100;
Expand Down Expand Up @@ -238,7 +239,9 @@ impl CubicBez {
})
}

fn parameters(&self) -> (Vec2, Vec2, Vec2, Vec2) {
/// Get the parameters such that the curve can be represented by the following formula:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It bothers me slightly that this is in decreasing exponent, where, for example, solve_cubic is in increasing order. Not a dealbreaker though, if it's documented. (and I realize this is existing code, just thinking about it more carefully when we're making it public)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also bothers me that these functions are inconsistent. Since they're both public though, wouldn't it be an issue to change them? Perhaps it's better to just document the exponent order?

/// B(t) = a*t^3 + b*t^2 + c*t + d
pub fn parameters(&self) -> (Vec2, Vec2, Vec2, Vec2) {
let c = (self.p1 - self.p0) * 3.0;
let b = (self.p2 - self.p1) * 3.0 - c;
let d = self.p0.to_vec2();
Expand Down Expand Up @@ -309,6 +312,13 @@ impl CubicBez {
pub fn is_nan(&self) -> bool {
self.p0.is_nan() || self.p1.is_nan() || self.p2.is_nan() || self.p3.is_nan()
}

/// Is this cubic Bezier curve a line?
#[inline]
pub fn is_linear(&self, accuracy: f64) -> bool {
self.baseline().nearest(self.p1, accuracy).distance_sq <= accuracy
&& self.baseline().nearest(self.p2, accuracy).distance_sq <= accuracy
}
}

/// An iterator for cubic beziers.
Expand Down Expand Up @@ -598,6 +608,7 @@ mod tests {
cubics_to_quadratic_splines, Affine, CubicBez, Nearest, ParamCurve, ParamCurveArclen,
ParamCurveArea, ParamCurveDeriv, ParamCurveExtrema, ParamCurveNearest, Point, QuadBez,
};
use arrayvec::{Array, ArrayVec};

#[test]
fn cubicbez_deriv() {
Expand Down Expand Up @@ -877,4 +888,28 @@ mod tests {
converted[0].points()[2].distance(Point::new(88639.0 / 90.0, 52584.0 / 90.0)) < 0.0001
);
}

use crate::param_curve::ParamCurveBezierClipping;
#[test]
fn solve_t_for_xy() {
fn verify<T: Array<Item = f64>>(mut roots: ArrayVec<T>, expected: &[f64]) {
assert_eq!(expected.len(), roots.len());
let epsilon = 1e-6;
roots.sort_by(|a, b| a.partial_cmp(b).unwrap());

for i in 0..expected.len() {
assert!((roots[i] - expected[i]).abs() < epsilon);
}
}

let curve = CubicBez::new((0.0, 0.0), (0.0, 8.0), (10.0, 8.0), (10.0, 0.0));
verify(curve.solve_t_for_x(5.0), &[0.5]);
verify(curve.solve_t_for_y(6.0), &[0.5]);

{
let curve = CubicBez::new((0.0, 10.0), (0.0, 10.0), (10.0, 10.0), (10.0, 10.0));

verify(curve.solve_t_for_y(10.0), &[]);
}
}
}
Loading