Pattern edit preview: two dimmed copy groups around centre tile

Review feedback on #3498: dimmed neighbours share one opacity composite
before the centre tile and one after, preserving overlap paint order
without per-copy compositing.

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-10-22 09:23:27 +07:00
committed by bflatastic
co-authored by nrq6993-a11y
parent e87534342b
commit d792e7b2be
2 changed files with 41 additions and 15 deletions
+20
View File
@@ -307,6 +307,23 @@ impl PatternDef {
}
out
}
/// Preview copies drawn before and after the centre tile in [`Self::preview_offsets_in_paint_order`],
/// for one dimmed composite each (the centre tile is drawn at full strength between them).
pub fn preview_offsets_dimmed_sides(&self) -> (Vec<Vec2>, Vec<Vec2>) {
let mut before = vec![];
let mut after = vec![];
let mut past_center = false;
for o in self.preview_offsets_in_paint_order() {
if o.x == 0.0 && o.y == 0.0 {
past_center = true;
} else if past_center {
after.push(o);
} else {
before.push(o);
}
}
(before, after)
}
/// Instances covering `region` (document space) as plain nodes for a pattern painted with
/// `paint_xf` (pattern space → document), fresh-id-less (id 0). Used by exporters and Distort
/// Pattern Fills; Expand uses [`Self::instances_in_with`].
@@ -768,6 +785,9 @@ mod tests {
d2.overlap = Overlap { right_in_front: true, bottom_in_front: true };
assert!(close(*d2.preview_offsets_in_paint_order().last().unwrap(), Vec2::new(10.0, 10.0)));
assert_eq!(def.preview_offsets_in_paint_order().len(), 9);
let (before, after) = def.preview_offsets_dimmed_sides();
assert_eq!(before.len() + after.len(), 8);
assert!(before.iter().all(|o| *o != Vec2::ZERO) && after.iter().all(|o| *o != Vec2::ZERO));
}
#[test]
+21 -15
View File
@@ -22,7 +22,7 @@ use std::sync::{Arc, Weak};
use vectorcraft_color::cms::Cms;
use vectorcraft_doc::pattern::{Overlap, PatternDef, TileType};
use vectorcraft_doc::{Document, Node};
use vectorcraft_geom::{Affine, Rect, Shape};
use vectorcraft_geom::{Affine, Rect, Shape, Vec2};
use vello_cpu::peniko::{Extend, ImageQuality, ImageSampler};
use vello_cpu::{Pixmap, RenderContext};
@@ -299,6 +299,22 @@ pub fn render_pattern_swatch(doc: &Document, name: &str, size: u32) -> Option<Re
}
impl Renderer {
/// Dimmed pattern-editing preview copies at `offsets`.
fn draw_dimmed_pattern_copies(&mut self, ctx: &mut RenderContext, f: &Frame, dim: f32, offsets: &[Vec2], children: &[Arc<Node>]) {
if offsets.is_empty() {
return;
}
let comp = crate::group::Composite { opacity: dim, ..Default::default() };
self.group(ctx, f, comp, &mut |r, c, fr| {
for o in offsets {
let frame = Frame { view: fr.view * Affine::translate(*o), visible: fr.visible - *o, ..*fr };
for a in children {
r.draw_arc(c, &frame, a);
}
}
});
}
/// Pattern editing mode: draw the tile layer, its dimmed copies and the tile edge instead of
/// the document. Returns false when not in pattern editing mode.
pub(crate) fn draw_pattern_edit(&mut self, ctx: &mut RenderContext, f: &Frame) -> bool {
@@ -308,20 +324,10 @@ impl Renderer {
let children: Vec<Arc<Node>> = layer.children().cloned().unwrap_or_default();
if layer.visible && !children.is_empty() {
let dim = (def.dim_copies / 100.0).clamp(0.0, 1.0);
for o in def.preview_offsets_in_paint_order() {
let is_center = o.x == 0.0 && o.y == 0.0;
if is_center {
self.draw_arc(ctx, f, layer);
} else {
let comp = crate::group::Composite { opacity: dim, ..Default::default() };
self.group(ctx, f, comp, &mut |r, c, fr| {
let frame = Frame { view: fr.view * Affine::translate(o), visible: fr.visible - o, ..*fr };
for a in &children {
r.draw_arc(c, &frame, a);
}
});
}
}
let (before, after) = def.preview_offsets_dimmed_sides();
self.draw_dimmed_pattern_copies(ctx, f, dim, &before, &children);
self.draw_arc(ctx, f, layer);
self.draw_dimmed_pattern_copies(ctx, f, dim, &after, &children);
} else {
self.draw_arc(ctx, f, layer);
}