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 all 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
2 changes: 2 additions & 0 deletions src/bezpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,7 @@ mod tests {

#[test]
#[should_panic(expected = "no open subpath")]
#[cfg(debug_assertions)] // Only provides proper panic message in debug mode
fn test_elements_to_segments_starts_on_closepath() {
let mut path = BezPath::new();
path.close_path();
Expand All @@ -1307,6 +1308,7 @@ mod tests {

#[test]
#[should_panic(expected = "no open subpath")]
#[cfg(debug_assertions)] // Only provides proper panic message in debug mode
fn test_must_not_start_on_quad() {
let mut path = BezPath::new();
path.quad_to((5.0, 5.0), (10.0, 10.0));
Expand Down
27 changes: 22 additions & 5 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ impl FloatExt<f32> for f32 {
/// See: <http://mathworld.wolfram.com/CubicFormula.html>
///
/// Return values of x for which c0 + c1 x + c2 x² + c3 x³ = 0.
///
/// Note: The input arguments are in increasing exponent order
pub fn solve_cubic(c0: f64, c1: f64, c2: f64, c3: f64) -> ArrayVec<f64, 3> {
let mut result = ArrayVec::new();
let c3_recip = c3.recip();
Expand Down Expand Up @@ -103,18 +105,16 @@ pub fn solve_cubic(c0: f64, c1: f64, c2: f64, c3: f64) -> ArrayVec<f64, 3> {
/// the other root might be out of representable range. In the degenerate
/// case where all coefficients are zero, so that all values of x satisfy
/// the equation, a single `0.0` is returned.
///
/// Note: The input arguments are in increasing exponent order
pub fn solve_quadratic(c0: f64, c1: f64, c2: f64) -> ArrayVec<f64, 2> {
let mut result = ArrayVec::new();
let sc0 = c0 * c2.recip();
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 +149,23 @@ 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.
///
/// Note: The input arguments are in increasing exponent order
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.

I'm wondering if Option might be a cleaner type. It's essentially the same type, and far more idiomatic Rust, but on the other hand, maybe it's better to keep it as it is, for consistency with the other methods.

It would also be good to document the degenerate case, as is done for quadratic.

Copy link
Author

Choose a reason for hiding this comment

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

Personally, I like the idea of keeping it consistent. You're absolutely right that what I have is the equivalent of option though. I think it comes down to personal preference, but since you maintain this repo, I'll let you have the final say.

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, this happens the line is horizontal, hence when c1 == 0.
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
18 changes: 14 additions & 4 deletions src/cubicbez.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//! Cubic Bézier segments.

use arrayvec::ArrayVec;
use std::ops::{Mul, Range};

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, GAUSS_LEGENDRE_COEFFS_9};
use crate::{
Affine, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveCurvature,
ParamCurveDeriv, ParamCurveExtrema, ParamCurveNearest, PathEl, Point, QuadBez, Rect, Shape,
Expand Down Expand Up @@ -238,7 +237,11 @@ 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
///
/// Note: Values returned are in decresing exponent order
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 @@ -310,6 +313,13 @@ impl CubicBez {
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
}

/// Determine the inflection points.
///
/// Return value is t parameter for the inflection points of the curve segment.
Expand Down
Loading