feat: EXPLODE now supports Dimension entities

Decomposes Dimension (Linear, Aligned, Radius, Diameter, Angular2Ln,
Angular3Pt, Ordinate) into constituent Lines (extension lines, dimension
line) plus a Text entity for the dimension label value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-07 11:29:39 +03:00
commit 7a07ef8710
2 changed files with 99 additions and 4 deletions

View file

@ -122,7 +122,7 @@ Underlay (PDF/DWF/DGN)
| FILLET (F) | 🔧 | LwPolyline desteği yok |
| CHAMFER (CHA) | ✅ | — |
| JOIN (J) | ✅ | — |
| EXPLODE (X) | 🔧 | Dimension eksik |
| EXPLODE (X) | ✅ | — |
| PEDIT (PE) | ✅ | — |
| STRETCH (SS) | ✅ | — |
| SPLINEDIT | ⬜ | — |
@ -303,8 +303,7 @@ Underlay (PDF/DWF/DGN)
### Yüksek Öncelik
1. **TRIM / EXTEND / OFFSET / BREAK / LENGTHEN → Spline desteği**
2. **EXPLODE → Dimension desteği**
3. **FILLET → LwPolyline desteği**
2. **FILLET → LwPolyline desteği**
### Orta Öncelik
5. **Solid3D tessellation** tamamlama (ACIS → truck pipeline)

View file

@ -10,13 +10,16 @@
// Polyline → Lines
// Insert → constituent entities (via acadrust explode_from_document)
// MLine → Lines (spine + offset lines per miter direction)
// Dimension → Lines (extension + dimension + arrows) + Text
//
// Unsupported entity types are skipped silently.
use std::f64::consts::TAU;
use acadrust::entities::EntityCommon;
use acadrust::entities::{Arc as ArcEnt, Circle as CircleEnt, Line as LineEnt, LwPolyline, MLine};
use acadrust::entities::{
Arc as ArcEnt, Circle as CircleEnt, Dimension, Line as LineEnt, LwPolyline, MLine,
};
use acadrust::entities::{Polyline, Polyline2D};
use acadrust::types::Vector3;
use acadrust::{CadDocument, EntityType, Handle};
@ -52,6 +55,7 @@ pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec<Entity
.map(normalize_insert_entity)
.collect(),
EntityType::MLine(ml) => explode_mline(ml),
EntityType::Dimension(dim) => explode_dimension(dim),
_ => vec![],
}
}
@ -333,6 +337,98 @@ fn explode_mline(ml: &MLine) -> Vec<EntityType> {
result
}
// ── Dimension explode ──────────────────────────────────────────────────────
/// Convert a Dimension entity into Lines (geometry) + Text (label).
fn explode_dimension(dim: &Dimension) -> Vec<EntityType> {
use acadrust::entities::Text;
let base = dim.base();
let common = base.common.clone();
let mut result: Vec<EntityType> = Vec::new();
// Helper: make a line segment
let make_seg = |a: &Vector3, b: &Vector3, common: &EntityCommon| -> EntityType {
let mut c = common.clone();
c.handle = Handle::NULL;
EntityType::Line(LineEnt { common: c, start: a.clone(), end: b.clone(), ..LineEnt::new() })
};
let v3 = |x: f64, y: f64, z: f64| Vector3::new(x, y, z);
match dim {
Dimension::Aligned(d) => {
let fx = d.first_point.x; let fy = d.first_point.y;
let sx = d.second_point.x; let sy = d.second_point.y;
let dx_s = sx - fx; let dy_s = sy - fy;
let len = (dx_s*dx_s + dy_s*dy_s).sqrt().max(1e-12);
let axis_angle = dy_s.atan2(dx_s);
let perp_x = -(axis_angle.sin()); let perp_y = axis_angle.cos();
let offset = (d.definition_point.x - fx) * perp_x + (d.definition_point.y - fy) * perp_y;
let d1 = v3(fx + perp_x * offset, fy + perp_y * offset, d.first_point.z);
let d2 = v3(sx + perp_x * offset, sy + perp_y * offset, d.second_point.z);
result.push(make_seg(&d.first_point, &d1, &common));
result.push(make_seg(&d.second_point, &d2, &common));
result.push(make_seg(&d1, &d2, &common));
let _ = len;
}
Dimension::Linear(d) => {
let angle = d.rotation.to_radians();
let perp_x = -(angle.sin()); let perp_y = angle.cos();
let fx = d.first_point.x; let fy = d.first_point.y;
let sx = d.second_point.x; let sy = d.second_point.y;
let offset = (d.definition_point.x - fx) * perp_x + (d.definition_point.y - fy) * perp_y;
let d1 = v3(fx + perp_x * offset, fy + perp_y * offset, d.first_point.z);
let d2 = v3(sx + perp_x * offset, sy + perp_y * offset, d.second_point.z);
result.push(make_seg(&d.first_point, &d1, &common));
result.push(make_seg(&d.second_point, &d2, &common));
result.push(make_seg(&d1, &d2, &common));
}
Dimension::Radius(d) => {
result.push(make_seg(&d.angle_vertex, &d.definition_point, &common));
}
Dimension::Diameter(d) => {
result.push(make_seg(&d.angle_vertex, &d.definition_point, &common));
}
Dimension::Angular2Ln(d) => {
result.push(make_seg(&d.first_point, &d.angle_vertex, &common));
result.push(make_seg(&d.second_point, &d.angle_vertex, &common));
}
Dimension::Angular3Pt(d) => {
result.push(make_seg(&d.first_point, &d.angle_vertex, &common));
result.push(make_seg(&d.second_point, &d.angle_vertex, &common));
}
Dimension::Ordinate(d) => {
result.push(make_seg(&d.feature_location, &d.definition_point, &common));
result.push(make_seg(&d.definition_point, &d.leader_endpoint, &common));
}
}
// Text entity for the dimension label
let text_val = if let Some(u) = &base.user_text {
if !u.trim().is_empty() { u.clone() } else { format!("{:.4}", dim.measurement()) }
} else if !base.text.trim().is_empty() {
base.text.clone()
} else {
match dim {
Dimension::Radius(_) => format!("R{:.4}", dim.measurement()),
Dimension::Diameter(_) => format!("Ø{:.4}", dim.measurement()),
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_) =>
format!("{:.2}°", dim.measurement()),
_ => format!("{:.4}", dim.measurement()),
}
};
let mut text = Text::with_value(text_val, base.text_middle_point.clone())
.with_height(base.line_spacing_factor.abs().max(0.1))
.with_rotation(base.text_rotation);
text.common = common.clone();
text.common.handle = Handle::NULL;
result.push(EntityType::Text(text));
result
}
// ── Command stub (kept for future interactive selection mode) ───────────────
pub struct ExplodeCommand;