2026-04-03 19:22:10 +03:00
|
|
|
// SOLID entity — 2D filled quadrilateral (or triangle when p3 == p4).
|
|
|
|
|
//
|
|
|
|
|
// Wireframe: the 4 perimeter edges as TruckObject::Lines.
|
|
|
|
|
// Filled: the scene injects a solid-fill HatchModel (like hatch.rs).
|
|
|
|
|
// Grips: 4 corner grip points.
|
|
|
|
|
|
|
|
|
|
use acadrust::entities::Solid;
|
|
|
|
|
|
|
|
|
|
use crate::command::EntityTransform;
|
|
|
|
|
use crate::entities::common::{edit_prop as edit, square_grip};
|
|
|
|
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
2026-06-16 11:33:46 +03:00
|
|
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
|
|
|
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
|
|
|
|
use crate::scene::model::wire_model::SnapHint;
|
2026-04-03 19:22:10 +03:00
|
|
|
|
2026-05-12 23:47:40 +03:00
|
|
|
fn v3(v: &acadrust::types::Vector3) -> [f64; 3] {
|
|
|
|
|
[v.x, v.y, v.z]
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 00:04:37 +03:00
|
|
|
fn dvec3(v: &acadrust::types::Vector3) -> glam::DVec3 {
|
|
|
|
|
glam::DVec3::new(v.x, v.y, v.z)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 19:22:10 +03:00
|
|
|
impl TruckConvertible for Solid {
|
|
|
|
|
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
|
|
|
|
let p0 = v3(&self.first_corner);
|
|
|
|
|
let p1 = v3(&self.second_corner);
|
|
|
|
|
let p2 = v3(&self.third_corner);
|
|
|
|
|
let p3 = v3(&self.fourth_corner);
|
|
|
|
|
|
|
|
|
|
// DXF SOLID vertex order: 1-2-4-3 (Z-shaped), render as closed quad outline.
|
|
|
|
|
// AutoCAD stores corners in "Z" order: p0-p1 are top edge, p2-p3 are bottom edge,
|
|
|
|
|
// so the visual quad is p0→p1→p3→p2→p0.
|
|
|
|
|
let pts = vec![
|
2026-05-12 10:36:15 +03:00
|
|
|
p0,
|
|
|
|
|
p1,
|
2026-05-12 23:47:40 +03:00
|
|
|
[f64::NAN; 3],
|
2026-05-12 10:36:15 +03:00
|
|
|
p1,
|
|
|
|
|
p3,
|
2026-05-12 23:47:40 +03:00
|
|
|
[f64::NAN; 3],
|
2026-05-12 10:36:15 +03:00
|
|
|
p3,
|
|
|
|
|
p2,
|
2026-05-12 23:47:40 +03:00
|
|
|
[f64::NAN; 3],
|
2026-05-12 10:36:15 +03:00
|
|
|
p2,
|
|
|
|
|
p0,
|
2026-04-03 19:22:10 +03:00
|
|
|
];
|
|
|
|
|
|
refactor(snap): producers emit f64 snap points (world_offset removal P3)
TruckEntity.snap_pts is now DVec3 and every entity producer builds snap
candidates straight from the acadrust f64 coordinates instead of casting
to f32 first (text, mtext, arc, circle, ellipse, point, shape, solid,
mline, mtext, attribute, tolerance, table, underlay, ole2frame, mesh,
multileader, dimension, block-cache). offset_snap_pts stays f64
throughout. Still offset-relative (inert) — but snap precision is no
longer capped at ~0.5 m by an early f32 cast, which fixes a latent
UTM-scale snap error and sets up the absolute-coordinate switch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 22:10:29 +03:00
|
|
|
let dv = |c: &acadrust::types::Vector3| glam::DVec3::new(c.x, c.y, c.z);
|
2026-04-03 19:22:10 +03:00
|
|
|
let snap = vec![
|
refactor(snap): producers emit f64 snap points (world_offset removal P3)
TruckEntity.snap_pts is now DVec3 and every entity producer builds snap
candidates straight from the acadrust f64 coordinates instead of casting
to f32 first (text, mtext, arc, circle, ellipse, point, shape, solid,
mline, mtext, attribute, tolerance, table, underlay, ole2frame, mesh,
multileader, dimension, block-cache). offset_snap_pts stays f64
throughout. Still offset-relative (inert) — but snap precision is no
longer capped at ~0.5 m by an early f32 cast, which fixes a latent
UTM-scale snap error and sets up the absolute-coordinate switch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 22:10:29 +03:00
|
|
|
(dv(&self.first_corner), SnapHint::Node),
|
|
|
|
|
(dv(&self.second_corner), SnapHint::Node),
|
|
|
|
|
(dv(&self.third_corner), SnapHint::Node),
|
|
|
|
|
(dv(&self.fourth_corner), SnapHint::Node),
|
2026-04-03 19:22:10 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
Some(TruckEntity {
|
|
|
|
|
object: TruckObject::Lines(pts),
|
|
|
|
|
snap_pts: snap,
|
|
|
|
|
tangent_geoms: vec![],
|
|
|
|
|
key_vertices: vec![p0, p1, p2, p3],
|
2026-05-06 23:24:26 +03:00
|
|
|
fill_tris: vec![],
|
2026-04-03 19:22:10 +03:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Grippable for Solid {
|
|
|
|
|
fn grips(&self) -> Vec<GripDef> {
|
|
|
|
|
vec![
|
2026-06-11 00:04:37 +03:00
|
|
|
square_grip(0, dvec3(&self.first_corner)),
|
|
|
|
|
square_grip(1, dvec3(&self.second_corner)),
|
|
|
|
|
square_grip(2, dvec3(&self.third_corner)),
|
|
|
|
|
square_grip(3, dvec3(&self.fourth_corner)),
|
2026-04-03 19:22:10 +03:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
|
|
|
|
let corner = match grip_id {
|
|
|
|
|
0 => &mut self.first_corner,
|
|
|
|
|
1 => &mut self.second_corner,
|
|
|
|
|
2 => &mut self.third_corner,
|
|
|
|
|
3 => &mut self.fourth_corner,
|
|
|
|
|
_ => return,
|
|
|
|
|
};
|
|
|
|
|
match apply {
|
|
|
|
|
GripApply::Translate(d) => {
|
|
|
|
|
corner.x += d.x as f64;
|
|
|
|
|
corner.y += d.y as f64;
|
|
|
|
|
corner.z += d.z as f64;
|
|
|
|
|
}
|
|
|
|
|
GripApply::Absolute(p) => {
|
|
|
|
|
corner.x = p.x as f64;
|
|
|
|
|
corner.y = p.y as f64;
|
|
|
|
|
corner.z = p.z as f64;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PropertyEditable for Solid {
|
2026-07-02 22:46:19 +03:00
|
|
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
|
|
|
|
vec![PropSection {
|
2026-04-03 19:22:10 +03:00
|
|
|
title: "Geometry".into(),
|
|
|
|
|
props: vec![
|
|
|
|
|
edit("P1 X", "sl_p1x", self.first_corner.x),
|
|
|
|
|
edit("P1 Y", "sl_p1y", self.first_corner.y),
|
|
|
|
|
edit("P1 Z", "sl_p1z", self.first_corner.z),
|
|
|
|
|
edit("P2 X", "sl_p2x", self.second_corner.x),
|
|
|
|
|
edit("P2 Y", "sl_p2y", self.second_corner.y),
|
|
|
|
|
edit("P2 Z", "sl_p2z", self.second_corner.z),
|
|
|
|
|
edit("P3 X", "sl_p3x", self.third_corner.x),
|
|
|
|
|
edit("P3 Y", "sl_p3y", self.third_corner.y),
|
|
|
|
|
edit("P3 Z", "sl_p3z", self.third_corner.z),
|
|
|
|
|
edit("P4 X", "sl_p4x", self.fourth_corner.x),
|
|
|
|
|
edit("P4 Y", "sl_p4y", self.fourth_corner.y),
|
|
|
|
|
edit("P4 Z", "sl_p4z", self.fourth_corner.z),
|
|
|
|
|
edit("Thickness", "sl_thick", self.thickness),
|
|
|
|
|
],
|
2026-07-02 22:46:19 +03:00
|
|
|
}]
|
2026-04-03 19:22:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
2026-05-12 10:36:15 +03:00
|
|
|
let Ok(v) = value.trim().parse::<f64>() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2026-04-03 19:22:10 +03:00
|
|
|
match field {
|
|
|
|
|
"sl_p1x" => self.first_corner.x = v,
|
|
|
|
|
"sl_p1y" => self.first_corner.y = v,
|
|
|
|
|
"sl_p1z" => self.first_corner.z = v,
|
|
|
|
|
"sl_p2x" => self.second_corner.x = v,
|
|
|
|
|
"sl_p2y" => self.second_corner.y = v,
|
|
|
|
|
"sl_p2z" => self.second_corner.z = v,
|
|
|
|
|
"sl_p3x" => self.third_corner.x = v,
|
|
|
|
|
"sl_p3y" => self.third_corner.y = v,
|
|
|
|
|
"sl_p3z" => self.third_corner.z = v,
|
|
|
|
|
"sl_p4x" => self.fourth_corner.x = v,
|
|
|
|
|
"sl_p4y" => self.fourth_corner.y = v,
|
|
|
|
|
"sl_p4z" => self.fourth_corner.z = v,
|
|
|
|
|
"sl_thick" => self.thickness = v,
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Transformable for Solid {
|
|
|
|
|
fn apply_transform(&mut self, t: &EntityTransform) {
|
2026-06-16 11:33:46 +03:00
|
|
|
crate::scene::view::transform::apply_standard_entity_transform(self, t, |entity, p1, p2| {
|
2026-04-03 19:22:10 +03:00
|
|
|
for corner in [
|
|
|
|
|
&mut entity.first_corner,
|
|
|
|
|
&mut entity.second_corner,
|
|
|
|
|
&mut entity.third_corner,
|
|
|
|
|
&mut entity.fourth_corner,
|
|
|
|
|
] {
|
2026-06-16 11:33:46 +03:00
|
|
|
crate::scene::view::transform::reflect_xy_point(&mut corner.x, &mut corner.y, p1, p2);
|
2026-04-03 19:22:10 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|