refactor(grip): square-only marker set + oriented stretch handles
Phase 1 of bringing the grip vocabulary in line with the standard CAD convention: drop the per-position-type shape encoding (Diamond for translate, Circle for parameter) and use a plain square for every endpoint / vertex / centre grip. The behavioural midpoint vs vertex distinction stays — it lives on `GripDef::is_midpoint` and drives `Translate` vs `Absolute` in the grip-edit dispatcher — only the marker shape collapses. - `GripShape` keeps `Square`, `Rectangle`, `Triangle`. Diamond / Circle are gone. - `GripDef` gains `dir: Option<[f32; 2]>` — a world-XY direction vector consumed by `Rectangle` to orient the box along its segment. - `entities::common` exposes `square_grip`, `center_grip` (same square marker, flagged as translate), `rectangle_grip(id, world, dir)` for oriented mid-segment handles, and a kept-for-Phase-2 `triangle_grip`. - `overlay::GripMarker` / `grips_to_screen[_paper]` thread `dir` through to the canvas painter; the Rectangle path rotates the box around the grip centre using the world-XY direction (flipping the sin for screen-Y). Every existing `diamond_grip(id, w)` call becomes `center_grip(id, w)` — the visual change is square-replacing-diamond at line midpoints / arc midpoints / circle centres / ellipse centres / viewport / hatch loop centroids / dimension grips / image corners / multileader / ray / underlay / ole2frame / solid3d. `LwPolyline` grows a stretch handle on every segment (straight or arc), drawn as the new oriented rectangle. The grip-edit path translates both segment endpoints for straight segments and adjusts the arc bulge from the new midpoint for arc segments.
This commit is contained in:
parent
1e33a9049c
commit
5755c2e130
20 changed files with 184 additions and 133 deletions
|
|
@ -26,6 +26,10 @@ pub struct GripMarker {
|
|||
pub shape: GripShape,
|
||||
/// True → grip is currently being dragged (drawn filled red).
|
||||
pub is_hot: bool,
|
||||
/// World-XY direction vector — only consumed by the `Rectangle`
|
||||
/// shape to orient the box along its segment. `None` for grips
|
||||
/// that don't need rotation.
|
||||
pub dir: Option<[f32; 2]>,
|
||||
}
|
||||
|
||||
// ── Grid display params ───────────────────────────────────────────────────
|
||||
|
|
@ -424,29 +428,38 @@ impl canvas::Program<Message> for SelectionCanvas {
|
|||
Size::new(h * 2.0, h * 2.0),
|
||||
),
|
||||
GripShape::Rectangle => {
|
||||
// Wider-than-tall box — direction-aware mid-segment
|
||||
// stretch handle.
|
||||
let w = h * 1.4;
|
||||
let t = h * 0.7;
|
||||
canvas::Path::rectangle(
|
||||
Point::new(sp.x - w, sp.y - t),
|
||||
Size::new(w * 2.0, t * 2.0),
|
||||
)
|
||||
// Mid-segment stretch handle: small box, longer along
|
||||
// the segment direction so the affordance reads as
|
||||
// "stretch perpendicular to the segment". `dir` is a
|
||||
// world-XY direction vector; project it onto the
|
||||
// screen-X / screen-Y axes implied by the grip's
|
||||
// 2-D screen position to compute the in-plane angle.
|
||||
let half_long = h * 1.4;
|
||||
let half_short = h * 0.7;
|
||||
let (cos_t, sin_t) = match grip.dir {
|
||||
Some([dx, dy]) if (dx * dx + dy * dy) > 1e-12 => {
|
||||
let n = (dx * dx + dy * dy).sqrt();
|
||||
// Screen Y is inverted vs world Y → flip sin.
|
||||
(dx / n, -dy / n)
|
||||
}
|
||||
_ => (1.0, 0.0),
|
||||
};
|
||||
let ax = (cos_t * half_long, sin_t * half_long);
|
||||
let ay = (-sin_t * half_short, cos_t * half_short);
|
||||
canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(sp.x + ax.0 + ay.0, sp.y + ax.1 + ay.1));
|
||||
b.line_to(Point::new(sp.x + ax.0 - ay.0, sp.y + ax.1 - ay.1));
|
||||
b.line_to(Point::new(sp.x - ax.0 - ay.0, sp.y - ax.1 - ay.1));
|
||||
b.line_to(Point::new(sp.x - ax.0 + ay.0, sp.y - ax.1 + ay.1));
|
||||
b.close();
|
||||
})
|
||||
}
|
||||
GripShape::Diamond => canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(sp.x, sp.y - h));
|
||||
b.line_to(Point::new(sp.x + h, sp.y));
|
||||
b.line_to(Point::new(sp.x, sp.y + h));
|
||||
b.line_to(Point::new(sp.x - h, sp.y));
|
||||
b.close();
|
||||
}),
|
||||
GripShape::Triangle => canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(sp.x, sp.y - h));
|
||||
b.line_to(Point::new(sp.x + h, sp.y + h));
|
||||
b.line_to(Point::new(sp.x - h, sp.y + h));
|
||||
b.close();
|
||||
}),
|
||||
GripShape::Circle => canvas::Path::circle(sp, h),
|
||||
};
|
||||
|
||||
if grip.is_hot {
|
||||
|
|
|
|||
Loading…
Reference in a new issue