feat: add OLE2Frame interactivity + fix Shape rotation from DXF
OLE2Frame: full TruckConvertible/Grippable/PropertyEditable/Transformable
impls in src/entities/ole2frame.rs — grips on corners + center, properties
panel, translate/scale transforms.
Shape: rotation (DXF code 50) now converted degrees→radians on load in
fix_dxf_dimension_rotations(), matching Dimension and Attribute behaviour.
All ❌ gaps resolved. Remaining: Viewport GPU scissor, LWPolyline plinegen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
de520199ac
commit
f000a8acfb
5 changed files with 169 additions and 5 deletions
|
|
@ -43,7 +43,7 @@ Legend: ✅ Done · ⚠️ Partial · ❌ Not done
|
||||||
| Status | Entity | Notes |
|
| Status | Entity | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| ✅ | **Face3D** | Full tessellation, grips, properties panel — `src/entities/mesh.rs` |
|
| ✅ | **Face3D** | Full tessellation, grips, properties panel — `src/entities/mesh.rs` |
|
||||||
| ❌ | **OLE2Frame** | Bounding box + X mark only; no interactivity |
|
| ✅ | **OLE2Frame** | Bounding box + X mark with grips, properties panel, transform — `src/entities/ole2frame.rs` |
|
||||||
|
|
||||||
### Wire Fallback Only (no full mesh)
|
### Wire Fallback Only (no full mesh)
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ These are fixed in our post-load `fix_dxf_dimension_rotations()` in `src/io/mod.
|
||||||
| ✅ | **Dimension (all)** | `text_rotation` | 53 | `src/scene/tessellate.rs::dimension_text_natural_rotation()` |
|
| ✅ | **Dimension (all)** | `text_rotation` | 53 | `src/scene/tessellate.rs::dimension_text_natural_rotation()` |
|
||||||
| ✅ | **AttributeEntity** | `rotation` | 50 | `src/io/mod.rs:224` |
|
| ✅ | **AttributeEntity** | `rotation` | 50 | `src/io/mod.rs:224` |
|
||||||
| ✅ | **AttributeDefinition** | `rotation` | 50 | `src/io/mod.rs:219` |
|
| ✅ | **AttributeDefinition** | `rotation` | 50 | `src/io/mod.rs:219` |
|
||||||
| ❌ | **Shape** | `rotation` | 50 | Shape not currently rendered — low priority |
|
| ✅ | **Shape** | `rotation` | 50 | Fixed in `fix_dxf_dimension_rotations()` — `src/io/mod.rs` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -207,13 +207,13 @@ These are fixed in our post-load `fix_dxf_dimension_rotations()` in `src/io/mod.
|
||||||
|
|
||||||
| Status | Count |
|
| Status | Count |
|
||||||
|---|---|
|
|---|---|
|
||||||
| ✅ Done | 44 |
|
| ✅ Done | 46 |
|
||||||
| ⚠️ Partial | 3 |
|
| ⚠️ Partial | 3 |
|
||||||
| ❌ Not done | 2 |
|
| ❌ Not done | 0 |
|
||||||
| **Total** | **49** |
|
| **Total** | **49** |
|
||||||
|
|
||||||
### Remaining gaps by priority
|
### Remaining gaps by priority
|
||||||
|
|
||||||
**Medium:** Viewport GPU scissor rect (pixel-level boundary clipping for overlapping viewports)
|
**Medium:** Viewport GPU scissor rect (pixel-level boundary clipping for overlapping viewports)
|
||||||
|
|
||||||
**Low:** LWPolyline plinegen (GPU shader change needed) · OLE2Frame improvement · Shape rotation
|
**Low:** LWPolyline plinegen (GPU shader change needed)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub mod lwpolyline;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod mline;
|
pub mod mline;
|
||||||
pub mod mtext;
|
pub mod mtext;
|
||||||
|
pub mod ole2frame;
|
||||||
pub mod point;
|
pub mod point;
|
||||||
pub mod polyline;
|
pub mod polyline;
|
||||||
pub mod raster_image;
|
pub mod raster_image;
|
||||||
|
|
|
||||||
151
src/entities/ole2frame.rs
Normal file
151
src/entities/ole2frame.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
use acadrust::entities::{Ole2Frame, OleObjectType};
|
||||||
|
use glam::Vec3;
|
||||||
|
|
||||||
|
use crate::command::EntityTransform;
|
||||||
|
use crate::entities::common::{diamond_grip, ro_prop as ro, edit_prop as edit, square_grip};
|
||||||
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
|
use crate::scene::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
|
use crate::scene::object::{GripApply, GripDef, PropSection};
|
||||||
|
use crate::scene::wire_model::SnapHint;
|
||||||
|
|
||||||
|
fn to_truck(ole: &Ole2Frame) -> TruckEntity {
|
||||||
|
let x0 = ole.upper_left_corner.x as f32;
|
||||||
|
let y0 = ole.lower_right_corner.y as f32;
|
||||||
|
let x1 = ole.lower_right_corner.x as f32;
|
||||||
|
let y1 = ole.upper_left_corner.y as f32;
|
||||||
|
let z = ole.upper_left_corner.z as f32;
|
||||||
|
|
||||||
|
if (x1 - x0).abs() < 1e-6 && (y1 - y0).abs() < 1e-6 {
|
||||||
|
let s = 0.5_f32;
|
||||||
|
return TruckEntity {
|
||||||
|
object: TruckObject::Lines(vec![[-s, 0.0, z], [s, 0.0, z]]),
|
||||||
|
snap_pts: vec![],
|
||||||
|
tangent_geoms: vec![],
|
||||||
|
key_vertices: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let cx = (x0 + x1) * 0.5;
|
||||||
|
let cy = (y0 + y1) * 0.5;
|
||||||
|
let pts = vec![
|
||||||
|
// Rectangle
|
||||||
|
[x0, y0, z], [x1, y0, z], [x1, y0, z], [x1, y1, z],
|
||||||
|
[x1, y1, z], [x0, y1, z], [x0, y1, z], [x0, y0, z],
|
||||||
|
// Diagonal X
|
||||||
|
[x0, y0, z], [x1, y1, z],
|
||||||
|
[f32::NAN; 3],
|
||||||
|
[x1, y0, z], [x0, y1, z],
|
||||||
|
];
|
||||||
|
let center = Vec3::new(cx, cy, z);
|
||||||
|
TruckEntity {
|
||||||
|
object: TruckObject::Lines(pts),
|
||||||
|
snap_pts: vec![(center, SnapHint::Center)],
|
||||||
|
tangent_geoms: vec![],
|
||||||
|
key_vertices: vec![[x0, y0, z], [x1, y1, z]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grips(ole: &Ole2Frame) -> Vec<GripDef> {
|
||||||
|
let ul = Vec3::new(
|
||||||
|
ole.upper_left_corner.x as f32,
|
||||||
|
ole.upper_left_corner.y as f32,
|
||||||
|
ole.upper_left_corner.z as f32,
|
||||||
|
);
|
||||||
|
let lr = Vec3::new(
|
||||||
|
ole.lower_right_corner.x as f32,
|
||||||
|
ole.lower_right_corner.y as f32,
|
||||||
|
ole.lower_right_corner.z as f32,
|
||||||
|
);
|
||||||
|
let center = (ul + lr) * 0.5;
|
||||||
|
vec![square_grip(0, ul), square_grip(1, lr), diamond_grip(2, center)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn properties(ole: &Ole2Frame) -> PropSection {
|
||||||
|
let type_str = match ole.ole_object_type {
|
||||||
|
OleObjectType::Link => "Link",
|
||||||
|
OleObjectType::Embedded => "Embedded",
|
||||||
|
OleObjectType::Static => "Static",
|
||||||
|
};
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Type", "ole_type", type_str),
|
||||||
|
edit("Upper Left X", "ole_ulx", ole.upper_left_corner.x),
|
||||||
|
edit("Upper Left Y", "ole_uly", ole.upper_left_corner.y),
|
||||||
|
edit("Lower Right X", "ole_lrx", ole.lower_right_corner.x),
|
||||||
|
edit("Lower Right Y", "ole_lry", ole.lower_right_corner.y),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) {
|
||||||
|
let Ok(v) = value.trim().parse::<f64>() else { return };
|
||||||
|
match field {
|
||||||
|
"ole_ulx" => ole.upper_left_corner.x = v,
|
||||||
|
"ole_uly" => ole.upper_left_corner.y = v,
|
||||||
|
"ole_lrx" => ole.lower_right_corner.x = v,
|
||||||
|
"ole_lry" => ole.lower_right_corner.y = v,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_grip(ole: &mut Ole2Frame, grip_id: usize, apply: GripApply) {
|
||||||
|
match (grip_id, apply) {
|
||||||
|
(0, GripApply::Absolute(p)) => {
|
||||||
|
ole.upper_left_corner.x = p.x as f64;
|
||||||
|
ole.upper_left_corner.y = p.y as f64;
|
||||||
|
}
|
||||||
|
(1, GripApply::Absolute(p)) => {
|
||||||
|
ole.lower_right_corner.x = p.x as f64;
|
||||||
|
ole.lower_right_corner.y = p.y as f64;
|
||||||
|
}
|
||||||
|
(2, GripApply::Translate(d)) => {
|
||||||
|
ole.upper_left_corner.x += d.x as f64;
|
||||||
|
ole.upper_left_corner.y += d.y as f64;
|
||||||
|
ole.lower_right_corner.x += d.x as f64;
|
||||||
|
ole.lower_right_corner.y += d.y as f64;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_transform(ole: &mut Ole2Frame, t: &EntityTransform) {
|
||||||
|
match t {
|
||||||
|
EntityTransform::Translate(d) => {
|
||||||
|
ole.upper_left_corner.x += d.x as f64;
|
||||||
|
ole.upper_left_corner.y += d.y as f64;
|
||||||
|
ole.upper_left_corner.z += d.z as f64;
|
||||||
|
ole.lower_right_corner.x += d.x as f64;
|
||||||
|
ole.lower_right_corner.y += d.y as f64;
|
||||||
|
ole.lower_right_corner.z += d.z as f64;
|
||||||
|
}
|
||||||
|
EntityTransform::Scale { center, factor } => {
|
||||||
|
let scale = |v: f64, c: f64| c + (v - c) * (*factor as f64);
|
||||||
|
ole.upper_left_corner.x = scale(ole.upper_left_corner.x, center.x as f64);
|
||||||
|
ole.upper_left_corner.y = scale(ole.upper_left_corner.y, center.y as f64);
|
||||||
|
ole.lower_right_corner.x = scale(ole.lower_right_corner.x, center.x as f64);
|
||||||
|
ole.lower_right_corner.y = scale(ole.lower_right_corner.y, center.y as f64);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TruckConvertible for Ole2Frame {
|
||||||
|
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
||||||
|
Some(to_truck(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grippable for Ole2Frame {
|
||||||
|
fn grips(&self) -> Vec<GripDef> { grips(self) }
|
||||||
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PropertyEditable for Ole2Frame {
|
||||||
|
fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) }
|
||||||
|
fn apply_geom_prop(&mut self, field: &str, value: &str) { apply_geom_prop(self, field, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transformable for Ole2Frame {
|
||||||
|
fn apply_transform(&mut self, t: &EntityTransform) { apply_transform(self, t); }
|
||||||
|
}
|
||||||
|
|
@ -63,6 +63,7 @@ impl EntityTypeOps for EntityType {
|
||||||
EntityType::MultiLeader(ml) => TruckConvertible::to_truck(ml, document),
|
EntityType::MultiLeader(ml) => TruckConvertible::to_truck(ml, document),
|
||||||
EntityType::Underlay(ul) => TruckConvertible::to_truck(ul, document),
|
EntityType::Underlay(ul) => TruckConvertible::to_truck(ul, document),
|
||||||
EntityType::Shape(shp) => TruckConvertible::to_truck(shp, document),
|
EntityType::Shape(shp) => TruckConvertible::to_truck(shp, document),
|
||||||
|
EntityType::Ole2Frame(ole) => TruckConvertible::to_truck(ole, document),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +103,7 @@ impl EntityTypeOps for EntityType {
|
||||||
EntityType::Hatch(hatch) => Grippable::grips(hatch),
|
EntityType::Hatch(hatch) => Grippable::grips(hatch),
|
||||||
EntityType::Underlay(ul) => Grippable::grips(ul),
|
EntityType::Underlay(ul) => Grippable::grips(ul),
|
||||||
EntityType::Shape(shp) => Grippable::grips(shp),
|
EntityType::Shape(shp) => Grippable::grips(shp),
|
||||||
|
EntityType::Ole2Frame(ole) => Grippable::grips(ole),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,6 +238,10 @@ impl EntityTypeOps for EntityType {
|
||||||
shp,
|
shp,
|
||||||
text_style_names,
|
text_style_names,
|
||||||
)),
|
)),
|
||||||
|
EntityType::Ole2Frame(ole) => Some(PropertyEditable::geometry_properties(
|
||||||
|
ole,
|
||||||
|
text_style_names,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,6 +283,7 @@ impl EntityTypeOps for EntityType {
|
||||||
EntityType::MultiLeader(ml) => PropertyEditable::apply_geom_prop(ml, field, value),
|
EntityType::MultiLeader(ml) => PropertyEditable::apply_geom_prop(ml, field, value),
|
||||||
EntityType::Underlay(ul) => PropertyEditable::apply_geom_prop(ul, field, value),
|
EntityType::Underlay(ul) => PropertyEditable::apply_geom_prop(ul, field, value),
|
||||||
EntityType::Shape(shp) => PropertyEditable::apply_geom_prop(shp, field, value),
|
EntityType::Shape(shp) => PropertyEditable::apply_geom_prop(shp, field, value),
|
||||||
|
EntityType::Ole2Frame(ole) => PropertyEditable::apply_geom_prop(ole, field, value),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,6 +323,7 @@ impl EntityTypeOps for EntityType {
|
||||||
EntityType::Hatch(hatch) => Grippable::apply_grip(hatch, grip_id, apply),
|
EntityType::Hatch(hatch) => Grippable::apply_grip(hatch, grip_id, apply),
|
||||||
EntityType::Underlay(ul) => Grippable::apply_grip(ul, grip_id, apply),
|
EntityType::Underlay(ul) => Grippable::apply_grip(ul, grip_id, apply),
|
||||||
EntityType::Shape(shp) => Grippable::apply_grip(shp, grip_id, apply),
|
EntityType::Shape(shp) => Grippable::apply_grip(shp, grip_id, apply),
|
||||||
|
EntityType::Ole2Frame(ole) => Grippable::apply_grip(ole, grip_id, apply),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -355,6 +363,7 @@ impl EntityTypeOps for EntityType {
|
||||||
EntityType::MultiLeader(ml) => Transformable::apply_transform(ml, t),
|
EntityType::MultiLeader(ml) => Transformable::apply_transform(ml, t),
|
||||||
EntityType::Underlay(ul) => Transformable::apply_transform(ul, t),
|
EntityType::Underlay(ul) => Transformable::apply_transform(ul, t),
|
||||||
EntityType::Shape(shp) => Transformable::apply_transform(shp, t),
|
EntityType::Shape(shp) => Transformable::apply_transform(shp, t),
|
||||||
|
EntityType::Ole2Frame(ole) => Transformable::apply_transform(ole, t),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,9 @@ fn fix_dxf_dimension_rotations(doc: &mut CadDocument) {
|
||||||
EntityType::AttributeEntity(a) => {
|
EntityType::AttributeEntity(a) => {
|
||||||
a.rotation = a.rotation.to_radians();
|
a.rotation = a.rotation.to_radians();
|
||||||
}
|
}
|
||||||
|
EntityType::Shape(s) => {
|
||||||
|
s.rotation = s.rotation.to_radians();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue