diff --git a/crates/doc/src/pattern.rs b/crates/doc/src/pattern.rs index ac00a6206..fdab92861 100644 --- a/crates/doc/src/pattern.rs +++ b/crates/doc/src/pattern.rs @@ -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, Vec) { + 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] diff --git a/crates/render/src/pattern.rs b/crates/render/src/pattern.rs index 29c788a13..9cb5e874f 100644 --- a/crates/render/src/pattern.rs +++ b/crates/render/src/pattern.rs @@ -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]) { + 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> = 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); }