forked from 1b-agent/opencadstudio-upstream
feat(leader): dynamic MLEADER text placement, grips, and consistent arrows
Reworks MLEADER text/leader geometry and grip editing, and aligns LEADER to MLEADER's conventions (kept as separate entities). Text & landing (render path): - Text is side-anchored on the leader-facing edge and reads outward; the side, alignment and the horizontal landing are recomputed every frame from the text grip's position relative to the leader, so moving the arrow or the text re-mirrors the whole layout and the landing stays attached to the text's near edge. - Fixes #49: text was centred on the leader endpoint regardless of side. Grips: - Text-location grip plus an MTEXT-style triangle wrap-width grip at the box's far edge; dragging it sets the MText wrap width. - Multifunction grip menu restored and made functional: leader-line vertices offer Add Leader / Remove Leader (Add grabs the new arrow so it follows the cursor — click places, Esc removes it); the text grip offers Move Independent (text only) and Move with Leader (translates the whole multileader via a move-all sentinel grip). Arrows: - LEADER and MLEADER arrowheads now render at the same size (text height), matching previews. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
6f64d8d0d9
commit
8b3d083789
6 files changed
+395
-162
No files matched your search
@@ -1,6 +1,6 @@
|
||||
// LEADER command
|
||||
//
|
||||
// Flow (matching the classic AutoCAD LEADER):
|
||||
// Flow:
|
||||
// 1. Click the arrowhead point, then one or more bend points.
|
||||
// 2. Enter (≥2 points) places the leader line plus a linked, empty MText
|
||||
// annotation at the landing, and opens the in-place MText editor so the
|
||||
@@ -132,7 +132,7 @@ fn build_mtext(text: &str, pos: Vec3, height: f64, attach: AttachmentPoint) -> M
|
||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
let mut points: Vec<[f32; 3]> = pts.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
if pts.len() >= 2 {
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.0);
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.5);
|
||||
points.push([f32::NAN; 3]);
|
||||
points.push([w1.x, w1.y, w1.z]);
|
||||
points.push([pts[0].x, pts[0].y, pts[0].z]);
|
||||
|
||||
@@ -102,25 +102,35 @@ fn build_mleader(text: &str, verts: &[Vec3]) -> MultiLeader {
|
||||
ml.arrowhead_size = 2.5;
|
||||
ml.dogleg_length = 2.5;
|
||||
|
||||
// Direction: from last leader pt toward content
|
||||
if let (Some(last_leader), Some(root)) =
|
||||
(leader_pts.last(), ml.context.leader_roots.first_mut())
|
||||
{
|
||||
let dx = (content_pt.x - last_leader.x) as f64;
|
||||
let dy = (content_pt.y - last_leader.y) as f64;
|
||||
let len = (dx * dx + dy * dy).sqrt().max(1e-9);
|
||||
root.direction = Vector3::new(dx / len, dy / len, 0.0);
|
||||
const DOGLEG: f64 = 2.5;
|
||||
// Which side of the leader the text sits on, from the last leader
|
||||
// segment's horizontal direction. The landing is always horizontal.
|
||||
let last_leader = leader_pts.last().copied().unwrap_or(content_pt);
|
||||
let to_right = (content_pt.x - last_leader.x) >= 0.0;
|
||||
let sign = if to_right { 1.0 } else { -1.0 };
|
||||
|
||||
if let Some(root) = ml.context.leader_roots.first_mut() {
|
||||
// Leader ends at the clicked point; a horizontal landing runs from
|
||||
// there toward the text.
|
||||
root.direction = Vector3::new(sign, 0.0, 0.0);
|
||||
root.connection_point = content_v3;
|
||||
root.landing_distance = 2.5;
|
||||
root.landing_distance = DOGLEG;
|
||||
}
|
||||
|
||||
// Seed the text one landing-length past the leader end, on the side the
|
||||
// user dragged toward. The actual horizontal alignment and the landing are
|
||||
// recomputed every frame from the text grip's position relative to the
|
||||
// leader, so moving the arrow or the text re-mirrors the layout.
|
||||
ml.context.text_location =
|
||||
Vector3::new(content_v3.x + sign * DOGLEG, content_v3.y, content_v3.z);
|
||||
|
||||
ml
|
||||
}
|
||||
|
||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
let mut points: Vec<[f32; 3]> = pts.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
if pts.len() >= 2 {
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.0);
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.5);
|
||||
points.push([f32::NAN; 3]);
|
||||
points.push([w1.x, w1.y, w1.z]);
|
||||
points.push([pts[0].x, pts[0].y, pts[0].z]);
|
||||
|
||||
Reference in new issue
Block a user