mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 15:48:13 +08:00
Add path-bool library (#1952)
* Add path-bool library * Cleanup code * Cargo format * Integrate boolean ops into graphite * Add test for editor crash * Fix edge sort floating point instability * Add unit test for red-dress failure * Backport tests and aux functions * Use curvature based sorting * Convert linear cubic splines to line segments * Deduplicate reversed path segments * Fix epsilon for empty segments * Remove parameter based intersection pruning * Add support for reversed paths * Add benchmark infrastructure * Add intersection benchmark * Add recursion bound * Implement support for overlapping path segments * Remove rouge prinln * Fix sorting for bezier segments with one control point at the start of the segment * Cleanup log statements * Directly translate graphite paths to Path segments * Round data before passing it to path_bool * Fix flag_faces traversal order * Add test for white dots in bottom right of painted dreams * Make rounding configurable * Update demo artwork to remove manual path modifications * Convert from path segments to manipulator groups directly * Remove dead code * Fix clippy lints * Replace functions in path segment with methods and add documentation * Add more documentation * Close subpaths * Reorganize files and add README.md * Add license information * Code review * Fix license info * Adopt new node macro and fix demo artwork * Close subpaths with Z --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
2febbfd698
commit
3eb98c6d6d
@@ -0,0 +1,89 @@
|
||||
use crate::aabb::Aabb;
|
||||
use crate::line_segment::LineSegment;
|
||||
|
||||
const INSIDE: u8 = 0;
|
||||
const LEFT: u8 = 1;
|
||||
const RIGHT: u8 = 1 << 1;
|
||||
const BOTTOM: u8 = 1 << 2;
|
||||
const TOP: u8 = 1 << 3;
|
||||
|
||||
fn out_code(x: f64, y: f64, bounding_box: &Aabb) -> u8 {
|
||||
let mut code = INSIDE;
|
||||
|
||||
if x < bounding_box.left {
|
||||
code |= LEFT;
|
||||
} else if x > bounding_box.right {
|
||||
code |= RIGHT;
|
||||
}
|
||||
|
||||
if y < bounding_box.top {
|
||||
code |= BOTTOM;
|
||||
} else if y > bounding_box.bottom {
|
||||
code |= TOP;
|
||||
}
|
||||
|
||||
code
|
||||
}
|
||||
|
||||
pub(crate) fn line_segment_aabb_intersect(seg: LineSegment, bounding_box: &Aabb) -> bool {
|
||||
let [mut p0, mut p1] = seg;
|
||||
|
||||
let mut outcode0 = out_code(p0.x, p0.y, bounding_box);
|
||||
let mut outcode1 = out_code(p1.x, p1.y, bounding_box);
|
||||
|
||||
loop {
|
||||
if (outcode0 | outcode1) == 0 {
|
||||
// bitwise OR is 0: both points inside window; trivially accept and exit loop
|
||||
return true;
|
||||
} else if (outcode0 & outcode1) != 0 {
|
||||
// bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP,
|
||||
// or BOTTOM), so both must be outside window; exit loop (accept is false)
|
||||
return false;
|
||||
} else {
|
||||
// failed both tests, so calculate the line segment to clip
|
||||
// from an outside point to an intersection with clip edge
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
|
||||
// At least one endpoint is outside the clip rectangle; pick it.
|
||||
let outcode_out = if outcode1 > outcode0 { outcode1 } else { outcode0 };
|
||||
|
||||
// Now find the intersection point;
|
||||
// use formulas:
|
||||
// slope = (y1 - y0) / (x1 - x0)
|
||||
// x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
|
||||
// y = y0 + slope * (xm - x0), where xm is xmin or xmax
|
||||
// No need to worry about divide-by-zero because, in each case, the
|
||||
// outcode bit being tested guarantees the denominator is non-zero
|
||||
if (outcode_out & TOP) != 0 {
|
||||
// point is above the clip window
|
||||
x = p0.x + (p1.x - p0.x) * (bounding_box.bottom - p0.y) / (p1.y - p0.y);
|
||||
y = bounding_box.bottom;
|
||||
} else if (outcode_out & BOTTOM) != 0 {
|
||||
// point is below the clip window
|
||||
x = p0.x + (p1.x - p0.x) * (bounding_box.top - p0.y) / (p1.y - p0.y);
|
||||
y = bounding_box.top;
|
||||
} else if (outcode_out & RIGHT) != 0 {
|
||||
// point is to the right of clip window
|
||||
y = p0.y + (p1.y - p0.y) * (bounding_box.right - p0.x) / (p1.x - p0.x);
|
||||
x = bounding_box.right;
|
||||
} else if (outcode_out & LEFT) != 0 {
|
||||
// point is to the left of clip window
|
||||
y = p0.y + (p1.y - p0.y) * (bounding_box.left - p0.x) / (p1.x - p0.x);
|
||||
x = bounding_box.left;
|
||||
}
|
||||
|
||||
// Now we move outside point to intersection point to clip
|
||||
// and get ready for next pass.
|
||||
if outcode_out == outcode0 {
|
||||
p0.x = x;
|
||||
p0.y = y;
|
||||
outcode0 = out_code(p0.x, p0.y, bounding_box);
|
||||
} else {
|
||||
p1.x = x;
|
||||
p1.y = y;
|
||||
outcode1 = out_code(p1.x, p1.y, bounding_box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user