mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 19:27:21 -06:00
Fix #1770: Shape Builder merges leave no spur or split outline when shared edges differ by rounding
The Shape Builder merges faces by uniting their contours, and the edges two faces share cancel out. The sweep can give two faces copies of a shared edge whose ends lie a few times its tolerance apart, with the difference left as pieces shorter than the tolerance. The union could then keep the space between the copies: a hairline that joined a hole as a spur, or a seam that cut a petal tip off the ring's outer outline as an outline of its own. On Apple Silicon, turning one petal of the rosette test by -180 degrees instead of 180 leaves a spur. A Shape Builder action unites the picked faces first, and then what is left of each shape. When two copies of a shared edge differ in the first of these unions that unites the faces' contours, each point of the faces' contours that lies within 16 times the sweep's tolerance of a point kept before it now moves onto that point before that union and each later one, with the handles next to it. A piece left within that distance of one point is dropped. The planar map joins the ends of copies of an edge within the same distance. Copies that share their ends leave at most a hairline contour, which the merge drops as a sliver. When the copies in that union all match exactly, the unions take the contours unchanged. Only the Shape Builder's merges and deletions unite faces this way, so Pathfinder output does not change.
This commit is contained in:
@@ -1127,8 +1127,9 @@ fn image_trace_views_draw_on_screen_only_and_expand_to_the_result() {
|
||||
/// #569: merging the regions of a rosette of unfilled ellipses (each off the centre, petals
|
||||
/// overlapping) merges every region the drags touch into one shape whose outline is only the
|
||||
/// merged area's border: no edge between merged regions stays in, as a stray line or a spur.
|
||||
#[test]
|
||||
fn shape_builder_merges_rosette_rings_cleanly() {
|
||||
/// Petal `k` turns about the center by 45° × `k`, or by each angle `turns` lists for it in turn.
|
||||
#[track_caller]
|
||||
fn rosette_rings_merge_cleanly(turns: &[(u32, &[f64])]) {
|
||||
let mut s = session();
|
||||
s.execute("paint.setFill", &json!({"none": true})).unwrap();
|
||||
let c = [400.0, 320.0];
|
||||
@@ -1137,7 +1138,10 @@ fn shape_builder_merges_rosette_rings_cleanly() {
|
||||
let id =
|
||||
s.execute("shape.ellipse", &json!({"x": c[0] - 60.0, "y": c[1] - 172.0, "width": 120, "height": 324})).unwrap()["id"].as_u64().unwrap();
|
||||
s.execute("select.set", &json!({"ids": [id]})).unwrap();
|
||||
s.execute("object.rotate", &json!({"angle": 45.0 * f64::from(k), "origin": c})).unwrap();
|
||||
let angles = turns.iter().find(|t| t.0 == k).map_or_else(|| vec![45.0 * f64::from(k)], |t| t.1.to_vec());
|
||||
for angle in angles {
|
||||
s.execute("object.rotate", &json!({"angle": angle, "origin": c})).unwrap();
|
||||
}
|
||||
petals.push(id);
|
||||
}
|
||||
s.execute("select.set", &json!({"ids": petals})).unwrap();
|
||||
@@ -1187,3 +1191,18 @@ fn shape_builder_merges_rosette_rings_cleanly() {
|
||||
let clean = vectorcraft_pathops::normalize(&ring, rule);
|
||||
assert!((ring.length() - clean.length()).abs() < 1.0, "{} pt of outline, {} without inner edges", ring.length(), clean.length());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shape_builder_merges_rosette_rings_cleanly() {
|
||||
rosette_rings_merge_cleanly(&[]);
|
||||
}
|
||||
|
||||
/// #1770: the rosette merges as cleanly with a petal turned by an equivalent angle (-180° for 180°,
|
||||
/// -855° for 225°, -45° for 315°) or in two steps, which moves its outline by a rounding error.
|
||||
#[test]
|
||||
fn shape_builder_merges_rosette_rings_cleanly_however_a_petal_turns() {
|
||||
rosette_rings_merge_cleanly(&[(4, &[-180.0])]);
|
||||
rosette_rings_merge_cleanly(&[(5, &[-855.0])]);
|
||||
rosette_rings_merge_cleanly(&[(7, &[-45.0])]);
|
||||
rosette_rings_merge_cleanly(&[(4, &[100.0, 80.0])]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user