mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 07:13:46 +00:00
3D: a symbol holding too many objects is refused before its art is placed
A 3D effect on a symbol instance placed a copy of the symbol's whole art before counting it against the effect's work limit, so a symbol far past the limit was copied in full on every check and redraw before it was refused. The symbol's objects are now counted first, and a symbol that would pass the limit is refused with the same message without being placed.
This commit is contained in:
@@ -99,14 +99,15 @@ impl Resolver<'_> {
|
||||
fn spend(&mut self, steps: usize) -> Result<(), String> {
|
||||
self.work = self.work.saturating_add(steps);
|
||||
if self.work > MAX_WORK {
|
||||
return Err(format!(
|
||||
"{} can't build solids from this art: its symbol instances, blends and clipping groups hold too many objects",
|
||||
self.label
|
||||
));
|
||||
return Err(self.too_much_work());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn too_much_work(&self) -> String {
|
||||
format!("{} can't build solids from this art: its symbol instances, blends and clipping groups hold too many objects", self.label)
|
||||
}
|
||||
|
||||
/// `f` run on the art of `c`. The effects of an object or group can move its art into a clip,
|
||||
/// so inside one with effects no image is left out for lying outside the clip ([`Self::shown`]).
|
||||
fn inside<T>(&mut self, c: &Node, f: impl FnOnce(&mut Self) -> Result<T, String>) -> Result<T, String> {
|
||||
@@ -129,6 +130,11 @@ impl Resolver<'_> {
|
||||
self.spend(1)?;
|
||||
match &n.kind {
|
||||
NodeKind::SymbolInstance { symbol, .. } => {
|
||||
// Counted before the art is placed, so a symbol past the limit is never copied.
|
||||
let held = self.doc.and_then(|d| d.symbols.iter().find(|s| s.name == *symbol)).map_or(0, |s| s.art.count_with_masks());
|
||||
if self.work.saturating_add(held) > MAX_WORK {
|
||||
return Err(self.too_much_work());
|
||||
}
|
||||
let art = crate::outline_object(self.doc, n).ok_or_else(|| {
|
||||
format!("{label} can't build a solid from the instance of symbol \"{symbol}\": the symbol's art isn't available")
|
||||
})?;
|
||||
|
||||
@@ -363,6 +363,18 @@ fn nested_symbol_instances_that_repeat_hidden_art_are_refused_past_the_work_limi
|
||||
assert!(solid_art_in(Some(&d), &n).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_symbol_holding_more_objects_than_the_work_limit_is_refused_before_its_art_is_placed() {
|
||||
let mut d = document();
|
||||
let members = (0..=source::MAX_WORK).map(|i| Arc::new(rect(0, i as f64, 0.0, i as f64 + 1.0, 1.0, 0.0))).collect();
|
||||
d.symbols.push(Symbol { dynamic: false, name: "Big".into(), art: Arc::new(Node::group(NodeId(0), members)) });
|
||||
let n = with(instance(30, "Big", Affine::IDENTITY), EXTRUDE, json!({}));
|
||||
let err = validate_three_d_in(Some(&d), &n, EXTRUDE, &json!({})).unwrap_err();
|
||||
assert!(err.contains("hold too many objects"), "{err}");
|
||||
let work = source::WORK.with(std::cell::Cell::get);
|
||||
assert!(work <= 2, "refused before the symbol's art is placed and listed: {work}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sources_and_sources_inside_sources_build_or_refuse_cleanly() {
|
||||
let mut d = document();
|
||||
|
||||
Reference in New Issue
Block a user