mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Merge pull request #3223 from Matoo125/fix/nested-repeat-budget
Repeat: nested Repeats stop at 250,000 instances in all
This commit is contained in:
@@ -467,6 +467,14 @@ impl RepeatKind {
|
||||
RepeatKind::Mirror { .. } => "Mirror Repeat",
|
||||
}
|
||||
}
|
||||
/// How many instances [`repeat_transforms`] makes (with its clamps).
|
||||
fn count(&self) -> usize {
|
||||
match self {
|
||||
RepeatKind::Radial { instances, .. } => (*instances).clamp(1, 1000) as usize,
|
||||
RepeatKind::Grid { rows, cols, .. } => (*rows).clamp(1, 500) as usize * (*cols).clamp(1, 500) as usize,
|
||||
RepeatKind::Mirror { .. } => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -475,6 +483,23 @@ pub struct RepeatSpec {
|
||||
pub source: Vec<Arc<Node>>,
|
||||
}
|
||||
|
||||
/// Most instances a Repeat makes in all, those of the Repeats nested in its source included: as
|
||||
/// many as one 500 × 500 grid, the budget [`PatternDef::offsets_covering`] also keeps.
|
||||
const MAX_REPEAT_INSTANCES: usize = 250_000;
|
||||
|
||||
/// Instances the Repeats in `nodes` make in all (0 when there are none).
|
||||
fn nested_instances(nodes: &[Arc<Node>]) -> usize {
|
||||
let mut sum = 0usize;
|
||||
for n in nodes {
|
||||
sum = sum.saturating_add(match (&n.kind, n.children()) {
|
||||
(NodeKind::Repeat(r), _) => r.instances_in_all(),
|
||||
(_, Some(c)) => nested_instances(c),
|
||||
_ => 0,
|
||||
});
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
/// Reflection across the line through `c` with direction angle `deg`.
|
||||
pub fn reflect_about(c: Point, deg: f64) -> Affine {
|
||||
let t = (2.0 * deg).to_radians();
|
||||
@@ -536,7 +561,21 @@ impl RepeatSpec {
|
||||
crate::live::nodes_bounds(&self.source)
|
||||
}
|
||||
pub fn transforms(&self) -> Vec<Affine> {
|
||||
repeat_transforms(&self.kind, self.source_bounds())
|
||||
let mut v = repeat_transforms(&self.kind, self.source_bounds());
|
||||
// Each instance draws the Repeats in the source again, so nested ones multiply (a 500 × 500
|
||||
// grid of one is 62.5 billion instances): keep the first that fit the budget.
|
||||
v.truncate(self.kept(nested_instances(&self.source)));
|
||||
v
|
||||
}
|
||||
/// How many of its instances this Repeat keeps when each makes `nested` instances of the
|
||||
/// Repeats in its source: as many as fit in [`MAX_REPEAT_INSTANCES`], at least one.
|
||||
fn kept(&self, nested: usize) -> usize {
|
||||
self.kind.count().min(MAX_REPEAT_INSTANCES / nested.max(1)).max(1)
|
||||
}
|
||||
/// Instances made in all, those of the Repeats nested in the source included.
|
||||
fn instances_in_all(&self) -> usize {
|
||||
let nested = nested_instances(&self.source);
|
||||
self.kept(nested).saturating_mul(nested.max(1))
|
||||
}
|
||||
/// Evaluated instances: one group of the (transformed) source per instance (ids 0).
|
||||
pub fn expand(&self) -> Vec<Node> {
|
||||
@@ -838,6 +877,35 @@ mod tests {
|
||||
assert!((angle.rem_euclid(180.0) - 0.0).abs() < 1e-9 || (angle.rem_euclid(180.0) - 180.0).abs() < 1e-9, "{angle}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_repeats_share_one_instance_budget() {
|
||||
// Instances made in all, counted from the transforms without drawing any.
|
||||
fn made(r: &RepeatSpec) -> usize {
|
||||
let each: usize = r.source.iter().map(|s| if let NodeKind::Repeat(n) = &s.kind { made(n) } else { 1 }).sum();
|
||||
r.transforms().len().saturating_mul(each)
|
||||
}
|
||||
let grid = |source: Arc<Node>| {
|
||||
let mut g = RepeatSpec::grid(vec![source], 0.0, 0.0);
|
||||
if let RepeatKind::Grid { rows, cols, .. } = &mut g.kind {
|
||||
*rows = 500;
|
||||
*cols = 500;
|
||||
}
|
||||
g
|
||||
};
|
||||
let repeat = |r: RepeatSpec| Arc::new(Node::new(NodeId(2), NodeKind::Repeat(r)));
|
||||
// One grid keeps all its instances.
|
||||
let inner = grid(sq(0.0, 0.0, 1.0));
|
||||
assert_eq!(made(&inner), 250_000);
|
||||
// A 500 × 500 grid of it made 62.5 billion.
|
||||
let outer = grid(repeat(inner.clone()));
|
||||
assert!(made(&outer) <= 250_000, "{} instances", made(&outer));
|
||||
assert_eq!(outer.expand().len(), outer.transforms().len());
|
||||
assert!(made(&RepeatSpec::mirror(vec![repeat(inner)], 90.0, 5.0)) <= 250_000);
|
||||
// Small nests are left as they are.
|
||||
let small = RepeatSpec::mirror(vec![repeat(RepeatSpec::grid(vec![sq(0.0, 0.0, 10.0)], 1.0, 1.0))], 90.0, 5.0);
|
||||
assert_eq!(made(&small), 18);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_roundtrip() {
|
||||
let mut def = PatternDef::new("Dots", vec![sq(0.0, 0.0, 10.0)]);
|
||||
|
||||
Reference in New Issue
Block a user