mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 15:57:49 +00:00
Graphs: an unchanged paint write keeps a series' kept gradient
A paint write that leaves a series mark's gradient as the graph drew it now keeps the series' stored gradient and its unit-box flag in two more cases: - a mark with no width or no height, such as a column for a value of 0, where the stored gradient was replaced by the mark's gradient in document coordinates and no mark took it on the next redraw; - a gradient kept in document coordinates (a file saved before the unit box), which a stroke weight written as it was moved to the unit box, so the next redraw placed it on each mark's own box.
This commit is contained in:
@@ -285,11 +285,16 @@ fn settle_series_paints(doc: &mut Document, before: SeriesPaintsBefore) -> bool
|
||||
/// The box a series keeps a gradient relative to: (0, 0)–(1, 1), as graphic styles keep theirs.
|
||||
const UNIT_BOX: Rect = vectorcraft_doc::GraphicStyle::UNIT_BOX;
|
||||
|
||||
/// The gradient `slot` keeps for its fill (`fill`) or stroke relative to the unit box, placed on box `on` as the graph
|
||||
/// draws it there. None when the slot keeps no such gradient or there is no box.
|
||||
/// The gradient `slot` keeps for its fill (`fill`) or stroke as the graph draws it on box `on`: one kept relative to the
|
||||
/// unit box placed on `on`, also when `on` has no width or no height, and one kept in document coordinates as it is kept.
|
||||
/// None when the slot keeps no gradient, or keeps one relative to the unit box and there is no box.
|
||||
fn kept_on(slot: &vectorcraft_doc::SeriesPaint, fill: bool, on: Option<Rect>) -> Option<Paint> {
|
||||
let (kept, unit) = if fill { (&slot.fill, slot.fill_unit_box) } else { (&slot.stroke, slot.stroke_unit_box) };
|
||||
let (Some(Paint::Gradient(g)), true, Some(on)) = (kept, unit, on) else { return None };
|
||||
let Some(Paint::Gradient(g)) = kept else { return None };
|
||||
if !unit {
|
||||
return Some(Paint::Gradient(g.clone()));
|
||||
}
|
||||
let on = on?;
|
||||
let mut g = g.clone();
|
||||
g.rebase(UNIT_BOX, on);
|
||||
Some(Paint::Gradient(g))
|
||||
@@ -331,18 +336,17 @@ fn same_paint_placed(a: &Paint, b: &Paint) -> bool {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// by half the weight for a stroke. The box can have no width or no height.
|
||||
fn paint_box(node: &Node, width: Option<f64>) -> Option<Rect> {
|
||||
let b = node.geometric_bounds()?;
|
||||
let b = width.map_or(b, |w| vectorcraft_doc::appearance::stroke_paint_bounds(b, w));
|
||||
(b.width() > 1e-9 && b.height() > 1e-9).then_some(b)
|
||||
Some(width.map_or(b, |w| vectorcraft_doc::appearance::stroke_paint_bounds(b, w)))
|
||||
}
|
||||
|
||||
/// `paint` as a series keeps it: a gradient placed on `from` (the edited mark's [`paint_box`]) is moved to the same place
|
||||
/// relative to [`UNIT_BOX`], so each mark takes it on its own box. Returns the paint and whether it was moved.
|
||||
/// relative to [`UNIT_BOX`], so each mark takes it on its own box. A box with no width or no height, which a gradient's
|
||||
/// place cannot be kept relative to, leaves the paint in document coordinates. Returns the paint and whether it was moved.
|
||||
fn unit_box_paint(paint: Paint, from: Option<Rect>) -> (Paint, bool) {
|
||||
match (paint, from) {
|
||||
match (paint, from.filter(|b| b.width() > 1e-9 && b.height() > 1e-9)) {
|
||||
(Paint::Gradient(mut g), Some(from)) => {
|
||||
g.rebase(from, UNIT_BOX);
|
||||
(Paint::Gradient(g), true)
|
||||
|
||||
@@ -2967,6 +2967,57 @@ fn rewriting_a_series_marks_drawn_gradient_stroke_records_no_undo_step() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writing the fill of a series mark with no height (a column for a value of 0) as it is, after the graph drew the mark's
|
||||
/// fill from a gradient the series keeps relative to the unit box, leaves the kept gradient and its unit-box flag as they
|
||||
/// are.
|
||||
#[test]
|
||||
fn rewriting_a_drawn_gradient_fill_on_a_series_mark_with_no_height_keeps_the_kept_gradient() {
|
||||
let (mut s, id, _, marks) = column_marks();
|
||||
s.execute("paint.setFill", &json!({"ids": [marks[0].0], "gradient": {"kind": "linear"}})).unwrap();
|
||||
s.execute("paint.setGradientGeom", &json!({"ids": [marks[0].0], "start": [0, 0], "end": [10, 40]})).unwrap();
|
||||
let kept = spec(&s, id).series_paints[0].clone();
|
||||
assert!(kept.fill_unit_box);
|
||||
s.execute("graph.setData", &json!({"id": id.0, "rows": [[0, 2], [3, 4], [6, 1]]})).unwrap();
|
||||
let flat = series_parts(&s, id, 0)
|
||||
.into_iter()
|
||||
.find(|m| matches!(m.appearance.fill_paint(), Paint::Gradient(_)) && m.geometric_bounds().is_some_and(|b| b.height() < 1e-9))
|
||||
.unwrap()
|
||||
.id;
|
||||
let changed = s.edit("Write the fill as it is", |d, _| Ok(super::capture_series_top_paints(d, &[(flat, true)]))).unwrap();
|
||||
assert!(!changed);
|
||||
assert_eq!(spec(&s, id).series_paints[0], kept);
|
||||
}
|
||||
|
||||
/// Writing a series mark's stroke weight as it is, after the graph drew the mark's stroke from a gradient the series keeps
|
||||
/// in document coordinates (a file saved before the unit box), leaves the kept gradient and its unit-box flag as they are
|
||||
/// and records no undo step.
|
||||
#[test]
|
||||
fn rewriting_a_series_marks_stroke_drawn_from_a_gradient_kept_in_document_coordinates_keeps_it() {
|
||||
let (mut s, id, _, marks) = column_marks();
|
||||
s.execute("paint.setStroke", &json!({"ids": [marks[1].0], "gradient": {"kind": "radial"}})).unwrap();
|
||||
s.execute("stroke.set", &json!({"ids": [marks[1].0], "weight": 2})).unwrap();
|
||||
s.execute("paint.setGradientGeom", &json!({"ids": [marks[1].0], "stroke": true, "start": [150, 200], "end": [170, 260]})).unwrap();
|
||||
let placed = s.doc().unwrap().doc.node(marks[1]).unwrap().appearance.stroke_paint();
|
||||
s.edit("Kept in document coordinates", |d, _| {
|
||||
let mut extras = d.node_mut(id).unwrap().extras_mut();
|
||||
let slot = &mut extras.graph.as_mut().unwrap().series_paints[0];
|
||||
(slot.stroke, slot.stroke_unit_box, slot.stroke_width) = (Some(placed.clone()), false, Some(2.0));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
s.execute("graph.setData", &json!({"id": id.0, "rows": [[1, 2], [3, 4], [6, 1]]})).unwrap();
|
||||
let kept = spec(&s, id).series_paints[0].clone();
|
||||
let undo = s.doc().unwrap().history.undo.len();
|
||||
let drawn: Vec<_> = series_parts(&s, id, 0).into_iter().filter(|m| m.appearance.stroke_paint() == placed).collect();
|
||||
assert!(drawn.len() >= 3, "{}", drawn.len());
|
||||
for mark in drawn {
|
||||
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 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
|
||||
/// band, which takes the kept stroke as its fill, has it at the same place on its own box.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user