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 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Sonnet 4.6 committed 2026-05-05 20:12:44 +03:00
1 parent de520199ac
commit f000a8acfb
5 files changed
+169 -5

No files matched your search

+5 -5
View File
@@ -43,7 +43,7 @@ Legend: ✅ Done · ⚠️ Partial · ❌ Not done
| Status | Entity | Notes |
|---|---|---|
| ✅ | **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)
@@ -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()` |
| ✅ | **AttributeEntity** | `rotation` | 50 | `src/io/mod.rs:224` |
| ✅ | **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 |
|---|---|
| ✅ Done | 44 |
| ✅ Done | 46 |
| ⚠️ Partial | 3 |
| ❌ Not done | 2 |
| ❌ Not done | 0 |
| **Total** | **49** |
### Remaining gaps by priority
**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)
+1
View File
@@ -13,6 +13,7 @@ pub mod lwpolyline;
pub mod mesh;
pub mod mline;
pub mod mtext;
pub mod ole2frame;
pub mod point;
pub mod polyline;
pub mod raster_image;
+151
View 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); }
}
+9
View File
@@ -63,6 +63,7 @@ impl EntityTypeOps for EntityType {
EntityType::MultiLeader(ml) => TruckConvertible::to_truck(ml, document),
EntityType::Underlay(ul) => TruckConvertible::to_truck(ul, document),
EntityType::Shape(shp) => TruckConvertible::to_truck(shp, document),
EntityType::Ole2Frame(ole) => TruckConvertible::to_truck(ole, document),
_ => None,
}
}
@@ -102,6 +103,7 @@ impl EntityTypeOps for EntityType {
EntityType::Hatch(hatch) => Grippable::grips(hatch),
EntityType::Underlay(ul) => Grippable::grips(ul),
EntityType::Shape(shp) => Grippable::grips(shp),
EntityType::Ole2Frame(ole) => Grippable::grips(ole),
_ => vec![],
}
}
@@ -236,6 +238,10 @@ impl EntityTypeOps for EntityType {
shp,
text_style_names,
)),
EntityType::Ole2Frame(ole) => Some(PropertyEditable::geometry_properties(
ole,
text_style_names,
)),
_ => None,
}
}
@@ -277,6 +283,7 @@ impl EntityTypeOps for EntityType {
EntityType::MultiLeader(ml) => PropertyEditable::apply_geom_prop(ml, field, value),
EntityType::Underlay(ul) => PropertyEditable::apply_geom_prop(ul, 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::Underlay(ul) => Grippable::apply_grip(ul, 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::Underlay(ul) => Transformable::apply_transform(ul, t),
EntityType::Shape(shp) => Transformable::apply_transform(shp, t),
EntityType::Ole2Frame(ole) => Transformable::apply_transform(ole, t),
_ => {}
}
}
+3
View File
@@ -221,6 +221,9 @@ fn fix_dxf_dimension_rotations(doc: &mut CadDocument) {
EntityType::AttributeEntity(a) => {
a.rotation = a.rotation.to_radians();
}
EntityType::Shape(s) => {
s.rotation = s.rotation.to_radians();
}
_ => {}
}
}