Skip to content

Commit

Permalink
Fix sorting for bezier segments with one control point at the start o…
Browse files Browse the repository at this point in the history
…f the segment
  • Loading branch information
TrueDoctor committed Sep 16, 2024
1 parent 26d9e1e commit 59e9dee
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 18 deletions.
15 changes: 4 additions & 11 deletions libraries/path-bool/src/path_boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::quad_tree::QuadTree;
use crate::vector::{vectors_equal, Vector};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet, VecDeque};
use std::f64::consts::TAU;

#[derive(Debug, Clone, Copy)]
pub enum PathBooleanOperation {
Expand Down Expand Up @@ -114,11 +115,8 @@ impl MinorGraphEdge {
fn compare_segments(a: &PathSegment, b: &PathSegment) -> Ordering {
let angle_a = a.start_angle();
let angle_b = b.start_angle();
use std::f64::consts::PI;

// Normalize angles to [0, 2π)
let angle_a = (angle_a + 2.0 * PI) % (2.0 * PI);
let angle_b = (angle_b + 2.0 * PI) % (2.0 * PI);
let angle_a = (angle_a * 1000.).round() / 1000.;
let angle_b = (angle_b * 1000.).round() / 1000.;
// dbg!(a, angle_a.to_degrees(), b, angle_b.to_degrees());
Expand Down Expand Up @@ -671,12 +669,6 @@ fn get_incidence_angle(edge: &MinorGraphEdge) -> f64 {
fn sort_outgoing_edges_by_angle(graph: &mut MinorGraph) {
for vertex in graph.vertices.values_mut() {
if vertex.outgoing_edges.len() > 2 {
let edges: Vec<_> = vertex
.outgoing_edges
.iter()
.map(|key| (*key, &graph.edges[*key]))
.map(|(key, edge)| ((key.0.as_ffi() & 0xFF), get_incidence_angle(edge)))
.collect();
vertex.outgoing_edges.sort_by(|&a, &b| {
// TODO(@TrueDoctor): Make more robust. The js version seems to sort the data slightly differently when the angles are reallly close. In that case put the edge wich was discovered later first.
let new = graph.edges[a].partial_cmp(&graph.edges[b]).unwrap();
Expand All @@ -692,7 +684,7 @@ fn sort_outgoing_edges_by_angle(graph: &mut MinorGraph) {
.outgoing_edges
.iter()
.map(|key| (*key, &graph.edges[*key]))
.map(|(key, edge)| ((key.0.as_ffi() & 0xFF), get_incidence_angle(edge)))
.map(|(key, edge)| ((key.0.as_ffi() & 0xFF), (dbg!(edge.start_segment()).start_angle())))
.collect();
#[cfg(feature = "logging")]
dbg!(edges);
Expand Down Expand Up @@ -808,7 +800,7 @@ fn compute_dual(minor_graph: &MinorGraph) -> Result<DualGraph, BooleanError> {

for (start_edge_key, start_edge) in &minor_graph.edges {
#[cfg(feature = "logging")]
eprintln!("Processing start edge: {}", (start_edge_key.0.as_ffi() & 0xFF) - 1);
eprintln!("Processing start edge: {}", (start_edge_key.0.as_ffi() & 0xFF));
if minor_to_dual_edge.contains_key(&start_edge_key) {
continue;
}
Expand Down Expand Up @@ -970,6 +962,7 @@ fn compute_dual(minor_graph: &MinorGraph) -> Result<DualGraph, BooleanError> {
}
let outer_face_key = if count != 1 {
// return Err(BooleanError::MultipleOuterFaces);
eprintln!("Found multiple outer faces: {areas:?}, falling back to area calculation");
*areas.iter().max_by_key(|(_, area)| ((area.abs() * 1000.) as u64)).unwrap().0
} else {
*windings.iter().find(|(&_, winding)| (winding < &0) ^ reverse_winding).expect("No outer face of a component found.").0
Expand Down
16 changes: 12 additions & 4 deletions libraries/path-bool/src/path_segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ 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::{deg2rad, lerp, vector_angle};
use crate::vector::Vector;
use crate::vector::{vectors_equal, Vector};
use crate::EPS;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PathSegment {
Expand All @@ -20,9 +21,16 @@ pub enum PathSegment {
impl PathSegment {
pub fn start_angle(&self) -> f64 {
let angle = match *self {
PathSegment::Line(start, end) => (end - start).angle_to(DVec2::X),
PathSegment::Cubic(start, control1, _, _) => (control1 - start).angle_to(DVec2::X),
PathSegment::Quadratic(start, control, _) => (control - start).angle_to(DVec2::X),
PathSegment::Line(start, end) => (end - start).to_angle(),
PathSegment::Cubic(start, control1, control2, _) => {
let diff = control1 - start;
if vectors_equal(diff, DVec2::ZERO, EPS.point) {
(control2 - start).to_angle()
} else {
diff.to_angle()
}
}
PathSegment::Quadratic(start, control, _) => (control - start).to_angle(),
PathSegment::Arc(..) => arc_segment_to_cubics(self, 0.001)[0].start_angle(),
};
use std::f64::consts::TAU;
Expand Down
7 changes: 4 additions & 3 deletions libraries/path-bool/src/visual_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,11 @@ fn visual_tests() {
fn render_svg(svg_code: &str) -> DynamicImage {
let opts = Options::default();
let tree = Tree::from_str(svg_code, &opts).unwrap();
let pixmap_size = tree.size.to_screen_size();
let (width, height) = (pixmap_size.width(), pixmap_size.height());
let pixmap_size = tree.size();
let (width, height) = (pixmap_size.width() as u32, pixmap_size.height() as u32);
let mut pixmap = resvg::tiny_skia::Pixmap::new(width, height).unwrap();
render(&tree, resvg::usvg::FitTo::Original, Transform::default(), pixmap.as_mut());
let mut pixmap_mut = pixmap.as_mut();
render(&tree, Transform::default(), &mut pixmap_mut);
DynamicImage::ImageRgba8(RgbaImage::from_raw(width, height, pixmap.data().to_vec()).unwrap())
}

Expand Down

0 comments on commit 59e9dee

Please sign in to comment.