mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 15:57:49 +00:00
Graphs: a series mark's gradient written unchanged leaves the kept gradient
A command that writes a series mark's paint compared the mark's gradient with the series' kept gradient placed on the mark's box exactly. After the mark was moved, the two differed by rounding, so the graph stored a slightly different gradient and recorded an undo step. The vector, focal point and freeform points now match within 1e-6 pt.
This commit is contained in:
@@ -282,6 +282,41 @@ fn kept_on(slot: &vectorcraft_doc::SeriesPaint, fill: bool, on: Option<Rect>) ->
|
||||
Some(Paint::Gradient(g))
|
||||
}
|
||||
|
||||
/// Whether `a` and `b` are the same paint, a gradient's vector, focal point and freeform points each within 1e-6 pt (the
|
||||
/// rounding a move or a placement on another box leaves).
|
||||
fn same_paint_placed(a: &Paint, b: &Paint) -> bool {
|
||||
let (Paint::Gradient(a), Paint::Gradient(b)) = (a, b) else { return a == b };
|
||||
let near = |p: vectorcraft_geom::Point, q: vectorcraft_geom::Point| (p - q).hypot() <= 1e-6;
|
||||
let mut a = (**a).clone();
|
||||
match (&mut a.geom, &b.geom) {
|
||||
(Some(ag), Some(bg)) => {
|
||||
let focal = match (ag.focal, bg.focal) {
|
||||
(Some(p), Some(q)) => near(p, q),
|
||||
(p, q) => p.is_none() && q.is_none(),
|
||||
};
|
||||
if !(near(ag.start, bg.start) && near(ag.end, bg.end) && focal) {
|
||||
return false;
|
||||
}
|
||||
(ag.start, ag.end, ag.focal) = (bg.start, bg.end, bg.focal);
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => return false,
|
||||
}
|
||||
match (&mut a.freeform, &b.freeform) {
|
||||
(Some(af), Some(bf)) => {
|
||||
if af.points.len() != bf.points.len() || !af.points.iter().zip(&bf.points).all(|(p, q)| near(p.at, q.at)) {
|
||||
return false;
|
||||
}
|
||||
for (p, q) in af.points.iter_mut().zip(&bf.points) {
|
||||
p.at = q.at;
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
_ => return false,
|
||||
}
|
||||
a == **b
|
||||
}
|
||||
|
||||
/// The box a gradient on `node`'s fill (`width` None) or on its stroke of `width` is placed on: the geometric bounds, grown
|
||||
/// by half the weight for a stroke. None for a box with no width or no height, which a gradient's place cannot be kept
|
||||
/// relative to.
|
||||
@@ -361,7 +396,7 @@ fn store_series_paints(doc: &mut Document, captures: Vec<Capture>) -> bool {
|
||||
}
|
||||
let Some(slot) = spec.series_paints.get_mut(index) else { continue };
|
||||
// A mark that shows the kept gradient as it was drawn leaves the kept gradient as it is.
|
||||
let drawn = kept_on(slot, fill, from).is_some_and(|kept| kept == paint);
|
||||
let drawn = kept_on(slot, fill, from).is_some_and(|kept| same_paint_placed(&kept, &paint));
|
||||
let (paint, unit) = unit_box_paint(paint, from);
|
||||
if fill {
|
||||
if !drawn {
|
||||
|
||||
@@ -2702,6 +2702,19 @@ fn rewriting_a_series_marks_drawn_gradient_stroke_records_no_undo_step() {
|
||||
assert!(s.doc().unwrap().doc == was, "writing mark {:?} changed the document", mark.id);
|
||||
assert_eq!(s.doc().unwrap().history.undo.len(), undo);
|
||||
}
|
||||
// A mark moved with its gradient shows the kept gradient on its moved box, up to rounding.
|
||||
let kept = spec(&s, id).series_paints[0].clone();
|
||||
for (i, mark) in series_parts(&s, id, 0).into_iter().filter(|m| matches!(m.appearance.stroke_paint(), Paint::Gradient(_))).enumerate() {
|
||||
s.execute("select.set", &json!({"ids": [mark.id.0]})).unwrap();
|
||||
s.execute("object.move", &json!({"dx": 13.37 + i as f64 * 0.731, "dy": -7.19})).unwrap();
|
||||
let moved = s.doc().unwrap().doc.node(mark.id).unwrap().geometric_bounds().unwrap();
|
||||
assert!((moved.x0 - mark.geometric_bounds().unwrap().x0 - 13.37 - i as f64 * 0.731).abs() < 1e-9, "mark {:?} did not move", mark.id);
|
||||
let undo = s.doc().unwrap().history.undo.len();
|
||||
let row = top_row(&s, mark.id, false);
|
||||
s.execute("appearance.setItem", &json!({"ids": [mark.id.0], "index": row, "weight": mark.appearance.stroke_width()})).unwrap();
|
||||
assert_eq!(spec(&s, id).series_paints[0], kept, "writing moved mark {:?} changed the kept stroke", mark.id);
|
||||
assert_eq!(s.doc().unwrap().history.undo.len(), undo);
|
||||
}
|
||||
}
|
||||
|
||||
/// A gradient placed on a line series' stroke is kept relative to the line's stroke box, and with Draw Filled Lines the
|
||||
|
||||
Reference in New Issue
Block a user