mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Fix #1705: a Transform effect's copies are bounded by the anchors they make
Each Transform effect clamps its own copies to 1000, but each copies all of what the effect before it made: three of them on one square ask for a thousand million squares, from a small file. Copies now stop where the result would pass two million anchors (1000 copies of a 2000-anchor path).
This commit is contained in:
1 parent
48041c8cd3
commit
779120cb5b
2 files changed
+35
No files matched your search
@@ -108,6 +108,10 @@ const MAX_SCALE: f64 = 100_000.0;
|
||||
const MAX_MOVE: f64 = 1.0e7;
|
||||
/// The Transform effect's copies stop before one reaches further than this (pt) from the origin.
|
||||
const MAX_REACH: f64 = 1.0e9;
|
||||
/// Most anchors a Transform effect's copies make of a path, the original included: 1000 copies of
|
||||
/// a 2000-anchor path. Stacked Transform effects multiply their copies (three of 1000 copies make
|
||||
/// a thousand million of one square), so the limit is on the result, not on each effect's count.
|
||||
pub(crate) const MAX_COPY_ANCHORS: usize = 2_000_000;
|
||||
|
||||
/// Transform effect: scale, rotate and reflect about reference point `reference` of the bounds
|
||||
/// (the 9-point grid, 4 = centre), then move; `copies` more times, each from the last. With
|
||||
@@ -115,6 +119,11 @@ const MAX_REACH: f64 = 1.0e9;
|
||||
/// the angle a share of theirs, drawn from `seed` (the object's), so an object keeps its result
|
||||
/// on every redraw while each object varies its own way.
|
||||
pub fn transform(path: &PathData, b: Rect, p: &Value, seed: u64) -> PathData {
|
||||
transform_within(path, b, p, seed, MAX_COPY_ANCHORS)
|
||||
}
|
||||
|
||||
/// [`transform`] whose copies make at most `max` anchors of the path, the original included.
|
||||
pub(crate) fn transform_within(path: &PathData, b: Rect, p: &Value, seed: u64, max: usize) -> PathData {
|
||||
let random = flag(p, "random", false);
|
||||
// The share (0..1) of value `k` applied.
|
||||
let share = |k: u64| if random { (noise(seed, k, 0, 0) + 1.0) / 2.0 } else { 1.0 };
|
||||
@@ -132,9 +141,17 @@ pub fn transform(path: &PathData, b: Rect, p: &Value, seed: u64) -> PathData {
|
||||
return path.transformed(m);
|
||||
}
|
||||
let within = |r: Rect| [r.x0, r.y0, r.x1, r.y1].iter().all(|v| v.abs() <= MAX_REACH);
|
||||
// Each copy adds the path once more, and a Transform after this one copies the lot again:
|
||||
// copies stop where the result would pass [`MAX_COPY_ANCHORS`] (#1705).
|
||||
let each = path.anchor_count();
|
||||
let mut anchors = each;
|
||||
let mut out = path.clone();
|
||||
let mut cur = path.clone();
|
||||
for _ in 0..copies {
|
||||
anchors = anchors.saturating_add(each);
|
||||
if anchors > max {
|
||||
break;
|
||||
}
|
||||
cur = cur.transformed(m);
|
||||
if !cur.bounds().is_some_and(within) {
|
||||
break;
|
||||
|
||||
@@ -206,6 +206,24 @@ fn random_transform_is_stable_per_object_and_within_the_values() {
|
||||
assert!(close(r.width(), 50.0, 1e-9) && close(r.center().x, 90.0, 1e-9), "{r:?}");
|
||||
}
|
||||
|
||||
/// #1705: stacked Transform effects multiply their copies; the anchors they make are bounded,
|
||||
/// whatever each one's count.
|
||||
#[test]
|
||||
fn stacked_transform_copies_stay_within_the_anchor_budget() {
|
||||
let p = json!({"copies": 1000});
|
||||
let b = square().bounds().unwrap();
|
||||
let mut path = square();
|
||||
let mut counts = vec![];
|
||||
for _ in 0..3 {
|
||||
path = crate::distort::transform_within(&path, b, &p, 0, 100);
|
||||
counts.push(path.anchor_count());
|
||||
}
|
||||
assert_eq!(counts, [100, 100, 100], "25 squares, then no room for a copy of them");
|
||||
// Within the budget every copy is made, and the effect's own budget takes a thousand copies.
|
||||
assert_eq!(crate::distort::transform_within(&square(), b, &json!({"copies": 24}), 0, 100).subpaths.len(), 25);
|
||||
assert_eq!(run("distort.transform", p, &square()).subpaths.len(), 1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transform_copies_stop_before_they_overflow() {
|
||||
let out = run("distort.transform", json!({"scaleH": 100000, "scaleV": 100000, "copies": 1000}), &square());
|
||||
|
||||
Reference in new issue
Block a user