forked from 1b-agent/opencadstudio-upstream
feat: implement Grippable and PropertyEditable traits for Solid3D, Region, and Body; add snap points and geometry properties
This commit is contained in:
1 parent
899b3f4796
commit
534e4ff26b
7 files changed
+247
-217
No files matched your search
@@ -4,6 +4,7 @@ use glam::Vec3;
|
||||
use crate::command::EntityTransform;
|
||||
use crate::entities::common::{ro_prop as ro, square_grip};
|
||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||
use crate::scene::wire_model::SnapHint;
|
||||
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||
|
||||
@@ -55,7 +56,12 @@ impl TruckConvertible for Face3D {
|
||||
|
||||
Some(TruckEntity {
|
||||
object: TruckObject::Lines(pts),
|
||||
snap_pts: vec![],
|
||||
snap_pts: vec![
|
||||
(Vec3::from(p0), SnapHint::Node),
|
||||
(Vec3::from(p1), SnapHint::Node),
|
||||
(Vec3::from(p2), SnapHint::Node),
|
||||
(Vec3::from(p3), SnapHint::Node),
|
||||
],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![p0, p1, p2, p3],
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ pub mod underlay;
|
||||
pub mod ray;
|
||||
pub mod shape;
|
||||
pub mod solid;
|
||||
pub mod solid3d;
|
||||
pub mod spline;
|
||||
pub mod text;
|
||||
pub(crate) mod text_support;
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
// Grippable + PropertyEditable for Solid3D, Region, Body.
|
||||
//
|
||||
// Geometry lives in ACIS data — we cannot edit it via the properties panel.
|
||||
// We expose the point_of_reference as a translate grip and show ACIS size
|
||||
// as read-only info. Grip translate also updates wire points so the wire
|
||||
// fallback stays in sync; the caller (scene/mod.rs apply_grip) translates
|
||||
// the MeshModel vertices to match.
|
||||
|
||||
use acadrust::entities::{Body, Region, Solid3D};
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::entities::common::{diamond_grip, ro_prop as ro};
|
||||
use crate::entities::traits::{Grippable, PropertyEditable};
|
||||
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||
|
||||
// ── shared helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
fn por_to_vec3(v: &acadrust::types::Vector3) -> Vec3 {
|
||||
Vec3::new(v.x as f32, v.y as f32, v.z as f32)
|
||||
}
|
||||
|
||||
fn translate_wires(wires: &mut Vec<acadrust::entities::Wire>, d: Vec3) {
|
||||
for wire in wires.iter_mut() {
|
||||
for pt in wire.points.iter_mut() {
|
||||
pt.x += d.x as f64;
|
||||
pt.y += d.y as f64;
|
||||
pt.z += d.z as f64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn acis_size_str(has_data: bool, sat_len: usize, sab_len: usize, is_binary: bool) -> String {
|
||||
if !has_data {
|
||||
return "none".to_string();
|
||||
}
|
||||
if is_binary {
|
||||
format!("{} bytes (SAB)", sab_len)
|
||||
} else {
|
||||
format!("{} bytes (SAT)", sat_len)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Solid3D ───────────────────────────────────────────────────────────────────
|
||||
|
||||
impl Grippable for Solid3D {
|
||||
fn grips(&self) -> Vec<GripDef> {
|
||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||
}
|
||||
|
||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||
if grip_id != 0 {
|
||||
return;
|
||||
}
|
||||
if let GripApply::Translate(d) = apply {
|
||||
self.point_of_reference.x += d.x as f64;
|
||||
self.point_of_reference.y += d.y as f64;
|
||||
self.point_of_reference.z += d.z as f64;
|
||||
translate_wires(&mut self.wires, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyEditable for Solid3D {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection {
|
||||
let size = acis_size_str(
|
||||
self.acis_data.has_data(),
|
||||
self.acis_data.sat_data.len(),
|
||||
self.acis_data.sab_data.len(),
|
||||
self.acis_data.is_binary,
|
||||
);
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Ref Pt X", "s3d_px", format!("{:.4}", self.point_of_reference.x)),
|
||||
ro("Ref Pt Y", "s3d_py", format!("{:.4}", self.point_of_reference.y)),
|
||||
ro("Ref Pt Z", "s3d_pz", format!("{:.4}", self.point_of_reference.z)),
|
||||
ro("ACIS Data", "s3d_acis", size),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
||||
}
|
||||
|
||||
// ── Region ────────────────────────────────────────────────────────────────────
|
||||
|
||||
impl Grippable for Region {
|
||||
fn grips(&self) -> Vec<GripDef> {
|
||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||
}
|
||||
|
||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||
if grip_id != 0 {
|
||||
return;
|
||||
}
|
||||
if let GripApply::Translate(d) = apply {
|
||||
self.point_of_reference.x += d.x as f64;
|
||||
self.point_of_reference.y += d.y as f64;
|
||||
self.point_of_reference.z += d.z as f64;
|
||||
translate_wires(&mut self.wires, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyEditable for Region {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection {
|
||||
let size = acis_size_str(
|
||||
self.acis_data.has_data(),
|
||||
self.acis_data.sat_data.len(),
|
||||
self.acis_data.sab_data.len(),
|
||||
self.acis_data.is_binary,
|
||||
);
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Ref Pt X", "rgn_px", format!("{:.4}", self.point_of_reference.x)),
|
||||
ro("Ref Pt Y", "rgn_py", format!("{:.4}", self.point_of_reference.y)),
|
||||
ro("Ref Pt Z", "rgn_pz", format!("{:.4}", self.point_of_reference.z)),
|
||||
ro("ACIS Data", "rgn_acis", size),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
||||
}
|
||||
|
||||
// ── Body ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
impl Grippable for Body {
|
||||
fn grips(&self) -> Vec<GripDef> {
|
||||
vec![diamond_grip(0, por_to_vec3(&self.point_of_reference))]
|
||||
}
|
||||
|
||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||
if grip_id != 0 {
|
||||
return;
|
||||
}
|
||||
if let GripApply::Translate(d) = apply {
|
||||
self.point_of_reference.x += d.x as f64;
|
||||
self.point_of_reference.y += d.y as f64;
|
||||
self.point_of_reference.z += d.z as f64;
|
||||
translate_wires(&mut self.wires, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyEditable for Body {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection {
|
||||
let size = acis_size_str(
|
||||
self.acis_data.has_data(),
|
||||
self.acis_data.sat_data.len(),
|
||||
self.acis_data.sab_data.len(),
|
||||
self.acis_data.is_binary,
|
||||
);
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Ref Pt X", "bdy_px", format!("{:.4}", self.point_of_reference.x)),
|
||||
ro("Ref Pt Y", "bdy_py", format!("{:.4}", self.point_of_reference.y)),
|
||||
ro("Ref Pt Z", "bdy_pz", format!("{:.4}", self.point_of_reference.z)),
|
||||
ro("ACIS Data", "bdy_acis", size),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
||||
}
|
||||
@@ -87,6 +87,9 @@ impl EntityTypeOps for EntityType {
|
||||
EntityType::MLine(ml) => Grippable::grips(ml),
|
||||
EntityType::Tolerance(tol) => Grippable::grips(tol),
|
||||
EntityType::Solid(solid) => Grippable::grips(solid),
|
||||
EntityType::Solid3D(s) => Grippable::grips(s),
|
||||
EntityType::Region(r) => Grippable::grips(r),
|
||||
EntityType::Body(b) => Grippable::grips(b),
|
||||
EntityType::Face3D(f) => Grippable::grips(f),
|
||||
EntityType::PolygonMesh(pm) => Grippable::grips(pm),
|
||||
EntityType::PolyfaceMesh(pfm) => Grippable::grips(pfm),
|
||||
@@ -177,6 +180,18 @@ impl EntityTypeOps for EntityType {
|
||||
solid,
|
||||
text_style_names,
|
||||
)),
|
||||
EntityType::Solid3D(s) => Some(PropertyEditable::geometry_properties(
|
||||
s,
|
||||
text_style_names,
|
||||
)),
|
||||
EntityType::Region(r) => Some(PropertyEditable::geometry_properties(
|
||||
r,
|
||||
text_style_names,
|
||||
)),
|
||||
EntityType::Body(b) => Some(PropertyEditable::geometry_properties(
|
||||
b,
|
||||
text_style_names,
|
||||
)),
|
||||
EntityType::Face3D(f) => Some(PropertyEditable::geometry_properties(
|
||||
f,
|
||||
text_style_names,
|
||||
@@ -267,6 +282,9 @@ impl EntityTypeOps for EntityType {
|
||||
EntityType::MLine(ml) => PropertyEditable::apply_geom_prop(ml, field, value),
|
||||
EntityType::Tolerance(tol) => PropertyEditable::apply_geom_prop(tol, field, value),
|
||||
EntityType::Solid(solid) => PropertyEditable::apply_geom_prop(solid, field, value),
|
||||
EntityType::Solid3D(s) => PropertyEditable::apply_geom_prop(s, field, value),
|
||||
EntityType::Region(r) => PropertyEditable::apply_geom_prop(r, field, value),
|
||||
EntityType::Body(b) => PropertyEditable::apply_geom_prop(b, field, value),
|
||||
EntityType::Face3D(f) => PropertyEditable::apply_geom_prop(f, field, value),
|
||||
EntityType::PolygonMesh(pm) => PropertyEditable::apply_geom_prop(pm, field, value),
|
||||
EntityType::PolyfaceMesh(pfm) => PropertyEditable::apply_geom_prop(pfm, field, value),
|
||||
@@ -307,6 +325,9 @@ impl EntityTypeOps for EntityType {
|
||||
EntityType::MLine(ml) => Grippable::apply_grip(ml, grip_id, apply),
|
||||
EntityType::Tolerance(tol) => Grippable::apply_grip(tol, grip_id, apply),
|
||||
EntityType::Solid(solid) => Grippable::apply_grip(solid, grip_id, apply),
|
||||
EntityType::Solid3D(s) => Grippable::apply_grip(s, grip_id, apply),
|
||||
EntityType::Region(r) => Grippable::apply_grip(r, grip_id, apply),
|
||||
EntityType::Body(b) => Grippable::apply_grip(b, grip_id, apply),
|
||||
EntityType::Face3D(f) => Grippable::apply_grip(f, grip_id, apply),
|
||||
EntityType::PolygonMesh(pm) => Grippable::apply_grip(pm, grip_id, apply),
|
||||
EntityType::PolyfaceMesh(pfm) => Grippable::apply_grip(pfm, grip_id, apply),
|
||||
|
||||
@@ -2304,9 +2304,42 @@ impl Scene {
|
||||
// ── Grip editing ──────────────────────────────────────────────────────
|
||||
|
||||
pub fn apply_grip(&mut self, handle: Handle, grip_id: usize, apply: GripApply) {
|
||||
// For Solid3D / Region / Body, record the old point_of_reference so we
|
||||
// can translate the pre-tessellated MeshModel by the same delta after
|
||||
// the grip is applied (the ACIS data itself is not modified).
|
||||
let old_por: Option<[f64; 3]> = match self.document.get_entity(handle) {
|
||||
Some(EntityType::Solid3D(s)) => Some([s.point_of_reference.x, s.point_of_reference.y, s.point_of_reference.z]),
|
||||
Some(EntityType::Region(r)) => Some([r.point_of_reference.x, r.point_of_reference.y, r.point_of_reference.z]),
|
||||
Some(EntityType::Body(b)) => Some([b.point_of_reference.x, b.point_of_reference.y, b.point_of_reference.z]),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(entity) = self.document.get_entity_mut(handle) {
|
||||
dispatch::apply_grip(entity, grip_id, apply);
|
||||
}
|
||||
|
||||
// Translate MeshModel vertices by the same delta the grip applied.
|
||||
if let Some(old) = old_por {
|
||||
let new_por: Option<[f64; 3]> = match self.document.get_entity(handle) {
|
||||
Some(EntityType::Solid3D(s)) => Some([s.point_of_reference.x, s.point_of_reference.y, s.point_of_reference.z]),
|
||||
Some(EntityType::Region(r)) => Some([r.point_of_reference.x, r.point_of_reference.y, r.point_of_reference.z]),
|
||||
Some(EntityType::Body(b)) => Some([b.point_of_reference.x, b.point_of_reference.y, b.point_of_reference.z]),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(new) = new_por {
|
||||
let dx = (new[0] - old[0]) as f32;
|
||||
let dy = (new[1] - old[1]) as f32;
|
||||
let dz = (new[2] - old[2]) as f32;
|
||||
if let Some(mesh) = self.meshes.get_mut(&handle) {
|
||||
for v in &mut mesh.verts {
|
||||
v[0] += dx;
|
||||
v[1] += dy;
|
||||
v[2] += dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild GPU hatch/solid model when a boundary vertex or corner moves.
|
||||
let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] };
|
||||
match self.document.get_entity(handle) {
|
||||
|
||||
+18
-1
@@ -259,7 +259,24 @@ pub fn tessellate(
|
||||
// stored in the entity when present (e.g. from SOLVIEW output
|
||||
// or when the SAT kernel cannot parse the ACIS data).
|
||||
let wire_pts = solid_wire_fallback(entity, world_offset);
|
||||
return WireModel::solid(name, wire_pts, color, selected);
|
||||
let mut wm = WireModel::solid(name, wire_pts, color, selected);
|
||||
// Add insertion snap at point_of_reference.
|
||||
let [ox, oy, oz] = world_offset;
|
||||
let por = match entity {
|
||||
EntityType::Solid3D(s) => Some(&s.point_of_reference),
|
||||
EntityType::Region(r) => Some(&r.point_of_reference),
|
||||
EntityType::Body(b) => Some(&b.point_of_reference),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(p) = por {
|
||||
let sp = Vec3::new(
|
||||
(p.x - ox) as f32,
|
||||
(p.y - oy) as f32,
|
||||
(p.z - oz) as f32,
|
||||
);
|
||||
wm.snap_pts.push((sp, SnapHint::Insertion));
|
||||
}
|
||||
return wm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user