mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3498 from nrq6993-a11y/stream/vc-feat1-3480-pattern-overlap-preview
Fix #3480: pattern edit preview honors Overlap stacking
This commit is contained in:
@@ -279,7 +279,7 @@ impl PatternDef {
|
||||
out
|
||||
}
|
||||
/// Offsets of the pattern-editing preview copies (`copies`×`copies` around the tile, the
|
||||
/// centre `(0, 0)` excluded).
|
||||
/// centre `(0, 0)` excluded), in row-major order (not paint order).
|
||||
pub fn preview_offsets(&self) -> Vec<Vec2> {
|
||||
let k = (self.copies.clamp(1, 15) / 2) as i64;
|
||||
let mut out = vec![];
|
||||
@@ -292,6 +292,38 @@ impl PatternDef {
|
||||
}
|
||||
out
|
||||
}
|
||||
/// Offsets of every pattern-editing preview copy, including the centre tile, in the same paint
|
||||
/// order [`offsets_covering`] uses for [`Overlap`].
|
||||
pub fn preview_offsets_in_paint_order(&self) -> Vec<Vec2> {
|
||||
let k = (self.copies.clamp(1, 15) / 2) as i64;
|
||||
let (i0, i1, j0, j1) = (-k, k, -k, k);
|
||||
let mut out = vec![];
|
||||
let rows: Vec<i64> = if self.overlap.bottom_in_front { (j0..=j1).collect() } else { (j0..=j1).rev().collect() };
|
||||
for &j in &rows {
|
||||
let cols: Vec<i64> = if self.overlap.right_in_front { (i0..=i1).collect() } else { (i0..=i1).rev().collect() };
|
||||
for i in cols {
|
||||
out.push(self.offset(i, j));
|
||||
}
|
||||
}
|
||||
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`].
|
||||
@@ -744,6 +776,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_paint_order_respects_overlap() {
|
||||
let mut def = PatternDef::new("p", vec![sq(0.0, 0.0, 10.0)]);
|
||||
def.copies = 3;
|
||||
assert!(close(*def.preview_offsets_in_paint_order().last().unwrap(), Vec2::new(-10.0, -10.0)));
|
||||
let mut d2 = def.clone();
|
||||
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]
|
||||
fn covering_offsets_and_overlap_order() {
|
||||
let def = PatternDef::new("p", vec![sq(0.0, 0.0, 10.0)]);
|
||||
|
||||
@@ -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,17 +324,13 @@ 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);
|
||||
let comp = crate::group::Composite { opacity: dim, ..Default::default() };
|
||||
self.group(ctx, f, comp, &mut |r, c, fr| {
|
||||
for o in def.preview_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);
|
||||
}
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
self.draw_arc(ctx, f, layer);
|
||||
// The tile edge and swatch bounds are editing aids: exports and thumbnails leave them out,
|
||||
// as they do guides (#3261).
|
||||
if f.opts.skip_templates {
|
||||
|
||||
Reference in New Issue
Block a user