forked from 1b-agent/opencadstudio-upstream
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.
219 lines
7.0 KiB
Rust
219 lines
7.0 KiB
Rust
use acadrust::entities::Circle;
|
|
use glam::Vec3;
|
|
use truck_modeling::{builder, Point3, Wire};
|
|
|
|
use crate::command::EntityTransform;
|
|
use crate::entities::common::{
|
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
|
};
|
|
use crate::entities::traits::TruckConvertible;
|
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
|
use crate::scene::wire_model::{SnapHint, TangentGeom};
|
|
|
|
fn to_truck(circle: &Circle) -> TruckEntity {
|
|
let cx = circle.center.x;
|
|
let cy = circle.center.y;
|
|
let cz = circle.center.z;
|
|
let r = circle.radius;
|
|
let normal = (circle.normal.x, circle.normal.y, circle.normal.z);
|
|
|
|
let (ax, ay) = crate::scene::transform::ocs_axes(normal);
|
|
let (cwx, cwy, cwz) = crate::scene::transform::ocs_point_to_wcs((cx, cy, cz), normal);
|
|
|
|
// Circle points in WCS: centre_wcs ± r * Ax and centre_wcs ± r * Ay
|
|
let pt = |da: (f64, f64, f64), db: (f64, f64, f64), s: f64| {
|
|
Point3::new(
|
|
cwx + s * da.0 + s * db.0,
|
|
cwy + s * da.1 + s * db.1,
|
|
cwz + s * da.2 + s * db.2,
|
|
)
|
|
};
|
|
// right: centre + r*Ax, left: centre - r*Ax, top: centre + r*Ay, bot: centre - r*Ay
|
|
let p_right = pt(ax, (0.0, 0.0, 0.0), r);
|
|
let p_left = pt((ax.0 * -1.0, ax.1 * -1.0, ax.2 * -1.0), (0.0, 0.0, 0.0), r);
|
|
let p_top = pt(ay, (0.0, 0.0, 0.0), r);
|
|
let p_bot = pt((ay.0 * -1.0, ay.1 * -1.0, ay.2 * -1.0), (0.0, 0.0, 0.0), r);
|
|
|
|
let cv = Vec3::new(cwx as f32, cwy as f32, cwz as f32);
|
|
let rf = r as f32;
|
|
let q = |d: (f64, f64, f64)| {
|
|
Vec3::new(
|
|
(cwx + r * d.0) as f32,
|
|
(cwy + r * d.1) as f32,
|
|
(cwz + r * d.2) as f32,
|
|
)
|
|
};
|
|
let snap_pts = vec![
|
|
(cv, SnapHint::Center),
|
|
(q(ax), SnapHint::Quadrant),
|
|
(q(ay), SnapHint::Quadrant),
|
|
(q((-ax.0, -ax.1, -ax.2)), SnapHint::Quadrant),
|
|
(q((-ay.0, -ay.1, -ay.2)), SnapHint::Quadrant),
|
|
];
|
|
let tangent = TangentGeom::Circle {
|
|
center: [cwx as f32, cwy as f32, cwz as f32],
|
|
radius: rf,
|
|
};
|
|
|
|
if circle.thickness.abs() > 1e-10 {
|
|
let t = circle.thickness;
|
|
let (nx, ny, nz) = normal;
|
|
let n = 64usize;
|
|
let tau = std::f64::consts::TAU;
|
|
let circ_pt = |a: f64| -> (f64, f64, f64) {
|
|
let (c, s) = (a.cos(), a.sin());
|
|
(
|
|
cwx + r * (c * ax.0 + s * ay.0),
|
|
cwy + r * (c * ax.1 + s * ay.1),
|
|
cwz + r * (c * ax.2 + s * ay.2),
|
|
)
|
|
};
|
|
let mut pts: Vec<[f64; 3]> = Vec::with_capacity((n + 1) * 2 + 4 * 3);
|
|
for i in 0..=n {
|
|
let (x, y, z) = circ_pt(i as f64 * tau / n as f64);
|
|
pts.push([x, y, z]);
|
|
}
|
|
pts.push([f64::NAN; 3]);
|
|
for i in 0..=n {
|
|
let (x, y, z) = circ_pt(i as f64 * tau / n as f64);
|
|
pts.push([x + t * nx, y + t * ny, z + t * nz]);
|
|
}
|
|
pts.push([f64::NAN; 3]);
|
|
for i in 0..4usize {
|
|
let (x, y, z) = circ_pt(i as f64 * std::f64::consts::FRAC_PI_2);
|
|
pts.push([x, y, z]);
|
|
pts.push([x + t * nx, y + t * ny, z + t * nz]);
|
|
if i < 3 {
|
|
pts.push([f64::NAN; 3]);
|
|
}
|
|
}
|
|
return TruckEntity {
|
|
object: TruckObject::Lines(pts),
|
|
snap_pts,
|
|
tangent_geoms: vec![tangent],
|
|
key_vertices: vec![],
|
|
fill_tris: vec![],
|
|
};
|
|
}
|
|
|
|
let right = builder::vertex(p_right);
|
|
let left = builder::vertex(p_left);
|
|
let upper = builder::circle_arc(&right, &left, p_top);
|
|
let lower = builder::circle_arc(&left, &right, p_bot);
|
|
let wire: Wire = [upper, lower].into_iter().collect();
|
|
TruckEntity {
|
|
object: TruckObject::Contour(wire),
|
|
snap_pts,
|
|
tangent_geoms: vec![tangent],
|
|
key_vertices: vec![],
|
|
fill_tris: vec![],
|
|
}
|
|
}
|
|
|
|
fn grips(circle: &Circle) -> Vec<GripDef> {
|
|
let ctr = Vec3::new(
|
|
circle.center.x as f32,
|
|
circle.center.y as f32,
|
|
circle.center.z as f32,
|
|
);
|
|
let r = circle.radius as f32;
|
|
vec![
|
|
center_grip(0, ctr),
|
|
square_grip(1, ctr + Vec3::new(r, 0.0, 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(4, ctr - Vec3::new(0.0, r, 0.0)),
|
|
]
|
|
}
|
|
|
|
fn properties(circle: &Circle) -> PropSection {
|
|
PropSection {
|
|
title: "Geometry".into(),
|
|
props: vec![
|
|
edit("Center X", "center_x", circle.center.x),
|
|
edit("Center Y", "center_y", circle.center.y),
|
|
edit("Center Z", "center_z", circle.center.z),
|
|
edit("Radius", "radius", circle.radius),
|
|
ro(
|
|
"Diameter",
|
|
"diameter",
|
|
format!("{:.4}", circle.radius * 2.0),
|
|
),
|
|
ro(
|
|
"Circumference",
|
|
"circumference",
|
|
format!("{:.4}", circle.radius * 2.0 * std::f64::consts::PI),
|
|
),
|
|
],
|
|
}
|
|
}
|
|
|
|
fn apply_geom_prop(circle: &mut Circle, field: &str, value: &str) {
|
|
let Some(v) = parse_f64(value) else {
|
|
return;
|
|
};
|
|
match field {
|
|
"center_x" => circle.center.x = v,
|
|
"center_y" => circle.center.y = v,
|
|
"center_z" => circle.center.z = v,
|
|
"radius" if v > 0.0 => circle.radius = v,
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn apply_grip(circle: &mut Circle, grip_id: usize, apply: GripApply) {
|
|
match (grip_id, apply) {
|
|
(0, GripApply::Absolute(p)) => {
|
|
circle.center.x = p.x as f64;
|
|
circle.center.y = p.y as f64;
|
|
circle.center.z = p.z as f64;
|
|
}
|
|
(0, GripApply::Translate(d)) => {
|
|
circle.center.x += d.x as f64;
|
|
circle.center.y += d.y as f64;
|
|
circle.center.z += d.z as f64;
|
|
}
|
|
(1..=4, GripApply::Absolute(p)) => {
|
|
let cx = circle.center.x as f32;
|
|
let cy = circle.center.y as f32;
|
|
let dx = p.x - cx;
|
|
let dy = p.y - cy;
|
|
circle.radius = ((dx * dx + dy * dy) as f64).sqrt();
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn apply_transform(circle: &mut Circle, t: &EntityTransform) {
|
|
crate::scene::transform::apply_standard_entity_transform(circle, t, |entity, p1, p2| {
|
|
crate::scene::transform::reflect_xy_point(
|
|
&mut entity.center.x,
|
|
&mut entity.center.y,
|
|
p1,
|
|
p2,
|
|
);
|
|
});
|
|
}
|
|
|
|
impl TruckConvertible for Circle {
|
|
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
|
Some(to_truck(self))
|
|
}
|
|
}
|
|
|
|
crate::impl_entity_basics!(Circle);
|
|
|
|
impl crate::entities::traits::MassPropsCalc for Circle {
|
|
fn mass_props(&self) -> crate::entities::traits::MassProps {
|
|
use std::f64::consts::{PI, TAU};
|
|
let r = self.radius;
|
|
crate::entities::traits::MassProps {
|
|
area: PI * r * r,
|
|
perimeter: TAU * r,
|
|
cx: self.center.x,
|
|
cy: self.center.y,
|
|
}
|
|
}
|
|
}
|