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
|
|
@ -299,7 +299,7 @@ impl OpenCADStudio {
|
||||||
};
|
};
|
||||||
screen_grips
|
screen_grips
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(_, screen, _, _)| {
|
.filter(|(_, screen, _, _, _)| {
|
||||||
screen.x.is_finite()
|
screen.x.is_finite()
|
||||||
&& screen.y.is_finite()
|
&& screen.y.is_finite()
|
||||||
&& screen.x >= -bounds.width
|
&& screen.x >= -bounds.width
|
||||||
|
|
@ -307,7 +307,7 @@ impl OpenCADStudio {
|
||||||
&& screen.y >= -bounds.height
|
&& screen.y >= -bounds.height
|
||||||
&& screen.y <= bounds.height * 2.0
|
&& screen.y <= bounds.height * 2.0
|
||||||
})
|
})
|
||||||
.map(|(grip_id, screen, _is_midpoint, shape)| {
|
.map(|(grip_id, screen, _is_midpoint, shape, dir)| {
|
||||||
let is_hot = tab
|
let is_hot = tab
|
||||||
.active_grip
|
.active_grip
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -316,6 +316,7 @@ impl OpenCADStudio {
|
||||||
pos: screen,
|
pos: screen,
|
||||||
shape,
|
shape,
|
||||||
is_hot,
|
is_hot,
|
||||||
|
dir,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use glam::Vec3;
|
||||||
use truck_modeling::{builder, Point3};
|
use truck_modeling::{builder, Point3};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, parse_f64, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, square_grip};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -118,10 +118,10 @@ fn grips(arc: &Arc) -> Vec<GripDef> {
|
||||||
let ea = arc.end_angle as f32;
|
let ea = arc.end_angle as f32;
|
||||||
let ma = sa + angle_span(sa, ea) * 0.5;
|
let ma = sa + angle_span(sa, ea) * 0.5;
|
||||||
vec![
|
vec![
|
||||||
diamond_grip(0, ctr),
|
center_grip(0, ctr),
|
||||||
square_grip(1, ctr + Vec3::new(r * sa.cos(), r * sa.sin(), 0.0)),
|
square_grip(1, ctr + Vec3::new(r * sa.cos(), r * sa.sin(), 0.0)),
|
||||||
square_grip(2, ctr + Vec3::new(r * ea.cos(), r * ea.sin(), 0.0)),
|
square_grip(2, ctr + Vec3::new(r * ea.cos(), r * ea.sin(), 0.0)),
|
||||||
diamond_grip(3, ctr + Vec3::new(r * ma.cos(), r * ma.sin(), 0.0)),
|
center_grip(3, ctr + Vec3::new(r * ma.cos(), r * ma.sin(), 0.0)),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use truck_modeling::{builder, Point3, Wire};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{
|
use crate::entities::common::{
|
||||||
diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||||
};
|
};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
|
|
@ -119,7 +119,7 @@ fn grips(circle: &Circle) -> Vec<GripDef> {
|
||||||
);
|
);
|
||||||
let r = circle.radius as f32;
|
let r = circle.radius as f32;
|
||||||
vec![
|
vec![
|
||||||
diamond_grip(0, ctr),
|
center_grip(0, ctr),
|
||||||
square_grip(1, ctr + Vec3::new(r, 0.0, 0.0)),
|
square_grip(1, ctr + Vec3::new(r, 0.0, 0.0)),
|
||||||
square_grip(2, ctr + Vec3::new(0.0, r, 0.0)),
|
square_grip(2, ctr + Vec3::new(0.0, r, 0.0)),
|
||||||
square_grip(3, ctr - Vec3::new(r, 0.0, 0.0)),
|
square_grip(3, ctr - Vec3::new(r, 0.0, 0.0)),
|
||||||
|
|
|
||||||
|
|
@ -127,44 +127,43 @@ pub fn square_grip(id: usize, world: Vec3) -> GripDef {
|
||||||
world,
|
world,
|
||||||
is_midpoint: false,
|
is_midpoint: false,
|
||||||
shape: GripShape::Square,
|
shape: GripShape::Square,
|
||||||
|
dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diamond_grip(id: usize, world: Vec3) -> GripDef {
|
/// Centre / translate grip — same square marker as a vertex grip but
|
||||||
|
/// flagged as a "whole-object move" handle for the grip-edit code.
|
||||||
|
pub fn center_grip(id: usize, world: Vec3) -> GripDef {
|
||||||
GripDef {
|
GripDef {
|
||||||
id,
|
id,
|
||||||
world,
|
world,
|
||||||
is_midpoint: true,
|
is_midpoint: true,
|
||||||
shape: GripShape::Diamond,
|
shape: GripShape::Square,
|
||||||
|
dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mid-segment stretch grip oriented along `dir` (the segment's in-plane
|
||||||
|
/// world-XY direction). Drawn as a small rectangle elongated along the
|
||||||
|
/// segment so the affordance reads as "stretch perpendicular".
|
||||||
|
pub fn rectangle_grip(id: usize, world: Vec3, dir: [f32; 2]) -> GripDef {
|
||||||
|
GripDef {
|
||||||
|
id,
|
||||||
|
world,
|
||||||
|
is_midpoint: true,
|
||||||
|
shape: GripShape::Rectangle,
|
||||||
|
dir: Some(dir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn triangle_grip(id: usize, world: Vec3) -> GripDef {
|
pub fn triangle_grip(id: usize, world: Vec3) -> GripDef {
|
||||||
GripDef {
|
GripDef {
|
||||||
id,
|
id,
|
||||||
world,
|
world,
|
||||||
is_midpoint: false,
|
is_midpoint: false,
|
||||||
shape: GripShape::Triangle,
|
shape: GripShape::Triangle,
|
||||||
}
|
dir: None,
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn rectangle_grip(id: usize, world: Vec3) -> GripDef {
|
|
||||||
GripDef {
|
|
||||||
id,
|
|
||||||
world,
|
|
||||||
is_midpoint: true,
|
|
||||||
shape: GripShape::Rectangle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn circle_grip(id: usize, world: Vec3) -> GripDef {
|
|
||||||
GripDef {
|
|
||||||
id,
|
|
||||||
world,
|
|
||||||
is_midpoint: false,
|
|
||||||
shape: GripShape::Circle,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{
|
use crate::entities::common::{
|
||||||
diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||||
};
|
};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -594,45 +594,45 @@ impl Grippable for Dimension {
|
||||||
match self {
|
match self {
|
||||||
Dimension::Linear(d) => vec![
|
Dimension::Linear(d) => vec![
|
||||||
square_grip(0, v3(&d.first_point)),
|
square_grip(0, v3(&d.first_point)),
|
||||||
diamond_grip(1, v3(&d.second_point)),
|
center_grip(1, v3(&d.second_point)),
|
||||||
diamond_grip(2, v3(&d.definition_point)),
|
center_grip(2, v3(&d.definition_point)),
|
||||||
diamond_grip(3, text),
|
center_grip(3, text),
|
||||||
],
|
],
|
||||||
Dimension::Aligned(d) => vec![
|
Dimension::Aligned(d) => vec![
|
||||||
square_grip(0, v3(&d.first_point)),
|
square_grip(0, v3(&d.first_point)),
|
||||||
diamond_grip(1, v3(&d.second_point)),
|
center_grip(1, v3(&d.second_point)),
|
||||||
diamond_grip(2, v3(&d.definition_point)),
|
center_grip(2, v3(&d.definition_point)),
|
||||||
diamond_grip(3, text),
|
center_grip(3, text),
|
||||||
],
|
],
|
||||||
Dimension::Radius(d) => vec![
|
Dimension::Radius(d) => vec![
|
||||||
square_grip(0, v3(&d.angle_vertex)),
|
square_grip(0, v3(&d.angle_vertex)),
|
||||||
diamond_grip(1, v3(&d.definition_point)),
|
center_grip(1, v3(&d.definition_point)),
|
||||||
diamond_grip(2, text),
|
center_grip(2, text),
|
||||||
],
|
],
|
||||||
Dimension::Diameter(d) => vec![
|
Dimension::Diameter(d) => vec![
|
||||||
square_grip(0, v3(&d.angle_vertex)),
|
square_grip(0, v3(&d.angle_vertex)),
|
||||||
diamond_grip(1, v3(&d.definition_point)),
|
center_grip(1, v3(&d.definition_point)),
|
||||||
diamond_grip(2, text),
|
center_grip(2, text),
|
||||||
],
|
],
|
||||||
Dimension::Angular2Ln(d) => vec![
|
Dimension::Angular2Ln(d) => vec![
|
||||||
square_grip(0, v3(&d.angle_vertex)),
|
square_grip(0, v3(&d.angle_vertex)),
|
||||||
diamond_grip(1, v3(&d.first_point)),
|
center_grip(1, v3(&d.first_point)),
|
||||||
diamond_grip(2, v3(&d.second_point)),
|
center_grip(2, v3(&d.second_point)),
|
||||||
diamond_grip(3, v3(&d.definition_point)),
|
center_grip(3, v3(&d.definition_point)),
|
||||||
diamond_grip(4, text),
|
center_grip(4, text),
|
||||||
],
|
],
|
||||||
Dimension::Angular3Pt(d) => vec![
|
Dimension::Angular3Pt(d) => vec![
|
||||||
square_grip(0, v3(&d.angle_vertex)),
|
square_grip(0, v3(&d.angle_vertex)),
|
||||||
diamond_grip(1, v3(&d.first_point)),
|
center_grip(1, v3(&d.first_point)),
|
||||||
diamond_grip(2, v3(&d.second_point)),
|
center_grip(2, v3(&d.second_point)),
|
||||||
diamond_grip(3, v3(&d.definition_point)),
|
center_grip(3, v3(&d.definition_point)),
|
||||||
diamond_grip(4, text),
|
center_grip(4, text),
|
||||||
],
|
],
|
||||||
Dimension::Ordinate(d) => vec![
|
Dimension::Ordinate(d) => vec![
|
||||||
square_grip(0, v3(&d.definition_point)),
|
square_grip(0, v3(&d.definition_point)),
|
||||||
diamond_grip(1, v3(&d.feature_location)),
|
center_grip(1, v3(&d.feature_location)),
|
||||||
diamond_grip(2, v3(&d.leader_endpoint)),
|
center_grip(2, v3(&d.leader_endpoint)),
|
||||||
diamond_grip(3, text),
|
center_grip(3, text),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use glam::Vec3;
|
||||||
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
|
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -164,7 +164,7 @@ fn grips(ell: &Ellipse) -> Vec<GripDef> {
|
||||||
ell.center.z as f32,
|
ell.center.z as f32,
|
||||||
);
|
);
|
||||||
vec![
|
vec![
|
||||||
diamond_grip(0, ctr),
|
center_grip(0, ctr),
|
||||||
square_grip(1, maj),
|
square_grip(1, maj),
|
||||||
square_grip(2, min),
|
square_grip(2, min),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use acadrust::entities::{BoundaryEdge, Hatch};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro};
|
use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, ro_prop as ro};
|
||||||
use crate::entities::traits::{Grippable, FallbackTess, PropertyEditable, Transformable};
|
use crate::entities::traits::{Grippable, FallbackTess, PropertyEditable, Transformable};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
use crate::scene::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry};
|
use crate::scene::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry};
|
||||||
|
|
@ -182,31 +182,31 @@ impl Grippable for Hatch {
|
||||||
match edge {
|
match edge {
|
||||||
BoundaryEdge::Polyline(p) => {
|
BoundaryEdge::Polyline(p) => {
|
||||||
for v in &p.vertices {
|
for v in &p.vertices {
|
||||||
out.push(diamond_grip(id, Vec3::new(v.x as f32, v.y as f32, elev)));
|
out.push(center_grip(id, Vec3::new(v.x as f32, v.y as f32, elev)));
|
||||||
id += 1;
|
id += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundaryEdge::Line(l) => {
|
BoundaryEdge::Line(l) => {
|
||||||
out.push(diamond_grip(
|
out.push(center_grip(
|
||||||
id,
|
id,
|
||||||
Vec3::new(l.start.x as f32, l.start.y as f32, elev),
|
Vec3::new(l.start.x as f32, l.start.y as f32, elev),
|
||||||
));
|
));
|
||||||
id += 1;
|
id += 1;
|
||||||
out.push(diamond_grip(
|
out.push(center_grip(
|
||||||
id,
|
id,
|
||||||
Vec3::new(l.end.x as f32, l.end.y as f32, elev),
|
Vec3::new(l.end.x as f32, l.end.y as f32, elev),
|
||||||
));
|
));
|
||||||
id += 1;
|
id += 1;
|
||||||
}
|
}
|
||||||
BoundaryEdge::CircularArc(a) => {
|
BoundaryEdge::CircularArc(a) => {
|
||||||
out.push(diamond_grip(
|
out.push(center_grip(
|
||||||
id,
|
id,
|
||||||
Vec3::new(a.center.x as f32, a.center.y as f32, elev),
|
Vec3::new(a.center.x as f32, a.center.y as f32, elev),
|
||||||
));
|
));
|
||||||
id += 1;
|
id += 1;
|
||||||
}
|
}
|
||||||
BoundaryEdge::EllipticArc(e) => {
|
BoundaryEdge::EllipticArc(e) => {
|
||||||
out.push(diamond_grip(
|
out.push(center_grip(
|
||||||
id,
|
id,
|
||||||
Vec3::new(e.center.x as f32, e.center.y as f32, elev),
|
Vec3::new(e.center.x as f32, e.center.y as f32, elev),
|
||||||
));
|
));
|
||||||
|
|
@ -219,7 +219,7 @@ impl Grippable for Hatch {
|
||||||
s.control_points.iter().map(|p| [p.x, p.y]).collect()
|
s.control_points.iter().map(|p| [p.x, p.y]).collect()
|
||||||
};
|
};
|
||||||
for [x, y] in pts {
|
for [x, y] in pts {
|
||||||
out.push(diamond_grip(id, Vec3::new(x as f32, y as f32, elev)));
|
out.push(center_grip(id, Vec3::new(x as f32, y as f32, elev)));
|
||||||
id += 1;
|
id += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use acadrust::Entity;
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{TruckConvertible};
|
use crate::entities::traits::{TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
|
|
@ -98,7 +98,7 @@ fn grips(leader: &Leader) -> Vec<GripDef> {
|
||||||
let sum = leader.vertices.iter().fold(Vec3::ZERO, |acc, v| {
|
let sum = leader.vertices.iter().fold(Vec3::ZERO, |acc, v| {
|
||||||
acc + Vec3::new(v.x as f32, v.y as f32, v.z as f32)
|
acc + Vec3::new(v.x as f32, v.y as f32, v.z as f32)
|
||||||
});
|
});
|
||||||
grips.push(diamond_grip(n, sum / n as f32));
|
grips.push(center_grip(n, sum / n as f32));
|
||||||
}
|
}
|
||||||
|
|
||||||
grips
|
grips
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use truck_modeling::{builder, Point3};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{
|
use crate::entities::common::{
|
||||||
diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||||
};
|
};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
|
|
@ -74,7 +74,7 @@ fn grips(line: &Line) -> Vec<GripDef> {
|
||||||
);
|
);
|
||||||
let e = Vec3::new(line.end.x as f32, line.end.y as f32, line.end.z as f32);
|
let e = Vec3::new(line.end.x as f32, line.end.y as f32, line.end.z as f32);
|
||||||
let m = (s + e) * 0.5;
|
let m = (s + e) * 0.5;
|
||||||
vec![square_grip(0, s), square_grip(1, e), diamond_grip(2, m)]
|
vec![square_grip(0, s), square_grip(1, e), center_grip(2, m)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(line: &Line) -> PropSection {
|
fn properties(line: &Line) -> PropSection {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use truck_modeling::{builder, Edge, Point3, Wire};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{
|
use crate::entities::common::{
|
||||||
diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
edit_prop as edit, parse_f64, rectangle_grip, ro_prop as ro, square_grip,
|
||||||
};
|
};
|
||||||
use crate::entities::traits::{TruckConvertible};
|
use crate::entities::traits::{TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
|
|
@ -295,21 +295,31 @@ fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
||||||
.map(|(i, v)| square_grip(i, Vec3::new(v.location.x as f32, v.location.y as f32, elev)))
|
.map(|(i, v)| square_grip(i, Vec3::new(v.location.x as f32, v.location.y as f32, elev)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Diamond midpoint grip for each arc segment
|
// One mid-segment stretch grip per segment (straight or arc). The
|
||||||
|
// marker is a small box rotated along the chord direction so the
|
||||||
|
// shape itself signals which way the segment runs.
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
let v0 = &pline.vertices[i];
|
let v0 = &pline.vertices[i];
|
||||||
if v0.bulge.abs() < 1e-9 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let v1 = &pline.vertices[(i + 1) % n];
|
let v1 = &pline.vertices[(i + 1) % n];
|
||||||
let mid = arc_midpoint(
|
let (mx, my) = if v0.bulge.abs() < 1e-9 {
|
||||||
|
(
|
||||||
|
(v0.location.x + v1.location.x) * 0.5,
|
||||||
|
(v0.location.y + v1.location.y) * 0.5,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let m = arc_midpoint(
|
||||||
[v0.location.x, v0.location.y],
|
[v0.location.x, v0.location.y],
|
||||||
[v1.location.x, v1.location.y],
|
[v1.location.x, v1.location.y],
|
||||||
v0.bulge,
|
v0.bulge,
|
||||||
);
|
);
|
||||||
out.push(diamond_grip(
|
(m[0], m[1])
|
||||||
|
};
|
||||||
|
let dx = (v1.location.x - v0.location.x) as f32;
|
||||||
|
let dy = (v1.location.y - v0.location.y) as f32;
|
||||||
|
out.push(rectangle_grip(
|
||||||
n + i,
|
n + i,
|
||||||
Vec3::new(mid[0] as f32, mid[1] as f32, elev),
|
Vec3::new(mx as f32, my as f32, elev),
|
||||||
|
[dx, dy],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|
@ -355,7 +365,11 @@ fn apply_grip(pline: &mut LwPolyline, grip_id: usize, apply: GripApply) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Arc midpoint grip for segment (grip_id - n)
|
// Mid-segment stretch grip for segment (grip_id - n).
|
||||||
|
// Straight segments translate both endpoints by the drag delta
|
||||||
|
// (the shared vertices then carry along whichever adjacent
|
||||||
|
// segments share them). Arc segments adjust their bulge from
|
||||||
|
// the new midpoint position.
|
||||||
let seg = grip_id - n;
|
let seg = grip_id - n;
|
||||||
let count = if pline.is_closed {
|
let count = if pline.is_closed {
|
||||||
n
|
n
|
||||||
|
|
@ -365,11 +379,31 @@ fn apply_grip(pline: &mut LwPolyline, grip_id: usize, apply: GripApply) {
|
||||||
if seg >= count {
|
if seg >= count {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let i0 = seg;
|
||||||
|
let i1 = (seg + 1) % n;
|
||||||
|
let is_arc = pline.vertices[i0].bulge.abs() >= 1e-9;
|
||||||
|
if !is_arc {
|
||||||
|
let d = match apply {
|
||||||
|
GripApply::Translate(d) => [d.x as f64, d.y as f64],
|
||||||
|
GripApply::Absolute(p) => {
|
||||||
|
let old_mid = (
|
||||||
|
(pline.vertices[i0].location.x + pline.vertices[i1].location.x) * 0.5,
|
||||||
|
(pline.vertices[i0].location.y + pline.vertices[i1].location.y) * 0.5,
|
||||||
|
);
|
||||||
|
[p.x as f64 - old_mid.0, p.y as f64 - old_mid.1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pline.vertices[i0].location.x += d[0];
|
||||||
|
pline.vertices[i0].location.y += d[1];
|
||||||
|
pline.vertices[i1].location.x += d[0];
|
||||||
|
pline.vertices[i1].location.y += d[1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
let new_mid: [f64; 2] = match apply {
|
let new_mid: [f64; 2] = match apply {
|
||||||
GripApply::Absolute(p) => [p.x as f64, p.y as f64],
|
GripApply::Absolute(p) => [p.x as f64, p.y as f64],
|
||||||
GripApply::Translate(d) => {
|
GripApply::Translate(d) => {
|
||||||
let v0 = &pline.vertices[seg];
|
let v0 = &pline.vertices[i0];
|
||||||
let v1 = &pline.vertices[(seg + 1) % n];
|
let v1 = &pline.vertices[i1];
|
||||||
let old = arc_midpoint(
|
let old = arc_midpoint(
|
||||||
[v0.location.x, v0.location.y],
|
[v0.location.x, v0.location.y],
|
||||||
[v1.location.x, v1.location.y],
|
[v1.location.x, v1.location.y],
|
||||||
|
|
@ -379,15 +413,15 @@ fn apply_grip(pline: &mut LwPolyline, grip_id: usize, apply: GripApply) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let p0 = [
|
let p0 = [
|
||||||
pline.vertices[seg].location.x,
|
pline.vertices[i0].location.x,
|
||||||
pline.vertices[seg].location.y,
|
pline.vertices[i0].location.y,
|
||||||
];
|
];
|
||||||
let p1 = [
|
let p1 = [
|
||||||
pline.vertices[(seg + 1) % n].location.x,
|
pline.vertices[i1].location.x,
|
||||||
pline.vertices[(seg + 1) % n].location.y,
|
pline.vertices[i1].location.y,
|
||||||
];
|
];
|
||||||
if let Some(new_bulge) = bulge_from_midpoint(p0, p1, new_mid) {
|
if let Some(new_bulge) = bulge_from_midpoint(p0, p1, new_mid) {
|
||||||
pline.vertices[seg].bulge = new_bulge.clamp(-1e6, 1e6);
|
pline.vertices[i0].bulge = new_bulge.clamp(-1e6, 1e6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ pub(crate) fn mleader_v_anchor(attach: TextAttachmentType) -> MTextVAnchor {
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{TruckConvertible};
|
use crate::entities::traits::{TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
|
|
@ -310,7 +310,7 @@ fn grips(ml: &MultiLeader) -> Vec<GripDef> {
|
||||||
|
|
||||||
if ml.content_type == LeaderContentType::MText {
|
if ml.content_type == LeaderContentType::MText {
|
||||||
let tl = &ml.context.text_location;
|
let tl = &ml.context.text_location;
|
||||||
result.push(diamond_grip(
|
result.push(center_grip(
|
||||||
id,
|
id,
|
||||||
Vec3::new(tl.x as f32, tl.y as f32, tl.z as f32),
|
Vec3::new(tl.x as f32, tl.y as f32, tl.z as f32),
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use acadrust::entities::{Ole2Frame, OleObjectType};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{TruckConvertible};
|
use crate::entities::traits::{TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -70,7 +70,7 @@ fn grips(ole: &Ole2Frame) -> Vec<GripDef> {
|
||||||
vec![
|
vec![
|
||||||
square_grip(0, ul),
|
square_grip(0, ul),
|
||||||
square_grip(1, lr),
|
square_grip(1, lr),
|
||||||
diamond_grip(2, center),
|
center_grip(2, center),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use acadrust::entities::{RasterImage, Wipeout};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
|
|
@ -140,9 +140,9 @@ impl Grippable for RasterImage {
|
||||||
);
|
);
|
||||||
vec![
|
vec![
|
||||||
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
|
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
|
||||||
diamond_grip(1, Vec3::from(to_f32_3(corners[1]))),
|
center_grip(1, Vec3::from(to_f32_3(corners[1]))),
|
||||||
diamond_grip(2, Vec3::from(to_f32_3(corners[2]))),
|
center_grip(2, Vec3::from(to_f32_3(corners[2]))),
|
||||||
diamond_grip(3, Vec3::from(to_f32_3(corners[3]))),
|
center_grip(3, Vec3::from(to_f32_3(corners[3]))),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -346,7 +346,7 @@ impl Grippable for Wipeout {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
square_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
|
square_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
|
||||||
} else {
|
} else {
|
||||||
diamond_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
|
center_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
@ -360,9 +360,9 @@ impl Grippable for Wipeout {
|
||||||
);
|
);
|
||||||
vec![
|
vec![
|
||||||
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
|
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
|
||||||
diamond_grip(1, Vec3::from(to_f32_3(corners[1]))),
|
center_grip(1, Vec3::from(to_f32_3(corners[1]))),
|
||||||
diamond_grip(2, Vec3::from(to_f32_3(corners[2]))),
|
center_grip(2, Vec3::from(to_f32_3(corners[2]))),
|
||||||
diamond_grip(3, Vec3::from(to_f32_3(corners[3]))),
|
center_grip(3, Vec3::from(to_f32_3(corners[3]))),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use acadrust::entities::{Ray, XLine};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, square_grip};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -48,7 +48,7 @@ impl Grippable for Ray {
|
||||||
let guide_dist = 10.0_f64;
|
let guide_dist = 10.0_f64;
|
||||||
vec![
|
vec![
|
||||||
square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)),
|
square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)),
|
||||||
diamond_grip(
|
center_grip(
|
||||||
1,
|
1,
|
||||||
Vec3::new(
|
Vec3::new(
|
||||||
(bp.x + dir.x * guide_dist) as f32,
|
(bp.x + dir.x * guide_dist) as f32,
|
||||||
|
|
@ -186,7 +186,7 @@ impl Grippable for XLine {
|
||||||
let guide_dist = 10.0_f64;
|
let guide_dist = 10.0_f64;
|
||||||
vec![
|
vec![
|
||||||
square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)),
|
square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)),
|
||||||
diamond_grip(
|
center_grip(
|
||||||
1,
|
1,
|
||||||
Vec3::new(
|
Vec3::new(
|
||||||
(bp.x + dir.x * guide_dist) as f32,
|
(bp.x + dir.x * guide_dist) as f32,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
use acadrust::entities::{Body, Region, Solid3D};
|
use acadrust::entities::{Body, Region, Solid3D};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::entities::common::{diamond_grip, ro_prop as ro};
|
use crate::entities::common::{center_grip, ro_prop as ro};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable};
|
use crate::entities::traits::{Grippable, PropertyEditable};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ fn acis_size_str(has_data: bool, sat_len: usize, sab_len: usize, is_binary: bool
|
||||||
|
|
||||||
impl Grippable for Solid3D {
|
impl Grippable for Solid3D {
|
||||||
fn grips(&self) -> Vec<GripDef> {
|
fn grips(&self) -> Vec<GripDef> {
|
||||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
vec![center_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||||
|
|
@ -120,7 +120,7 @@ impl PropertyEditable for Solid3D {
|
||||||
|
|
||||||
impl Grippable for Region {
|
impl Grippable for Region {
|
||||||
fn grips(&self) -> Vec<GripDef> {
|
fn grips(&self) -> Vec<GripDef> {
|
||||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
vec![center_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||||
|
|
@ -188,7 +188,7 @@ impl PropertyEditable for Region {
|
||||||
|
|
||||||
impl Grippable for Body {
|
impl Grippable for Body {
|
||||||
fn grips(&self) -> Vec<GripDef> {
|
fn grips(&self) -> Vec<GripDef> {
|
||||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
vec![center_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use acadrust::entities::{Underlay, UnderlayDisplayFlags};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{diamond_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -87,7 +87,7 @@ impl Grippable for Underlay {
|
||||||
if !self.clip_boundary_vertices.is_empty() {
|
if !self.clip_boundary_vertices.is_empty() {
|
||||||
let world_verts = self.world_clip_boundary();
|
let world_verts = self.world_clip_boundary();
|
||||||
for (i, v) in world_verts.iter().enumerate() {
|
for (i, v) in world_verts.iter().enumerate() {
|
||||||
grips.push(diamond_grip(
|
grips.push(center_grip(
|
||||||
i + 1,
|
i + 1,
|
||||||
Vec3::new(v.x as f32, v.y as f32, v.z as f32),
|
Vec3::new(v.x as f32, v.y as f32, v.z as f32),
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{
|
use crate::entities::common::{
|
||||||
diamond_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
|
|
@ -97,7 +97,7 @@ fn grips(vp: &Viewport) -> Vec<GripDef> {
|
||||||
let hw = (vp.width / 2.0) as f32;
|
let hw = (vp.width / 2.0) as f32;
|
||||||
let hh = (vp.height / 2.0) as f32;
|
let hh = (vp.height / 2.0) as f32;
|
||||||
vec![
|
vec![
|
||||||
diamond_grip(0, Vec3::new(cx, cy, cz)),
|
center_grip(0, Vec3::new(cx, cy, cz)),
|
||||||
square_grip(1, Vec3::new(cx + hw, cy + hh, cz)),
|
square_grip(1, Vec3::new(cx + hw, cy + hh, cz)),
|
||||||
square_grip(2, Vec3::new(cx - hw, cy + hh, cz)),
|
square_grip(2, Vec3::new(cx - hw, cy + hh, cz)),
|
||||||
square_grip(3, Vec3::new(cx - hw, cy - hh, cz)),
|
square_grip(3, Vec3::new(cx - hw, cy - hh, cz)),
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ pub fn grips_to_screen(
|
||||||
grips: &[GripDef],
|
grips: &[GripDef],
|
||||||
view_proj: Mat4,
|
view_proj: Mat4,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
) -> Vec<(usize, Point, bool, GripShape)> {
|
) -> Vec<(usize, Point, bool, GripShape, Option<[f32; 2]>)> {
|
||||||
grips
|
grips
|
||||||
.iter()
|
.iter()
|
||||||
.map(|g| {
|
.map(|g| {
|
||||||
|
|
@ -45,7 +45,7 @@ pub fn grips_to_screen(
|
||||||
bounds.x + (ndc.x + 1.0) * 0.5 * bounds.width,
|
bounds.x + (ndc.x + 1.0) * 0.5 * bounds.width,
|
||||||
bounds.y + (1.0 - ndc.y) * 0.5 * bounds.height,
|
bounds.y + (1.0 - ndc.y) * 0.5 * bounds.height,
|
||||||
);
|
);
|
||||||
(g.id, screen, g.is_midpoint, g.shape)
|
(g.id, screen, g.is_midpoint, g.shape, g.dir)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ pub fn grips_to_screen_paper(
|
||||||
half_w: f32,
|
half_w: f32,
|
||||||
half_h: f32,
|
half_h: f32,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
) -> Vec<(usize, Point, bool, GripShape)> {
|
) -> Vec<(usize, Point, bool, GripShape, Option<[f32; 2]>)> {
|
||||||
grips
|
grips
|
||||||
.iter()
|
.iter()
|
||||||
.map(|g| {
|
.map(|g| {
|
||||||
|
|
@ -67,7 +67,7 @@ pub fn grips_to_screen_paper(
|
||||||
(g.world.x - tx + half_w) / (2.0 * half_w) * bounds.width,
|
(g.world.x - tx + half_w) / (2.0 * half_w) * bounds.width,
|
||||||
(ty + half_h - g.world.y) / (2.0 * half_h) * bounds.height,
|
(ty + half_h - g.world.y) / (2.0 * half_h) * bounds.height,
|
||||||
);
|
);
|
||||||
(g.id, screen, g.is_midpoint, g.shape)
|
(g.id, screen, g.is_midpoint, g.shape, g.dir)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,23 +51,23 @@ pub struct PropSection {
|
||||||
|
|
||||||
// ── Grip types ─────────────────────────────────────────────────────────────
|
// ── Grip types ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Visual marker shape for a grip point. The complete vocabulary used
|
/// Visual marker shape for a grip point. The complete vocabulary that
|
||||||
/// across entity types:
|
/// matches the standard CAD grip conventions:
|
||||||
/// * `Square` — vertex / endpoint that moves a single point.
|
/// * `Square` — endpoint / vertex / centre. Drag → moves a single
|
||||||
/// * `Rectangle` — direction-aware mid-segment stretch (polyline
|
/// point or translates the entity.
|
||||||
/// straight segments, dimension extension lines).
|
/// * `Rectangle` — direction-aware mid-segment stretch handle.
|
||||||
/// * `Diamond` — midpoint of a curve / centre of a closed shape (drags
|
/// Drawn as a small box rotated along `dir` (the in-plane segment
|
||||||
/// the whole shape or stretches the midpoint).
|
/// direction in world XY). Used for polyline / wipeout / image /
|
||||||
/// * `Triangle` — directional control (rotate / add vertex / continue).
|
/// dimension segment midpoints.
|
||||||
/// * `Circle` — parametric control (radius / dimension value).
|
/// * `Triangle` — directional indicator (Phase 2: dynamic-block
|
||||||
|
/// parameters, dimension reverse-arrow flips, multi-functional
|
||||||
|
/// hover popups).
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum GripShape {
|
pub enum GripShape {
|
||||||
Square,
|
Square,
|
||||||
Rectangle,
|
Rectangle,
|
||||||
Diamond,
|
|
||||||
Triangle,
|
Triangle,
|
||||||
Circle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes one grip point for an entity.
|
/// Describes one grip point for an entity.
|
||||||
|
|
@ -77,11 +77,15 @@ pub struct GripDef {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
/// World-space position of the grip.
|
/// World-space position of the grip.
|
||||||
pub world: Vec3,
|
pub world: Vec3,
|
||||||
/// `true` → midpoint grip (diamond, translates whole object).
|
/// `true` → midpoint / centre grip (drags the whole shape).
|
||||||
/// `false` → endpoint grip (square, moves a single vertex).
|
/// `false` → endpoint / vertex grip (moves a single point).
|
||||||
pub is_midpoint: bool,
|
pub is_midpoint: bool,
|
||||||
/// Visual marker shape for the grip.
|
/// Visual marker shape for the grip.
|
||||||
pub shape: GripShape,
|
pub shape: GripShape,
|
||||||
|
/// World-XY direction vector used to orient a `Rectangle` grip
|
||||||
|
/// along its segment. `None` for shapes that don't need rotation
|
||||||
|
/// (Square, Triangle in non-directional contexts).
|
||||||
|
pub dir: Option<[f32; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How to apply a grip drag result.
|
/// How to apply a grip drag result.
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ pub struct GripMarker {
|
||||||
pub shape: GripShape,
|
pub shape: GripShape,
|
||||||
/// True → grip is currently being dragged (drawn filled red).
|
/// True → grip is currently being dragged (drawn filled red).
|
||||||
pub is_hot: bool,
|
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 ───────────────────────────────────────────────────
|
// ── Grid display params ───────────────────────────────────────────────────
|
||||||
|
|
@ -424,29 +428,38 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||||
Size::new(h * 2.0, h * 2.0),
|
Size::new(h * 2.0, h * 2.0),
|
||||||
),
|
),
|
||||||
GripShape::Rectangle => {
|
GripShape::Rectangle => {
|
||||||
// Wider-than-tall box — direction-aware mid-segment
|
// Mid-segment stretch handle: small box, longer along
|
||||||
// stretch handle.
|
// the segment direction so the affordance reads as
|
||||||
let w = h * 1.4;
|
// "stretch perpendicular to the segment". `dir` is a
|
||||||
let t = h * 0.7;
|
// world-XY direction vector; project it onto the
|
||||||
canvas::Path::rectangle(
|
// screen-X / screen-Y axes implied by the grip's
|
||||||
Point::new(sp.x - w, sp.y - t),
|
// 2-D screen position to compute the in-plane angle.
|
||||||
Size::new(w * 2.0, t * 2.0),
|
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)
|
||||||
}
|
}
|
||||||
GripShape::Diamond => canvas::Path::new(|b| {
|
_ => (1.0, 0.0),
|
||||||
b.move_to(Point::new(sp.x, sp.y - h));
|
};
|
||||||
b.line_to(Point::new(sp.x + h, sp.y));
|
let ax = (cos_t * half_long, sin_t * half_long);
|
||||||
b.line_to(Point::new(sp.x, sp.y + h));
|
let ay = (-sin_t * half_short, cos_t * half_short);
|
||||||
b.line_to(Point::new(sp.x - h, sp.y));
|
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();
|
b.close();
|
||||||
}),
|
})
|
||||||
|
}
|
||||||
GripShape::Triangle => canvas::Path::new(|b| {
|
GripShape::Triangle => canvas::Path::new(|b| {
|
||||||
b.move_to(Point::new(sp.x, sp.y - h));
|
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.line_to(Point::new(sp.x - h, sp.y + h));
|
b.line_to(Point::new(sp.x - h, sp.y + h));
|
||||||
b.close();
|
b.close();
|
||||||
}),
|
}),
|
||||||
GripShape::Circle => canvas::Path::circle(sp, h),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if grip.is_hot {
|
if grip.is_hot {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue