mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 10:17:02 -06:00
Merge pull request #2016 from EricKuck/fix/ai-pattern-phase
AI import: a pattern fill tiles from its tile's lower left, wherever the definition drew the tile
This commit is contained in:
@@ -400,8 +400,8 @@ struct Reader<'a> {
|
||||
large_canvas: Option<f64>,
|
||||
/// This reader reads a pattern's definition (see [`Reader::define_pattern`]).
|
||||
pattern: Option<PatternHead>,
|
||||
/// The tile's top left (y down) of each pattern read, by name.
|
||||
pattern_tiles: HashMap<String, Point>,
|
||||
/// The tile (y down) of each pattern read, by name.
|
||||
pattern_tiles: HashMap<String, Rect>,
|
||||
/// The legacy text object being read (`To` … `TO`), and the modal type attributes.
|
||||
text: Option<legacy_text::LegacyText>,
|
||||
type_state: legacy_text::TypeState,
|
||||
@@ -739,7 +739,7 @@ impl<'a> Reader<'a> {
|
||||
let art: Vec<Arc<Node>> = frame.children.iter().map(|n| Arc::new(self.doc.reid(n))).collect();
|
||||
let mut def = PatternDef::new(&head.name, art);
|
||||
def.tile = FLIP.transform_rect_bbox(tile);
|
||||
if self.pattern_tiles.insert(def.name.clone(), def.tile.origin()).is_some() {
|
||||
if self.pattern_tiles.insert(def.name.clone(), def.tile).is_some() {
|
||||
self.doc.patterns.retain(|p| p.name != def.name);
|
||||
}
|
||||
self.doc.patterns.push(def);
|
||||
@@ -1411,13 +1411,15 @@ impl<'a> Reader<'a> {
|
||||
return;
|
||||
}
|
||||
let name = vals.iter().find_map(|v| if let V::Str(_) = v { v.text() } else { None });
|
||||
let Some(origin) = name.as_deref().and_then(|n| self.pattern_tiles.get(n)).map(|p| p.to_vec2()) else {
|
||||
let Some(tile) = name.as_deref().and_then(|n| self.pattern_tiles.get(n)).copied() else {
|
||||
self.unreadable("pattern fills");
|
||||
return;
|
||||
};
|
||||
let matrix = vals.iter().rev().find_map(|v| if let V::Arr(a) = v { paint::affine(&Self::nums(a)) } else { None });
|
||||
// The tile's art (y down from the tile's top left) → the pattern's space → art space.
|
||||
let xf = self.space() * matrix.unwrap_or(Affine::IDENTITY) * FLIP * Affine::translate(origin);
|
||||
// The tile's art (y down from the tile's top left) → the pattern's space → art space. The
|
||||
// tile's lower left is the pattern's origin wherever the definition drew it, as the file's
|
||||
// PDF part tiles it.
|
||||
let xf = self.space() * matrix.unwrap_or(Affine::IDENTITY) * FLIP * Affine::translate((0.0, -tile.height()));
|
||||
if xf.determinant().abs() < 1e-12 || !xf.as_coeffs().iter().all(|v| v.is_finite()) {
|
||||
self.unreadable("pattern fills");
|
||||
return;
|
||||
|
||||
@@ -202,6 +202,21 @@ fn patterns_are_pattern_swatches_and_fill_with_them() {
|
||||
assert!(s.warnings.is_empty(), "{:?}", s.warnings);
|
||||
}
|
||||
|
||||
/// Where a definition draws its tile doesn't move the fill: the tile's lower left is the pattern's
|
||||
/// origin, so the same dots drawn at 30..38 × 40..48 tile as they do at 0..8 × 0..8.
|
||||
#[test]
|
||||
fn a_pattern_tiles_from_its_lower_left_wherever_its_definition_drew_it() {
|
||||
let moved = DOTS
|
||||
.replace("(Dots) 0 0 8 8", "(Dots) 30 40 38 48")
|
||||
.replace("%_0 8 m\n%_8 8 L\n%_8 0 L\n%_0 0 L\n%_0 8 L", "%_30 48 m\n%_38 48 L\n%_38 40 L\n%_30 40 L\n%_30 48 L")
|
||||
.replace("%_4 4 m\n%_4 4 L", "%_34 44 m\n%_34 44 L");
|
||||
let body = format!("{moved}{}", layer(&format!("(Dots) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p\n{}f\n", rect_path(10.0, 20.0, 70.0, 80.0))));
|
||||
let s = read_ok(&body);
|
||||
assert_eq!(s.doc.pattern("Dots").unwrap().tile, Rect::new(30.0, -48.0, 38.0, -40.0));
|
||||
let Paint::Pattern { xf, .. } = fill(&art(&s.doc)[0]) else { panic!() };
|
||||
assert_eq!(xf * Point::ZERO, Point::new(0.0, 92.0));
|
||||
}
|
||||
|
||||
/// A pattern whose definition it can't read whole isn't kept: a fill with it is something the file
|
||||
/// has that isn't read, where it shows. Its definition costs nothing else.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user