mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 01:14:29 -06:00
Render: the frame after anchors of a long path move updates only the segments that touch them
The renderer keeps the path of a path or compound path of 1,024 anchors or more by object id, with the bounds of its segments and of its miter spikes in runs of 128 segments. When anchors move and the subpaths and anchor counts stay the same, the next frame rewrites only the path elements of the segments that start or end at a moved anchor, and measures again only the runs that hold them and the runs of the segments with length before them. Other edits make the path again as before. The pixels and the cull bounds are the same. - StrokeLayer::bounds_on is unchanged. The miter tip of one joint (miter_tip) and the stroke box with given tips (bounds_with_tips) are new functions beside it, and Appearance::stroked_bounds_by takes the box of each shaped stroke, so the cull bounds of a kept path use the kept tips. - geom::bez::has_length is the predicate reach.rs used for segments that draw, made public. Docs: docs/target-app-parity.md.
This commit is contained in:
@@ -4,6 +4,7 @@ use super::{
|
||||
holds_instance, holds_plugin_effect, images, kurbo, lru, mask_value, painted_items, plain_image, polygon_length, rects_overlap,
|
||||
shape_masked_image, single_plain_fill, text_geom_snapped, white_shape_mask,
|
||||
};
|
||||
use crate::kept_paths::{self, KeptPath};
|
||||
|
||||
impl Renderer {
|
||||
/// Cached cull bounds of a node (containers union their cached children), symbol instances
|
||||
@@ -28,12 +29,56 @@ impl Renderer {
|
||||
}
|
||||
acc
|
||||
}
|
||||
_ => brush_fx::cull_bounds(symbols, a),
|
||||
_ => self.kept_bounds(symbols, a).unwrap_or_else(|| brush_fx::cull_bounds(symbols, a)),
|
||||
};
|
||||
self.geom.insert(key, GeomEntry { node: a.clone(), bounds: b, path: None, stamp: self.stamp });
|
||||
b
|
||||
}
|
||||
|
||||
/// The kept path of long path or compound path `a` ([`KeptPath`]), made, or brought up to date
|
||||
/// from the one kept for its id, when it is not `a`'s yet. `None` when `a` has none.
|
||||
fn kept_path(&mut self, a: &Arc<Node>) -> Option<&mut KeptPath> {
|
||||
if !kept_paths::kept(a) {
|
||||
return None;
|
||||
}
|
||||
let stamp = self.stamp;
|
||||
let made = match self.kept.get_mut(&a.id) {
|
||||
Some(k) if Arc::ptr_eq(&k.node, a) => true,
|
||||
Some(k) => {
|
||||
// The object the path was made from, when no longer drawn, shares the path through
|
||||
// its `geom` entry: dropping that entry lets the path change in place.
|
||||
let old = Arc::as_ptr(&k.node) as usize;
|
||||
if self.geom.get(&old).is_some_and(|e| Arc::ptr_eq(&e.node, &k.node) && e.stamp != stamp) {
|
||||
self.geom.remove(&old);
|
||||
}
|
||||
k.update(a)
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
if !made {
|
||||
let k = KeptPath::new(a, stamp)?;
|
||||
self.kept.insert(a.id, k);
|
||||
}
|
||||
let k = self.kept.get_mut(&a.id)?;
|
||||
k.stamp = stamp;
|
||||
Some(k)
|
||||
}
|
||||
|
||||
/// Cull bounds of long path or compound path `a` measured from its kept path
|
||||
/// ([`Self::kept_path`]): the same as [`brush_fx::cull_bounds`]. `None` when it has no kept path
|
||||
/// or is measured another way ([`brush_fx::path_cull_bounds`]).
|
||||
fn kept_bounds(&mut self, symbols: &SymbolBoxes, a: &Arc<Node>) -> Option<Option<Rect>> {
|
||||
let k = self.kept_path(a)?;
|
||||
brush_fx::path_cull_bounds(symbols, a, || {
|
||||
let b = k.geometric()?;
|
||||
let bp = k.path.clone();
|
||||
Some(a.appearance.stroked_bounds_by(b, |s| {
|
||||
let tips = k.tips(s);
|
||||
s.bounds_with_tips(&bp, b, tips)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Drop the cached bounds of symbol instances and of the objects that hold them when `doc`'s
|
||||
/// symbols are not those of the document last drawn (by name and art): those bounds measured
|
||||
/// the instances by their symbol's art.
|
||||
@@ -105,7 +150,10 @@ impl Renderer {
|
||||
{
|
||||
return Some(p.clone());
|
||||
}
|
||||
let p = Arc::new(a.stroke_path()?);
|
||||
let p = match self.kept_path(a) {
|
||||
Some(k) => k.path.clone(),
|
||||
None => Arc::new(a.stroke_path()?),
|
||||
};
|
||||
let bounds = self.bounds_of(symbols, a);
|
||||
self.geom.insert(key, GeomEntry { node: a.clone(), bounds, path: Some(p.clone()), stamp: self.stamp });
|
||||
Some(p)
|
||||
|
||||
Reference in New Issue
Block a user