mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Upgrade to the Rust 2024 edition (#2367)
* Update to rust 2024 edition * Fixes * Clean up imports * Cargo fmt again --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "bezier-rs"
|
||||
version = "0.4.0"
|
||||
rust-version = "1.79"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "Computational geometry algorithms for Bézier segments and shapes useful in the context of 2D graphics"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::*;
|
||||
use utils::format_point;
|
||||
|
||||
use std::fmt::Write;
|
||||
use utils::format_point;
|
||||
|
||||
/// Functionality relating to core `Bezier` operations, such as constructors and `abs_diff_eq`.
|
||||
impl Bezier {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::utils::{TValue, TValueType};
|
||||
|
||||
use super::*;
|
||||
use crate::utils::{TValue, TValueType};
|
||||
|
||||
/// Functionality relating to looking up properties of the `Bezier` or points along the `Bezier`.
|
||||
impl Bezier {
|
||||
|
||||
@@ -69,7 +69,7 @@ impl Bezier {
|
||||
/// - For a linear segment, the order of the points will be: `start`, `end`.
|
||||
/// - For a quadratic segment, the order of the points will be: `start`, `handle`, `end`.
|
||||
/// - For a cubic segment, the order of the points will be: `start`, `handle_start`, `handle_end`, `end`.
|
||||
pub fn get_points(&self) -> impl Iterator<Item = DVec2> {
|
||||
pub fn get_points(&self) -> impl Iterator<Item = DVec2> + use<> {
|
||||
match self.handles {
|
||||
BezierHandles::Linear => [self.start, self.end, DVec2::ZERO, DVec2::ZERO].into_iter().take(2),
|
||||
BezierHandles::Quadratic { handle } => [self.start, handle, self.end, DVec2::ZERO].into_iter().take(3),
|
||||
|
||||
@@ -7,11 +7,9 @@ mod transform;
|
||||
|
||||
use crate::consts::*;
|
||||
use crate::utils;
|
||||
|
||||
pub use structs::*;
|
||||
|
||||
use glam::DVec2;
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
pub use structs::*;
|
||||
|
||||
/// Representation of the handle point(s) in a bezier segment.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use super::*;
|
||||
use crate::polynomial::Polynomial;
|
||||
use crate::utils::{solve_cubic, solve_quadratic, TValue};
|
||||
use crate::{to_symmetrical_basis_pair, SymmetricalBasis};
|
||||
|
||||
use crate::utils::{TValue, solve_cubic, solve_quadratic};
|
||||
use crate::{SymmetricalBasis, to_symmetrical_basis_pair};
|
||||
use glam::DMat2;
|
||||
use std::ops::Range;
|
||||
|
||||
@@ -99,11 +98,7 @@ impl Bezier {
|
||||
pub fn tangent(&self, t: TValue) -> DVec2 {
|
||||
let t = self.t_value_to_parametric(t);
|
||||
let tangent = self.non_normalized_tangent(t);
|
||||
if tangent.length() > 0. {
|
||||
tangent.normalize()
|
||||
} else {
|
||||
tangent
|
||||
}
|
||||
if tangent.length() > 0. { tangent.normalize() } else { tangent }
|
||||
}
|
||||
|
||||
/// Find the `t`-value(s) such that the tangent(s) at `t` pass through the specified point.
|
||||
@@ -147,11 +142,7 @@ impl Bezier {
|
||||
|
||||
let numerator = d.x * dd.y - d.y * dd.x;
|
||||
let denominator = (d.x.powf(2.) + d.y.powf(2.)).powf(1.5);
|
||||
if denominator.abs() < MAX_ABSOLUTE_DIFFERENCE {
|
||||
0.
|
||||
} else {
|
||||
numerator / denominator
|
||||
}
|
||||
if denominator.abs() < MAX_ABSOLUTE_DIFFERENCE { 0. } else { numerator / denominator }
|
||||
}
|
||||
|
||||
/// Returns two lists of `t`-values representing the local extrema of the `x` and `y` parametric curves respectively.
|
||||
@@ -228,7 +219,7 @@ impl Bezier {
|
||||
}
|
||||
|
||||
/// Returns an `Iterator` containing all possible parametric `t`-values at the given `x`-coordinate.
|
||||
pub fn find_tvalues_for_x(&self, x: f64) -> impl Iterator<Item = f64> {
|
||||
pub fn find_tvalues_for_x(&self, x: f64) -> impl Iterator<Item = f64> + use<> {
|
||||
// Compute the roots of the resulting bezier curve
|
||||
match self.handles {
|
||||
BezierHandles::Linear => {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
use crate::compare::compare_points;
|
||||
use crate::utils::{f64_compare, Cap, TValue};
|
||||
use crate::utils::{Cap, TValue, f64_compare};
|
||||
use crate::{AppendType, ManipulatorGroup, Subpath};
|
||||
|
||||
use glam::DMat2;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -476,11 +474,7 @@ impl Bezier {
|
||||
error,
|
||||
max_iterations,
|
||||
});
|
||||
if final_low_t != 1. {
|
||||
[auto_arcs, arc_approximations].concat()
|
||||
} else {
|
||||
auto_arcs
|
||||
}
|
||||
if final_low_t != 1. { [auto_arcs, arc_approximations].concat() } else { auto_arcs }
|
||||
}
|
||||
ArcStrategy::FavorLargerArcs => self.approximate_curve_with_arcs(0., 1., error, max_iterations, false).0,
|
||||
ArcStrategy::FavorCorrectness => self
|
||||
@@ -620,9 +614,9 @@ impl Bezier {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::EmptyId;
|
||||
use crate::compare::{compare_arcs, compare_points};
|
||||
use crate::utils::{Cap, TValue};
|
||||
use crate::EmptyId;
|
||||
|
||||
#[test]
|
||||
fn test_split() {
|
||||
@@ -777,14 +771,18 @@ mod tests {
|
||||
|
||||
// Check that the reduce helper is correct
|
||||
let (helper_curves, helper_t_values) = bezier.reduced_curves_and_t_values(None);
|
||||
assert!(reduced_curves
|
||||
.iter()
|
||||
.zip(helper_curves.iter())
|
||||
.all(|(bezier1, bezier2)| bezier1.abs_diff_eq(bezier2, MAX_ABSOLUTE_DIFFERENCE)));
|
||||
assert!(reduced_curves
|
||||
.iter()
|
||||
.zip(helper_t_values.iter())
|
||||
.all(|(curve, t_pair)| curve.abs_diff_eq(&bezier.trim(TValue::Parametric(t_pair[0]), TValue::Parametric(t_pair[1])), MAX_ABSOLUTE_DIFFERENCE)))
|
||||
assert!(
|
||||
reduced_curves
|
||||
.iter()
|
||||
.zip(helper_curves.iter())
|
||||
.all(|(bezier1, bezier2)| bezier1.abs_diff_eq(bezier2, MAX_ABSOLUTE_DIFFERENCE))
|
||||
);
|
||||
assert!(
|
||||
reduced_curves
|
||||
.iter()
|
||||
.zip(helper_t_values.iter())
|
||||
.all(|(curve, t_pair)| curve.abs_diff_eq(&bezier.trim(TValue::Parametric(t_pair[0]), TValue::Parametric(t_pair[1])), MAX_ABSOLUTE_DIFFERENCE))
|
||||
)
|
||||
}
|
||||
|
||||
fn assert_valid_offset<PointId: crate::Identifier>(bezier: &Bezier, offset: &Subpath<PointId>, expected_distance: f64) {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/// Comparison functions used for tests in the bezier module
|
||||
#[cfg(test)]
|
||||
use super::{CircleArc, Subpath};
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
#[cfg(test)]
|
||||
use crate::utils::f64_compare;
|
||||
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
// Compare two f64s with some maximum absolute difference to account for floating point errors
|
||||
|
||||
@@ -102,7 +102,7 @@ impl<const N: usize> Default for Polynomial<N> {
|
||||
impl<const N: usize> Display for Polynomial<N> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let mut first = true;
|
||||
for (index, coefficient) in self.coefficients.iter().enumerate().rev().filter(|(_, &coefficient)| coefficient != 0.) {
|
||||
for (index, coefficient) in self.coefficients.iter().enumerate().rev().filter(|&(_, &coefficient)| coefficient != 0.) {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::*;
|
||||
use crate::consts::*;
|
||||
use crate::utils::format_point;
|
||||
|
||||
use glam::DVec2;
|
||||
use std::fmt::Write;
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::utils::SubpathTValue;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
fn set_up_open_subpath() -> Subpath<EmptyId> {
|
||||
|
||||
@@ -4,13 +4,12 @@ mod manipulators;
|
||||
mod solvers;
|
||||
mod structs;
|
||||
mod transform;
|
||||
pub use core::*;
|
||||
pub use structs::*;
|
||||
|
||||
use crate::Bezier;
|
||||
|
||||
pub use core::*;
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
use std::ops::{Index, IndexMut};
|
||||
pub use structs::*;
|
||||
|
||||
/// Structure used to represent a path composed of [Bezier] curves.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use super::*;
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::utils::{compute_circular_subpath_details, is_rectangle_inside_other, line_intersection, SubpathTValue};
|
||||
use crate::TValue;
|
||||
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::utils::{SubpathTValue, compute_circular_subpath_details, is_rectangle_inside_other, line_intersection};
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -539,10 +538,9 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Bezier;
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::utils;
|
||||
use crate::Bezier;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
fn normalize_t(n: i64, t: f64) -> f64 {
|
||||
@@ -629,44 +627,54 @@ mod tests {
|
||||
let mut n = (subpath.len() as i64) - 1;
|
||||
|
||||
let t0 = 0.;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t0)),
|
||||
linear_bezier.evaluate(TValue::Parametric(normalize_t(n, t0))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t0)),
|
||||
linear_bezier.evaluate(TValue::Parametric(normalize_t(n, t0))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
let t1 = 0.25;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t1)),
|
||||
linear_bezier.evaluate(TValue::Parametric(normalize_t(n, t1))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t1)),
|
||||
linear_bezier.evaluate(TValue::Parametric(normalize_t(n, t1))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
let t2 = 0.50;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t2)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(normalize_t(n, t2))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t2)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(normalize_t(n, t2))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
let t3 = 0.75;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t3)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(normalize_t(n, t3))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t3)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(normalize_t(n, t3))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
let t4 = 1.;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t4)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(1.)),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t4)),
|
||||
quadratic_bezier.evaluate(TValue::Parametric(1.)),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
// Test closed subpath
|
||||
|
||||
@@ -674,20 +682,24 @@ mod tests {
|
||||
n = subpath.len() as i64;
|
||||
|
||||
let t5 = 2. / 3.;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t5)),
|
||||
cubic_bezier.evaluate(TValue::Parametric(normalize_t(n, t5))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t5)),
|
||||
cubic_bezier.evaluate(TValue::Parametric(normalize_t(n, t5))),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
let t6 = 1.;
|
||||
assert!(utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t6)),
|
||||
cubic_bezier.evaluate(TValue::Parametric(1.)),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
subpath.evaluate(SubpathTValue::GlobalParametric(t6)),
|
||||
cubic_bezier.evaluate(TValue::Parametric(1.)),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -737,35 +749,41 @@ mod tests {
|
||||
let quadratic_1_intersections = quadratic_bezier_1.intersections(&line, None, None);
|
||||
let subpath_intersections = subpath.intersections(&line, None, None);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[1])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[2].0,
|
||||
t: subpath_intersections[2].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[1])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[2].0,
|
||||
t: subpath_intersections[2].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -816,25 +834,29 @@ mod tests {
|
||||
let quadratic_1_intersections = quadratic_bezier_1.intersections(&line, None, None);
|
||||
let subpath_intersections = subpath.intersections(&line, None, None);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -884,35 +906,41 @@ mod tests {
|
||||
let quadratic_1_intersections = quadratic_bezier_1.intersections(&line, None, None);
|
||||
let subpath_intersections = subpath.intersections(&line, None, None);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
cubic_bezier.evaluate(TValue::Parametric(cubic_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[0].0,
|
||||
t: subpath_intersections[0].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[0])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[1].0,
|
||||
t: subpath_intersections[1].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
|
||||
assert!(utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[1])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[2].0,
|
||||
t: subpath_intersections[2].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all());
|
||||
assert!(
|
||||
utils::dvec2_compare(
|
||||
quadratic_bezier_1.evaluate(TValue::Parametric(quadratic_1_intersections[1])),
|
||||
subpath.evaluate(SubpathTValue::Parametric {
|
||||
segment_index: subpath_intersections[2].0,
|
||||
t: subpath_intersections[2].1
|
||||
}),
|
||||
MAX_ABSOLUTE_DIFFERENCE
|
||||
)
|
||||
.all()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: add more intersection tests
|
||||
@@ -924,19 +952,19 @@ mod tests {
|
||||
|
||||
let curve = Bezier::from_quadratic_dvec2(DVec2::new(189., 289.), DVec2::new(9., 286.), DVec2::new(45., 410.));
|
||||
let curve_intersecting = Subpath::<EmptyId>::from_bezier(&curve);
|
||||
assert_eq!(curve_intersecting.is_inside_subpath(&boundary_polygon, None, None), false);
|
||||
assert!(!curve_intersecting.is_inside_subpath(&boundary_polygon, None, None));
|
||||
|
||||
let curve = Bezier::from_quadratic_dvec2(DVec2::new(115., 37.), DVec2::new(51.4, 91.8), DVec2::new(76.5, 242.));
|
||||
let curve_outside = Subpath::<EmptyId>::from_bezier(&curve);
|
||||
assert_eq!(curve_outside.is_inside_subpath(&boundary_polygon, None, None), false);
|
||||
assert!(!curve_outside.is_inside_subpath(&boundary_polygon, None, None));
|
||||
|
||||
let curve = Bezier::from_cubic_dvec2(DVec2::new(210.1, 133.5), DVec2::new(150.2, 436.9), DVec2::new(436., 285.), DVec2::new(247.6, 240.7));
|
||||
let curve_inside = Subpath::<EmptyId>::from_bezier(&curve);
|
||||
assert_eq!(curve_inside.is_inside_subpath(&boundary_polygon, None, None), true);
|
||||
assert!(curve_inside.is_inside_subpath(&boundary_polygon, None, None));
|
||||
|
||||
let line = Bezier::from_linear_dvec2(DVec2::new(101., 101.5), DVec2::new(150.2, 499.));
|
||||
let line_inside = Subpath::<EmptyId>::from_bezier(&line);
|
||||
assert_eq!(line_inside.is_inside_subpath(&boundary_polygon, None, None), true);
|
||||
assert!(line_inside.is_inside_subpath(&boundary_polygon, None, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use super::Bezier;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::{
|
||||
fmt::{Debug, Formatter, Result},
|
||||
hash::Hash,
|
||||
};
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// An id type used for each [ManipulatorGroup].
|
||||
pub trait Identifier: Sized + Clone + PartialEq + Hash + 'static {
|
||||
@@ -113,7 +110,7 @@ impl<PointId: crate::Identifier> ManipulatorGroup<PointId> {
|
||||
|
||||
/// Are all handles at finite positions
|
||||
pub fn is_finite(&self) -> bool {
|
||||
self.anchor.is_finite() && self.in_handle.map_or(true, |handle| handle.is_finite()) && self.out_handle.map_or(true, |handle| handle.is_finite())
|
||||
self.anchor.is_finite() && self.in_handle.is_none_or(|handle| handle.is_finite()) && self.out_handle.is_none_or(|handle| handle.is_finite())
|
||||
}
|
||||
|
||||
/// Reverse directions of handles
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
use std::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::utils::{Cap, Join, SubpathTValue, TValue};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::vec;
|
||||
|
||||
/// Helper function to ensure the index and t value pair is mapped within a maximum index value.
|
||||
/// Allows for the point to be fetched without needing to handle an additional edge case.
|
||||
/// - Ex. Via `subpath.iter().nth(index).evaluate(t);`
|
||||
fn map_index_within_range(index: usize, t: f64, max_size: usize) -> (usize, f64) {
|
||||
if max_size > 0 && index == max_size && t == 0. {
|
||||
(index - 1, 1.)
|
||||
} else {
|
||||
(index, t)
|
||||
}
|
||||
if max_size > 0 && index == max_size && t == 0. { (index - 1, 1.) } else { (index, t) }
|
||||
}
|
||||
|
||||
/// Functionality that transforms Subpaths, such as split, reduce, offset, etc.
|
||||
@@ -549,10 +543,10 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Cap, Join, ManipulatorGroup, Subpath};
|
||||
use crate::EmptyId;
|
||||
use crate::compare::{compare_points, compare_subpaths, compare_vec_of_points};
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::utils::{SubpathTValue, TValue};
|
||||
use crate::EmptyId;
|
||||
use glam::DVec2;
|
||||
|
||||
fn set_up_open_subpath() -> Subpath<EmptyId> {
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
*/
|
||||
|
||||
use crate::{Bezier, BezierHandles};
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
impl std::ops::Index<usize> for Bezier {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::consts::{MAX_ABSOLUTE_DIFFERENCE, STRICT_MAX_ABSOLUTE_DIFFERENCE};
|
||||
use crate::{ManipulatorGroup, Subpath};
|
||||
|
||||
use glam::{BVec2, DMat2, DVec2};
|
||||
use std::fmt::Write;
|
||||
|
||||
@@ -94,11 +93,7 @@ pub fn compute_abc_for_cubic_through_points(start_point: DVec2, point_on_curve:
|
||||
/// Find the roots of the linear equation `ax + b`.
|
||||
pub fn solve_linear(a: f64, b: f64) -> [Option<f64>; 3] {
|
||||
// There exist roots when `a` is not 0
|
||||
if a.abs() > MAX_ABSOLUTE_DIFFERENCE {
|
||||
[Some(-b / a), None, None]
|
||||
} else {
|
||||
[None; 3]
|
||||
}
|
||||
if a.abs() > MAX_ABSOLUTE_DIFFERENCE { [Some(-b / a), None, None] } else { [None; 3] }
|
||||
}
|
||||
|
||||
/// Find the roots of the linear equation `ax^2 + bx + c`.
|
||||
@@ -310,7 +305,8 @@ pub fn format_point(svg: &mut String, prefix: &str, x: f64, y: f64) -> std::fmt:
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{consts::MAX_ABSOLUTE_DIFFERENCE, Bezier, EmptyId};
|
||||
use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::{Bezier, EmptyId};
|
||||
|
||||
/// Compare vectors of `f64`s with a provided max absolute value difference.
|
||||
fn f64_compare_vector(a: Vec<f64>, b: Vec<f64>, max_abs_diff: f64) -> bool {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "dyn-any"
|
||||
version = "0.3.1"
|
||||
rust-version = "1.79"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "An Any trait that works for arbitrary lifetimes"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "dyn-any-derive"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
|
||||
description = "#[derive(DynAny)]"
|
||||
|
||||
@@ -5,7 +5,7 @@ extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput, GenericParam, Lifetime, LifetimeParam, TypeParamBound};
|
||||
use syn::{DeriveInput, GenericParam, Lifetime, LifetimeParam, TypeParamBound, parse_macro_input};
|
||||
|
||||
/// Derives an implementation for the [`DynAny`] trait.
|
||||
///
|
||||
@@ -62,7 +62,7 @@ fn replace_lifetimes(generics: &syn::Generics, replacement: &str) -> Vec<proc_ma
|
||||
GenericParam::Type(t) => {
|
||||
let mut t = t.clone();
|
||||
t.bounds.iter_mut().for_each(|bond| {
|
||||
if let TypeParamBound::Lifetime(ref mut t) = bond {
|
||||
if let TypeParamBound::Lifetime(t) = bond {
|
||||
*t = Lifetime::new(replacement, Span::call_site())
|
||||
}
|
||||
});
|
||||
|
||||
@@ -187,7 +187,6 @@ macro_rules! impl_slice {
|
||||
|
||||
mod slice {
|
||||
use super::*;
|
||||
|
||||
use core::slice::*;
|
||||
|
||||
impl_slice!(Iter, IterMut, Chunks, ChunksMut, RChunks, RChunksMut, Windows);
|
||||
@@ -249,24 +248,24 @@ impl From<()> for Box<dyn DynAny<'static>> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque},
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use alloc::borrow::Cow;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
use core::iter::Empty;
|
||||
use core::marker::{PhantomData, PhantomPinned};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::num::Wrapping;
|
||||
use core::ops::Range;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::*;
|
||||
use core::{
|
||||
cell::{Cell, RefCell, UnsafeCell},
|
||||
iter::Empty,
|
||||
marker::{PhantomData, PhantomPinned},
|
||||
mem::{ManuallyDrop, MaybeUninit},
|
||||
num::Wrapping,
|
||||
ops::Range,
|
||||
pin::Pin,
|
||||
time::Duration,
|
||||
};
|
||||
use core::time::Duration;
|
||||
|
||||
impl_type!(
|
||||
Option<T>, Result<T, E>, Cell<T>, UnsafeCell<T>, RefCell<T>, MaybeUninit<T>,
|
||||
@@ -286,10 +285,9 @@ impl_type!(
|
||||
);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::*,
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl_type!(Once, Mutex<T>, RwLock<T>, HashSet<T>, HashMap<K, V>);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "math-parser"
|
||||
version = "0.0.0"
|
||||
rust-version = "1.79"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "Parser for Graphite style mathematics expressions"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||
use math_parser::ast;
|
||||
use math_parser::context::EvalContext;
|
||||
|
||||
macro_rules! generate_benchmarks {
|
||||
($( $input:expr ),* $(,)?) => {
|
||||
($( $input:expr_2021 ),* $(,)?) => {
|
||||
fn parsing_bench(c: &mut Criterion) {
|
||||
$(
|
||||
c.bench_function(concat!("parse ", $input), |b| {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{collections::HashMap, f64::consts::PI};
|
||||
|
||||
use crate::value::{Number, Value};
|
||||
use lazy_static::lazy_static;
|
||||
use num_complex::{Complex, ComplexFloat};
|
||||
use std::collections::HashMap;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use crate::value::{Number, Value};
|
||||
type FunctionImplementation = Box<dyn Fn(&[Value]) -> Option<Value> + Send + Sync>;
|
||||
lazy_static! {
|
||||
pub static ref DEFAULT_FUNCTIONS: HashMap<&'static str, FunctionImplementation> = {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::value::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
//TODO: editor integration, implement these traits for whatever is needed, maybe merge them if needed
|
||||
pub trait ValueProvider {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use crate::ast::{Literal, Node};
|
||||
use crate::constants::DEFAULT_FUNCTIONS;
|
||||
use crate::context::{EvalContext, FunctionProvider, ValueProvider};
|
||||
use crate::value::{Number, Value};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
ast::{Literal, Node},
|
||||
constants::DEFAULT_FUNCTIONS,
|
||||
context::{EvalContext, FunctionProvider, ValueProvider},
|
||||
value::{Number, Value},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EvalError {
|
||||
#[error("Missing value: {0}")]
|
||||
@@ -49,14 +46,12 @@ impl Node {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
ast::{BinaryOp, Literal, Node, UnaryOp},
|
||||
context::{EvalContext, ValueMap},
|
||||
value::Value,
|
||||
};
|
||||
use crate::ast::{BinaryOp, Literal, Node, UnaryOp};
|
||||
use crate::context::{EvalContext, ValueMap};
|
||||
use crate::value::Value;
|
||||
|
||||
macro_rules! eval_tests {
|
||||
($($name:ident: $expected:expr => $expr:expr),* $(,)?) => {
|
||||
($($name:ident: $expected:expr_2021 => $expr:expr_2021),* $(,)?) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
||||
@@ -21,16 +21,14 @@ pub fn evaluate(expression: &str) -> Result<(Result<Value, EvalError>, Unit), Pa
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use value::Number;
|
||||
|
||||
use ast::Unit;
|
||||
|
||||
use super::*;
|
||||
use ast::Unit;
|
||||
use value::Number;
|
||||
|
||||
const EPSILON: f64 = 1e-10_f64;
|
||||
|
||||
macro_rules! test_end_to_end{
|
||||
($($name:ident: $input:expr => ($expected_value:expr, $expected_unit:expr)),* $(,)?) => {
|
||||
($($name:ident: $input:expr_2021 => ($expected_value:expr_2021, $expected_unit:expr_2021)),* $(,)?) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
use std::num::{ParseFloatError, ParseIntError};
|
||||
|
||||
use crate::ast::{BinaryOp, Literal, Node, UnaryOp, Unit};
|
||||
use crate::context::EvalContext;
|
||||
use crate::value::{Complex, Number, Value};
|
||||
use lazy_static::lazy_static;
|
||||
use num_complex::ComplexFloat;
|
||||
use pest::{
|
||||
iterators::{Pair, Pairs},
|
||||
pratt_parser::{Assoc, Op, PrattParser},
|
||||
Parser,
|
||||
};
|
||||
use pest::Parser;
|
||||
use pest::iterators::{Pair, Pairs};
|
||||
use pest::pratt_parser::{Assoc, Op, PrattParser};
|
||||
use pest_derive::Parser;
|
||||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
ast::{BinaryOp, Literal, Node, UnaryOp, Unit},
|
||||
context::EvalContext,
|
||||
value::{Complex, Number, Value},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "./grammer.pest"] // Point to the grammar file
|
||||
struct ExprParser;
|
||||
@@ -321,7 +315,7 @@ fn parse_expr(pairs: Pairs<Rule>) -> Result<(Node, NodeMetadata), ParseError> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
macro_rules! test_parser {
|
||||
($($name:ident: $input:expr => $expected:expr),* $(,)?) => {
|
||||
($($name:ident: $input:expr_2021 => $expected:expr_2021),* $(,)?) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use num_complex::ComplexFloat;
|
||||
|
||||
use crate::ast::{BinaryOp, UnaryOp};
|
||||
use num_complex::ComplexFloat;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
pub type Complex = num_complex::Complex<f64>;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "path-bool"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.81"
|
||||
rust-version = "1.85"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>", "Adam Platkevič"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
keywords = [
|
||||
"bezier",
|
||||
"curve",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||
use path_bool::*;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||
use glam::DVec2;
|
||||
use path_bool::*;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) use util::*;
|
||||
pub use intersection_path_segment::path_segment_intersection;
|
||||
#[cfg(feature = "parsing")]
|
||||
pub use parsing::path_data::{path_from_path_data, path_to_path_data};
|
||||
pub use path_boolean::{path_boolean, BooleanError, FillRule, PathBooleanOperation, EPS};
|
||||
pub use path_boolean::{BooleanError, EPS, FillRule, PathBooleanOperation, path_boolean};
|
||||
pub use path_segment::PathSegment;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::path::{path_from_commands, path_to_commands, Path};
|
||||
use crate::path_command::{AbsolutePathCommand, PathCommand, RelativePathCommand};
|
||||
use crate::BooleanError;
|
||||
use crate::path::{Path, path_from_commands, path_to_commands};
|
||||
use crate::path_command::{AbsolutePathCommand, PathCommand, RelativePathCommand};
|
||||
use glam::DVec2;
|
||||
use regex::Regex;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ pub(crate) mod path_segment;
|
||||
use glam::DVec2;
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
use crate::path_command::{to_absolute_commands, AbsolutePathCommand, PathCommand};
|
||||
use crate::path_command::{AbsolutePathCommand, PathCommand, to_absolute_commands};
|
||||
use crate::path_segment::PathSegment;
|
||||
|
||||
pub type Path = Vec<PathSegment>;
|
||||
@@ -107,7 +107,7 @@ where
|
||||
let start = seg.start();
|
||||
let mut commands = Vec::new();
|
||||
|
||||
if last_point.map_or(true, |lp| !start.abs_diff_eq(lp, eps)) {
|
||||
if last_point.is_none_or(|lp| !start.abs_diff_eq(lp, eps)) {
|
||||
if last_point.is_some() {
|
||||
commands.push(PathCommand::Absolute(AbsolutePathCommand::Z));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::aabb::{bounding_box_max_extent, bounding_boxes_overlap, Aabb};
|
||||
use crate::aabb::{Aabb, bounding_box_max_extent, bounding_boxes_overlap};
|
||||
use crate::epsilons::Epsilons;
|
||||
use crate::line_segment::{line_segment_intersection, line_segments_intersect};
|
||||
use crate::line_segment_aabb::line_segment_aabb_intersect;
|
||||
use crate::math::lerp;
|
||||
use crate::path_segment::PathSegment;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -21,11 +21,7 @@ pub fn line_segment_intersection([p1, p2]: LineSegment, [p3, p4]: LineSegment, e
|
||||
let s = (c.x * b.y - c.y * b.x) / denom;
|
||||
let t = (a.x * c.y - a.y * c.x) / denom;
|
||||
|
||||
if (-eps..=1. + eps).contains(&s) && (-eps..=1. + eps).contains(&t) {
|
||||
Some((s, t))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if (-eps..=1. + eps).contains(&s) && (-eps..=1. + eps).contains(&t) { Some((s, t)) } else { None }
|
||||
}
|
||||
|
||||
pub fn line_segments_intersect(seg1: LineSegment, seg2: LineSegment, eps: f64) -> bool {
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
//! The implementations in this module closely follow the SVG path specification,
|
||||
//! making it suitable for use in vector graphics applications.
|
||||
|
||||
use crate::EPS;
|
||||
use crate::aabb::{Aabb, bounding_box_around_point, expand_bounding_box, extend_bounding_box, merge_bounding_boxes};
|
||||
use crate::math::{lerp, vector_angle};
|
||||
use glam::{DMat2, DMat3, DVec2};
|
||||
use std::f64::consts::{PI, TAU};
|
||||
|
||||
use crate::aabb::{bounding_box_around_point, expand_bounding_box, extend_bounding_box, merge_bounding_boxes, Aabb};
|
||||
use crate::math::{lerp, vector_angle};
|
||||
use crate::EPS;
|
||||
|
||||
/// Represents a segment of a path in a 2D space, based on the SVG path specification.
|
||||
///
|
||||
/// This enum closely follows the path segment types defined in the SVG 2 specification.
|
||||
@@ -155,11 +154,7 @@ impl PathSegment {
|
||||
let b = 6. * b;
|
||||
let numerator = a.x * b.y - a.y * b.x;
|
||||
let denominator = a.length_squared() * a.length();
|
||||
if denominator == 0. {
|
||||
0.
|
||||
} else {
|
||||
numerator / denominator
|
||||
}
|
||||
if denominator == 0. { 0. } else { numerator / denominator }
|
||||
}
|
||||
PathSegment::Quadratic(start, control, end) => {
|
||||
// First derivative
|
||||
@@ -168,11 +163,7 @@ impl PathSegment {
|
||||
let b = 2. * (start - 2. * control + end);
|
||||
let numerator = a.x * b.y - a.y * b.x;
|
||||
let denominator = a.length_squared() * a.length();
|
||||
if denominator == 0. {
|
||||
0.
|
||||
} else {
|
||||
numerator / denominator
|
||||
}
|
||||
if denominator == 0. { 0. } else { numerator / denominator }
|
||||
}
|
||||
PathSegment::Arc(..) => self.arc_segment_to_cubics(0.001)[0].start_curvature(),
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ new_key_type! {
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::aabb::{bounding_box_around_point, bounding_box_max_extent, merge_bounding_boxes, Aabb};
|
||||
use crate::aabb::{Aabb, bounding_box_around_point, bounding_box_max_extent, merge_bounding_boxes};
|
||||
use crate::epsilons::Epsilons;
|
||||
use crate::intersection_path_segment::{path_segment_intersection, segments_equal};
|
||||
use crate::path::Path;
|
||||
@@ -72,9 +72,8 @@ use crate::path_segment::PathSegment;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::path_to_path_data;
|
||||
use crate::quad_tree::QuadTree;
|
||||
|
||||
use glam::DVec2;
|
||||
use slotmap::{new_key_type, SlotMap};
|
||||
use slotmap::{SlotMap, new_key_type};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::fmt::Display;
|
||||
@@ -1090,7 +1089,11 @@ fn compute_dual(minor_graph: &MinorGraph) -> Result<DualGraph, BooleanError> {
|
||||
let (key, _) = *areas.iter().max_by_key(|(_, area)| ((area.abs() * 1000.) as u64)).unwrap();
|
||||
*key
|
||||
} else {
|
||||
*windings.iter().find(|(&_, winding)| (winding < &0) ^ reverse_winding).expect("No outer face of a component found.").0
|
||||
*windings
|
||||
.iter()
|
||||
.find(|&&(&_, ref winding)| (winding < &0) ^ reverse_winding)
|
||||
.expect("No outer face of a component found.")
|
||||
.0
|
||||
};
|
||||
#[cfg(feature = "logging")]
|
||||
dbg!(outer_face_key);
|
||||
@@ -1338,7 +1341,7 @@ fn get_selected_faces<'a>(predicate: &'a impl Fn(u8) -> bool, flags: &'a HashMap
|
||||
flags.iter().filter_map(|(key, &flag)| predicate(flag).then_some(*key))
|
||||
}
|
||||
|
||||
fn walk_faces<'a>(faces: &'a [DualVertexKey], edges: &SlotMap<DualEdgeKey, DualGraphHalfEdge>, vertices: &SlotMap<DualVertexKey, DualGraphVertex>) -> impl Iterator<Item = PathSegment> + 'a {
|
||||
fn walk_faces<'a>(faces: &'a [DualVertexKey], edges: &SlotMap<DualEdgeKey, DualGraphHalfEdge>, vertices: &SlotMap<DualVertexKey, DualGraphVertex>) -> impl Iterator<Item = PathSegment> + use<'a> {
|
||||
let face_set: HashSet<_> = faces.iter().copied().collect();
|
||||
// TODO: Try using a binary search to avoid the hashset construction
|
||||
let is_removed_edge = |edge: &DualGraphHalfEdge| face_set.contains(&edge.incident_vertex) == face_set.contains(&edges[edge.twin.unwrap()].incident_vertex);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::path_boolean::{self, FillRule, PathBooleanOperation};
|
||||
use crate::path_data::{path_from_path_data, path_to_path_data};
|
||||
|
||||
use core::panic;
|
||||
use glob::glob;
|
||||
use image::{DynamicImage, GenericImageView, RgbaImage};
|
||||
|
||||
@@ -9,7 +9,7 @@ syn = "2.0.87"
|
||||
[package]
|
||||
name = "rawkit"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "A library to extract images from camera raw files"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "rawkit-proc-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
description = "Procedural macros for Rawkit"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use toml::{Table, Value};
|
||||
|
||||
use quote::{ToTokens, quote};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use toml::{Table, Value};
|
||||
|
||||
enum CustomValue {
|
||||
String(String),
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn tag_derive(input: TokenStream) -> TokenStream {
|
||||
|
||||
let new_name = format_ident!("_{}", name);
|
||||
|
||||
let gen = quote! {
|
||||
let r#gen = quote! {
|
||||
struct #new_name {
|
||||
#( #struct_idents: <#struct_types as Tag>::Output ),*
|
||||
}
|
||||
@@ -39,5 +39,5 @@ pub fn tag_derive(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
r#gen.into()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::tiff::Ifd;
|
||||
use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::SonyDataOffset;
|
||||
use crate::tiff::Ifd;
|
||||
use crate::{RawImage, SubtractBlack, Transform};
|
||||
|
||||
use bitstream_io::{BitRead, BitReader, Endianness, BE};
|
||||
use bitstream_io::{BE, BitRead, BitReader, Endianness};
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub fn decode_a100<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
@@ -63,11 +62,7 @@ fn ljpeg_diff<R: Read + Seek, E: Endianness>(huff: &[u16], file: &mut BitReader<
|
||||
|
||||
let diff = read_n_bits_from_file(length, file) as i32;
|
||||
|
||||
if length == 0 || (diff & (1 << (length - 1))) == 0 {
|
||||
diff - (1 << length) - 1
|
||||
} else {
|
||||
diff
|
||||
}
|
||||
if length == 0 || (diff & (1 << (length - 1))) == 0 { diff - (1 << length) - 1 } else { diff }
|
||||
}
|
||||
|
||||
fn sony_arw_load_raw<R: Read + Seek>(width: usize, height: usize, file: &mut BitReader<R, BE>) -> Option<Vec<u16>> {
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, I
|
||||
use crate::tiff::values::CurveLookupTable;
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::{RawImage, SubtractBlack, Transform};
|
||||
|
||||
use rawkit_proc_macros::Tag;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::{BitsPerSample, BlackLevel, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels};
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::{RawImage, SubtractBlack, Transform};
|
||||
|
||||
use rawkit_proc_macros::Tag;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
|
||||
@@ -7,17 +7,15 @@ pub mod processing;
|
||||
pub mod tiff;
|
||||
|
||||
use crate::metadata::identify::CameraModel;
|
||||
|
||||
use processing::{Pixel, PixelTransform, RawPixel, RawPixelTransform};
|
||||
use rawkit_proc_macros::Tag;
|
||||
use std::io::{Read, Seek};
|
||||
use thiserror::Error;
|
||||
use tiff::file::TiffRead;
|
||||
use tiff::tags::{Compression, ImageLength, ImageWidth, Orientation, StripByteCounts, SubIfd, Tag};
|
||||
use tiff::values::Transform;
|
||||
use tiff::{Ifd, TiffError};
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) const CHANNELS_IN_RGB: usize = 3;
|
||||
pub(crate) type Histogram = [[usize; 0x2000]; CHANNELS_IN_RGB];
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::{Make, Model, Tag};
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
|
||||
use rawkit_proc_macros::Tag;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{Pixel, RawImage, CHANNELS_IN_RGB};
|
||||
use crate::{CHANNELS_IN_RGB, Pixel, RawImage};
|
||||
|
||||
impl RawImage {
|
||||
pub fn convert_to_rgb_fn(&self) -> impl Fn(Pixel) -> [u16; CHANNELS_IN_RGB] {
|
||||
pub fn convert_to_rgb_fn(&self) -> impl Fn(Pixel) -> [u16; CHANNELS_IN_RGB] + use<> {
|
||||
let Some(camera_to_rgb) = self.camera_to_rgb else { todo!() };
|
||||
|
||||
move |pixel: Pixel| {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{Histogram, Image, Pixel, CHANNELS_IN_RGB};
|
||||
use crate::{CHANNELS_IN_RGB, Histogram, Image, Pixel};
|
||||
use std::f64::consts::E;
|
||||
|
||||
impl Image<u16> {
|
||||
pub fn gamma_correction_fn(&self, histogram: &Histogram) -> impl Fn(Pixel) -> [u16; CHANNELS_IN_RGB] {
|
||||
pub fn gamma_correction_fn(&self, histogram: &Histogram) -> impl Fn(Pixel) -> [u16; CHANNELS_IN_RGB] + use<> {
|
||||
let percentage = self.width * self.height;
|
||||
|
||||
let mut white = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Histogram, Pixel, PixelTransform, RawImage, CHANNELS_IN_RGB};
|
||||
use crate::{CHANNELS_IN_RGB, Histogram, Pixel, PixelTransform, RawImage};
|
||||
|
||||
impl RawImage {
|
||||
pub fn record_histogram_fn(&self) -> RecordHistogram {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{RawImage, RawPixel, SubtractBlack};
|
||||
|
||||
impl RawImage {
|
||||
pub fn scale_to_16bit_fn(&self) -> impl Fn(RawPixel) -> u16 {
|
||||
pub fn scale_to_16bit_fn(&self) -> impl Fn(RawPixel) -> u16 + use<> {
|
||||
let black_level = match self.black {
|
||||
SubtractBlack::CfaGrid(x) => x,
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{RawImage, RawPixel};
|
||||
|
||||
impl RawImage {
|
||||
pub fn scale_white_balance_fn(&self) -> impl Fn(RawPixel) -> u16 {
|
||||
pub fn scale_white_balance_fn(&self) -> impl Fn(RawPixel) -> u16 + use<> {
|
||||
let Some(mut white_balance) = self.white_balance else { todo!() };
|
||||
|
||||
if white_balance[1] == 0. {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::RawPixel;
|
||||
use crate::{RawImage, SubtractBlack};
|
||||
|
||||
impl RawImage {
|
||||
pub fn subtract_black_fn(&self) -> impl Fn(RawPixel) -> u16 {
|
||||
pub fn subtract_black_fn(&self) -> impl Fn(RawPixel) -> u16 + use<> {
|
||||
match self.black {
|
||||
SubtractBlack::CfaGrid(black_levels) => move |pixel: RawPixel| pixel.value.saturating_sub(black_levels[2 * (pixel.row % 2) + (pixel.column % 2)]),
|
||||
_ => todo!(),
|
||||
|
||||
@@ -4,11 +4,10 @@ mod types;
|
||||
pub mod values;
|
||||
|
||||
use file::TiffRead;
|
||||
use tags::Tag;
|
||||
|
||||
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||
use std::fmt::Display;
|
||||
use std::io::{Read, Seek};
|
||||
use tags::Tag;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeOrientation, TypeSRational, TypeSShort, TypeShort, TypeSonyToneCurve, TypeString};
|
||||
use super::{Ifd, TagId, TiffError, TiffRead};
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub trait SimpleTag {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
use super::file::TiffRead;
|
||||
use super::values::{CurveLookupTable, Rational, Transform};
|
||||
use super::{Ifd, IfdTagType, TiffError};
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub struct TypeAscii;
|
||||
pub struct TypeByte;
|
||||
@@ -41,11 +40,7 @@ impl PrimitiveType for TypeAscii {
|
||||
|
||||
fn read_primitive<R: Read + Seek>(_: IfdTagType, file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
|
||||
let value = file.read_ascii()?;
|
||||
if value.is_ascii() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(TiffError::InvalidValue)
|
||||
}
|
||||
if value.is_ascii() { Ok(value) } else { Err(TiffError::InvalidValue) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// Only compile this file if the feature "rawkit-tests" is enabled
|
||||
#![cfg(feature = "rawkit-tests")]
|
||||
|
||||
use rawkit::RawImage;
|
||||
|
||||
use image::codecs::png::{CompressionType, FilterType, PngEncoder};
|
||||
use image::{ColorType, ImageEncoder};
|
||||
use libraw::Processor;
|
||||
use rawkit::RawImage;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::fs::{create_dir, metadata, read_dir, File};
|
||||
use std::fs::{File, create_dir, metadata, read_dir};
|
||||
use std::io::{BufWriter, Cursor, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
@@ -338,10 +337,10 @@ fn extract_data_from_dng_images() {
|
||||
}
|
||||
|
||||
fn extract_data_from_dng_image(path: &Path) {
|
||||
use rawkit::tiff::Ifd;
|
||||
use rawkit::tiff::file::TiffRead;
|
||||
use rawkit::tiff::tags::{ColorMatrix2, Make, Model};
|
||||
use rawkit::tiff::values::ToFloat;
|
||||
use rawkit::tiff::Ifd;
|
||||
use std::io::{BufReader, Write};
|
||||
|
||||
let reader = BufReader::new(File::open(path).unwrap());
|
||||
|
||||
Reference in New Issue
Block a user