fix(mleader): straight leaders, real landing + connection-anchored text (#347)
Render MultiLeader labels from the stored context fields instead of re-deriving them: - Draw each leader line with the line's own path type (code 170) rather than the MultiLeader's overall/style type. A spline-style leader can carry straight lines; splining a straight two-point run through Catmull-Rom bowed it, so the leader looked wavy with a dirty elbow. - Drop the stray landing segment that ran from the dogleg end up to the text's top-left insertion, leaving just the horizontal dogleg. - Anchor the horizontal-leader text so its bottom line sits at the connection point (the box then spans connection.y → text_location.y, matching the writer's content_base_point + landing_gap geometry). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
208bb099d5
commit
b61bc0fdc0
1 changed files with 30 additions and 14 deletions
|
|
@ -205,8 +205,10 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option<TruckE
|
||||||
snap_pts.push(node(c));
|
snap_pts.push(node(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ml.path_type == MultiLeaderPathType::Spline && ctrl.len() >= 2 {
|
if line.path_type == MultiLeaderPathType::Spline && ctrl.len() >= 2 {
|
||||||
// Catmull-Rom spline through the bend points.
|
// Catmull-Rom spline through the bend points. Use the leader
|
||||||
|
// line's own path type — a spline-style MultiLeader can carry
|
||||||
|
// straight lines, and splining a straight run bows it.
|
||||||
let pts = catmull_rom_pts(&ctrl, 8);
|
let pts = catmull_rom_pts(&ctrl, 8);
|
||||||
for &pt in &pts {
|
for &pt in &pts {
|
||||||
points.push(pt);
|
points.push(pt);
|
||||||
|
|
@ -1212,7 +1214,11 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
snap_pts.push((Vec3::from(c).as_dvec3(), SnapHint::Node));
|
snap_pts.push((Vec3::from(c).as_dvec3(), SnapHint::Node));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ml.path_type == MultiLeaderPathType::Spline && ctrl.len() >= 2 {
|
// Use the leader LINE's own path type (its stored 170 code),
|
||||||
|
// not the MultiLeader's overall/style default: a spline-style
|
||||||
|
// leader can carry straight lines, and drawing those through a
|
||||||
|
// Catmull-Rom bows an otherwise-straight two-point segment.
|
||||||
|
if line.path_type == MultiLeaderPathType::Spline && ctrl.len() >= 2 {
|
||||||
let ctrl_f64: Vec<[f64; 3]> = ctrl
|
let ctrl_f64: Vec<[f64; 3]> = ctrl
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| [c[0] as f64, c[1] as f64, c[2] as f64])
|
.map(|c| [c[0] as f64, c[1] as f64, c[2] as f64])
|
||||||
|
|
@ -1259,7 +1265,12 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ml.enable_landing && ml.enable_dogleg && ml.dogleg_length > 0.0 {
|
if ml.enable_landing && ml.enable_dogleg && ml.dogleg_length > 0.0 {
|
||||||
// Horizontal landing toward the text's side (mirrors live).
|
// Horizontal landing (dogleg) from the leader elbow (connection
|
||||||
|
// point) toward the text side. The stored geometry places the
|
||||||
|
// text so its near-bottom edge sits one landing_gap past this
|
||||||
|
// dogleg end, so the dogleg stops here — drawing on to
|
||||||
|
// text_location (the block's top-left insertion) would streak a
|
||||||
|
// stray line up the side of the text.
|
||||||
let d = ml.dogleg_length * effective_scale as f64;
|
let d = ml.dogleg_length * effective_scale as f64;
|
||||||
let landing_end = [
|
let landing_end = [
|
||||||
(cp.x + text_sign_w * d) as f32,
|
(cp.x + text_sign_w * d) as f32,
|
||||||
|
|
@ -1269,15 +1280,6 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
points.push(nan);
|
points.push(nan);
|
||||||
points.push(cp_f);
|
points.push(cp_f);
|
||||||
points.push(landing_end);
|
points.push(landing_end);
|
||||||
// Continue the landing to the text's near edge so it stays
|
|
||||||
// attached as the text moves.
|
|
||||||
if ml.content_type == LeaderContentType::MText && !ml.context.text_string.is_empty()
|
|
||||||
{
|
|
||||||
let tx = (ml.context.text_location.x) as f32;
|
|
||||||
let ty = (ml.context.text_location.y) as f32;
|
|
||||||
points.push(landing_end);
|
|
||||||
points.push([tx, ty, cp_f[2]]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1399,7 +1401,21 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
// sit at large absolute coordinates and casting first then subtracting
|
// sit at large absolute coordinates and casting first then subtracting
|
||||||
// throws away the precision needed for the rotated sub-glyph offsets.
|
// throws away the precision needed for the rotated sub-glyph offsets.
|
||||||
let local_ins_x = (ins.x) as f32;
|
let local_ins_x = (ins.x) as f32;
|
||||||
let local_ins_y = (ins.y) as f32;
|
// Horizontal leaders attach at the connection point: the dogleg runs
|
||||||
|
// horizontally at connection_point.y, and the text's attachment line
|
||||||
|
// (bottom/middle/top per text_left_attachment) sits there. Anchor the
|
||||||
|
// insertion Y to that connection instead of the stored text_location.y
|
||||||
|
// so the text meets the landing exactly — the two can disagree by a
|
||||||
|
// few percent because our MText line height need not match the writer's.
|
||||||
|
let local_ins_y = match ml.text_attachment_direction {
|
||||||
|
TextAttachmentDirectionType::Horizontal => ml
|
||||||
|
.context
|
||||||
|
.leader_roots
|
||||||
|
.first()
|
||||||
|
.map(|r| r.connection_point.y as f32)
|
||||||
|
.unwrap_or(ins.y as f32),
|
||||||
|
TextAttachmentDirectionType::Vertical => ins.y as f32,
|
||||||
|
};
|
||||||
let z = (ins.z) as f32;
|
let z = (ins.z) as f32;
|
||||||
|
|
||||||
// Rotation: prefer text_direction (transforms survive rotations /
|
// Rotation: prefer text_direction (transforms survive rotations /
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue