Text: tracking grows lines from their paragraph alignment (#146)
The per-line tracking compensation in text_geom used only the animators' Line Anchor, which defaults to 0% (left) when no animator has one, so tracking on centred or right-aligned text pushed the line to the right instead of spreading it around its alignment point. Without a Line Anchor, a line now grows from where its own paragraph's alignment pins it: 50% for centred, 100% for right-aligned (a justified paragraph by its last line), read per paragraph rather than from the document. The Line Anchor property still overrides it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1edf858180
commit
2448deeb40
|
|
@ -533,6 +533,15 @@ pub fn caret_maps(ctx: &EvalCtx, layer: &Layer, chars: usize) -> Vec<Mat3> {
|
|||
out
|
||||
}
|
||||
|
||||
/// A paragraph's alignment: 0 left, 1 centre, 2 right (a justified paragraph by its last line).
|
||||
fn align_index(j: Justify) -> u8 {
|
||||
match j {
|
||||
Justify::Center | Justify::JustifyLastCenter => 1,
|
||||
Justify::Right | Justify::JustifyLastRight => 2,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Lay out and animate a text layer.
|
||||
pub fn text_geom(ctx: &EvalCtx, layer: &Layer) -> Option<TextGeom> {
|
||||
let doc = source_text(ctx, layer)?;
|
||||
|
|
@ -578,7 +587,9 @@ pub fn text_geom(ctx: &EvalCtx, layer: &Layer) -> Option<TextGeom> {
|
|||
})
|
||||
.collect();
|
||||
let advance = |gi: usize| lay.glyphs[gi].advance + vadv[gi];
|
||||
// Tracking (before / after each character) and Line Anchor, per line.
|
||||
// Tracking (before / after each character) and Line Anchor, per line. Without a Line Anchor
|
||||
// a line grows from where its paragraph's alignment pins it (left, centre or right).
|
||||
let paras = doc.paras();
|
||||
let mut shift = vec![0.0f64; n];
|
||||
let mut li_start = 0;
|
||||
while li_start < n {
|
||||
|
|
@ -588,7 +599,8 @@ pub fn text_geom(ctx: &EvalCtx, layer: &Layer) -> Option<TextGeom> {
|
|||
li_end += 1;
|
||||
}
|
||||
let mut pen = 0.0;
|
||||
let (mut la, mut la_k) = (0.0, 0.0);
|
||||
let justify = lay.layout.lines.get(line).and_then(|l| paras.get(l.para)).map_or(doc.justify, |p| p.justify);
|
||||
let (mut la, mut la_k) = (50.0 * f64::from(align_index(justify)), 0.0);
|
||||
for gi in li_start..li_end {
|
||||
let x = &xfs[gi];
|
||||
let em = lay.styles.get(lay.glyphs[gi].run).map_or(doc.size, |s| s.size) / 1000.0;
|
||||
|
|
@ -639,11 +651,7 @@ pub fn text_geom(ctx: &EvalCtx, layer: &Layer) -> Option<TextGeom> {
|
|||
|
||||
// Path Options.
|
||||
let on_path = text_path(ctx, layer, text);
|
||||
let align = match doc.justify {
|
||||
Justify::Center | Justify::JustifyLastCenter => 1,
|
||||
Justify::Right | Justify::JustifyLastRight => 2,
|
||||
_ => 0,
|
||||
};
|
||||
let align = align_index(doc.justify);
|
||||
let first_baseline = lay.line_boxes.first().map(|b| b[1]).unwrap_or(0.0);
|
||||
let mut arc = vec![0.0f64; n];
|
||||
if let Some((pm, _, force, fm, lm)) = &on_path {
|
||||
|
|
|
|||
|
|
@ -207,6 +207,39 @@ fn tracking_line_anchor_and_character_offset() {
|
|||
assert_eq!(substitute('a', &CharXf { char_value: Some((66.0, 1.0)), ..Default::default() }), Some('B'));
|
||||
}
|
||||
|
||||
/// Without a Line Anchor, tracking grows each line from where its paragraph's alignment pins it:
|
||||
/// left text grows right, centred text around its centre, right text to the left (#146).
|
||||
#[test]
|
||||
fn tracking_grows_from_each_paragraphs_alignment() {
|
||||
use effectcraft_keyframe::{Justify, ParaStyle};
|
||||
let tracked = |d: TextDoc, amount: f64| {
|
||||
let (mut p, cid, comp) = setup(600, 300);
|
||||
let mut l = text_layer(&mut p, &comp, d);
|
||||
animator(&mut p, &mut l, &["tracking"], &["range"]);
|
||||
set(&mut l, "text/animators/#1/properties/tracking", Value::Scalar(amount));
|
||||
p.comp_mut(cid).unwrap().layers.push(l);
|
||||
glyph_paths(&ctx(&p, cid, 0.0), &p.comp(cid).unwrap().layers[0])
|
||||
};
|
||||
// 4 chars × 500/1000 em × 40 px: 20 px of tracking each, half before and half after.
|
||||
for (justify, x0, centre, x1) in [(Justify::Left, 10.0, 40.0, 70.0), (Justify::Center, -30.0, 0.0, 30.0), (Justify::Right, -70.0, -40.0, -10.0)] {
|
||||
let d = TextDoc { justify, ..doc("ABCD", 40.0) };
|
||||
let (w0, w1) = (bounds_of(&tracked(d.clone(), 0.0)), bounds_of(&tracked(d, 500.0)));
|
||||
let got = [w1.x0 - w0.x0, w1.center().x - w0.center().x, w1.x1 - w0.x1];
|
||||
assert!(got.iter().zip([x0, centre, x1]).all(|(g, w)| (g - w).abs() < 1.0), "{justify:?}: moved {got:?}, want {:?}", [x0, centre, x1]);
|
||||
}
|
||||
// Each paragraph by its own alignment: a left paragraph above a right-aligned one.
|
||||
let mut d = doc("ABCD\nABCD", 40.0);
|
||||
let left = d.base_para();
|
||||
d.set_paras(vec![left.clone(), ParaStyle { justify: Justify::Right, ..left }]);
|
||||
let lines = |paths: Vec<(BezPath, CharXf)>| -> [kurbo::Rect; 2] {
|
||||
let (a, b): (Vec<_>, Vec<_>) = paths.into_iter().partition(|g| bounds_of(std::slice::from_ref(g)).center().y < 10.0);
|
||||
[bounds_of(&a), bounds_of(&b)]
|
||||
};
|
||||
let ([a0, b0], [a1, b1]) = (lines(tracked(d.clone(), 0.0)), lines(tracked(d, 500.0)));
|
||||
assert!((a1.x0 - a0.x0 - 10.0).abs() < 1.0, "the left paragraph grows right: {} {}", a0.x0, a1.x0);
|
||||
assert!((b1.x1 - b0.x1 + 10.0).abs() < 1.0, "the right paragraph grows left: {} {}", b0.x1, b1.x1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anchor_grouping_line_rotates_around_line_centre() {
|
||||
let (mut p, cid, comp) = setup(600, 300);
|
||||
|
|
|
|||
Loading…
Reference in New Issue