Merge main into feat/plugin-effect-paint

This commit is contained in:
dotCooCoo
2026-11-08 17:59:33 -08:00
145 changed files with 5805 additions and 400 deletions
+4
View File
@@ -329,6 +329,10 @@ pub struct Scaling {
/// Pattern fills and strokes move with the art (Transform Patterns; General › Transform
/// Pattern Tiles is its default): their tiles transform too, instead of staying put.
pub patterns: bool,
/// Gradients fitted to the object stay fitted under a transform that flattens it to a line or
/// a point (no area left), as a Transform effect leaves them, instead of being fixed to their
/// fit and flattened to one color with the object.
pub keep_fit_when_flat: bool,
}
impl Scaling {
+14 -3
View File
@@ -433,7 +433,7 @@ impl Node {
// Type and symbols in perspective keep looking the same, moved by `a`.
self.transform_projection(a);
// Refitting an unplaced gradient only reproduces moves and uniform scales.
if !keeps_gradient_fit(a) {
if !keeps_gradient_fit(a) && !(sc.keep_fit_when_flat && flattens(a)) {
self.pin_gradients();
}
let k = Scaling::factor(a);
@@ -772,13 +772,24 @@ impl Node {
}
}
/// Is `a` a move plus a positive uniform scale (after which a refit gradient still matches)?
fn keeps_gradient_fit(a: Affine) -> bool {
/// Is `a` a move plus a positive uniform scale (after which a refit gradient still matches)? The
/// terms are compared within 1e-12 of the scale (at least of 1). A scale of 0 or less, a turn, a
/// reflection and an uneven scale give false, and so does a map that flattens the object
/// ([`flattens`]), which callers that keep a fitted gradient there check apart.
pub fn keeps_gradient_fit(a: Affine) -> bool {
let [m0, m1, m2, m3, _, _] = a.as_coeffs();
let eps = 1e-12 * m0.abs().max(1.0);
m0 > 0.0 && m1.abs() <= eps && m2.abs() <= eps && (m0 - m3).abs() <= eps
}
/// Does `a` flatten what it maps to a line or a point (its determinant 0, within 1e-12 of the
/// squares of its terms), or is it not finite?
pub fn flattens(a: Affine) -> bool {
let [m0, m1, m2, m3, _, _] = a.as_coeffs();
let norm = m0 * m0 + m1 * m1 + m2 * m2 + m3 * m3;
!a.is_finite() || a.determinant().abs() <= 1e-12 * norm
}
impl Node {
/// Whether a document point lies in this object's filled regions ([`Node::clip_shapes`]; true
/// everywhere for objects without them, such as type, whose boxes stand in).
+2 -1
View File
@@ -221,7 +221,8 @@ fn assert_round_corners(n: &Node, back: Affine, r: f64) {
}
}
const KEEP_CORNERS: Scaling = Scaling { strokes: false, effects: None, keep_type_strokes: false, keep_corners: true, patterns: false };
const KEEP_CORNERS: Scaling =
Scaling { strokes: false, effects: None, keep_type_strokes: false, keep_corners: true, patterns: false, keep_fit_when_flat: false };
#[test]
fn uneven_scales_keep_live_corners_circular() {