use std::f64::consts::PI;
use super::*;
use crate::types::{Handle, Vector3};
fn inherit_common(source: &EntityCommon) -> EntityCommon {
EntityCommon {
handle: Handle::NULL,
layer: source.layer.clone(),
color: source.color,
line_weight: source.line_weight,
linetype: source.linetype.clone(),
linetype_scale: source.linetype_scale,
transparency: source.transparency,
invisible: source.invisible,
owner_handle: source.owner_handle,
..EntityCommon::new()
}
}
fn normalize_angle(a: f64) -> f64 {
let mut a = a % (2.0 * PI);
if a < 0.0 {
a += 2.0 * PI;
}
a
}
fn arc_from_bulge(
p1x: f64,
p1y: f64,
p2x: f64,
p2y: f64,
bulge: f64,
elevation: f64,
thickness: f64,
normal: Vector3,
common: &EntityCommon,
) -> EntityType {
let dx = p2x - p1x;
let dy = p2y - p1y;
let d = (dx * dx + dy * dy).sqrt();
if d < 1e-10 {
return EntityType::Point(Point {
common: inherit_common(common),
location: Vector3::new(p1x, p1y, elevation),
thickness,
normal,
});
}
let b_sq = bulge * bulge;
let radius = d * (1.0 + b_sq) / (4.0 * bulge.abs());
let perp_offset = d * (1.0 - b_sq) / (4.0 * bulge);
let mx = (p1x + p2x) / 2.0;
let my = (p1y + p2y) / 2.0;
let px = -dy / d;
let py = dx / d;
let cx = mx + px * perp_offset;
let cy = my + py * perp_offset;
let angle_p1 = (p1y - cy).atan2(p1x - cx);
let angle_p2 = (p2y - cy).atan2(p2x - cx);
let (sa, ea) = if bulge > 0.0 {
(angle_p1, angle_p2)
} else {
(angle_p2, angle_p1)
};
EntityType::Arc(Arc {
common: inherit_common(common),
center: Vector3::new(cx, cy, elevation),
radius: radius.abs(),
start_angle: normalize_angle(sa),
end_angle: normalize_angle(ea),
thickness,
normal,
})
}
fn line_entity(start: Vector3, end: Vector3, thickness: f64, normal: Vector3, common: &EntityCommon) -> EntityType {
EntityType::Line(Line {
common: inherit_common(common),
start,
end,
thickness,
normal,
})
}
fn explode_lwpolyline(pl: &LwPolyline) -> Vec<EntityType> {
let n = pl.vertices.len();
if n < 2 {
return Vec::new();
}
let seg_count = if pl.is_closed { n } else { n - 1 };
let mut result = Vec::with_capacity(seg_count);
for i in 0..seg_count {
let v1 = &pl.vertices[i];
let v2 = &pl.vertices[(i + 1) % n];
if v1.bulge.abs() < 1e-10 {
result.push(line_entity(
Vector3::new(v1.location.x, v1.location.y, pl.elevation),
Vector3::new(v2.location.x, v2.location.y, pl.elevation),
pl.thickness,
pl.normal,
&pl.common,
));
} else {
result.push(arc_from_bulge(
v1.location.x,
v1.location.y,
v2.location.x,
v2.location.y,
v1.bulge,
pl.elevation,
pl.thickness,
pl.normal,
&pl.common,
));
}
}
result
}
fn explode_polyline2d(pl: &Polyline2D) -> Vec<EntityType> {
let n = pl.vertices.len();
if n < 2 {
return Vec::new();
}
let closed = pl.flags.is_closed();
let seg_count = if closed { n } else { n - 1 };
let mut result = Vec::with_capacity(seg_count);
for i in 0..seg_count {
let v1 = &pl.vertices[i];
let v2 = &pl.vertices[(i + 1) % n];
if v1.bulge.abs() < 1e-10 {
result.push(line_entity(
v1.location,
v2.location,
pl.thickness,
pl.normal,
&pl.common,
));
} else {
result.push(arc_from_bulge(
v1.location.x,
v1.location.y,
v2.location.x,
v2.location.y,
v1.bulge,
v1.location.z,
pl.thickness,
pl.normal,
&pl.common,
));
}
}
result
}
fn explode_polyline3d(pl: &Polyline) -> Vec<EntityType> {
let n = pl.vertices.len();
if n < 2 {
return Vec::new();
}
let closed = pl.flags.is_closed();
let seg_count = if closed { n } else { n - 1 };
let mut result = Vec::with_capacity(seg_count);
for i in 0..seg_count {
let start = pl.vertices[i].location;
let end = pl.vertices[(i + 1) % n].location;
result.push(line_entity(start, end, 0.0, Vector3::UNIT_Z, &pl.common));
}
result
}
fn explode_polyline3d_new(pl: &Polyline3D) -> Vec<EntityType> {
let n = pl.vertices.len();
if n < 2 {
return Vec::new();
}
let closed = pl.flags.closed;
let seg_count = if closed { n } else { n - 1 };
let mut result = Vec::with_capacity(seg_count);
for i in 0..seg_count {
let start = pl.vertices[i].position;
let end = pl.vertices[(i + 1) % n].position;
result.push(line_entity(start, end, 0.0, Vector3::UNIT_Z, &pl.common));
}
result
}
fn explode_circle(circle: &Circle) -> Vec<EntityType> {
vec![EntityType::Arc(Arc {
common: inherit_common(&circle.common),
center: circle.center,
radius: circle.radius,
start_angle: 0.0,
end_angle: 2.0 * PI,
thickness: circle.thickness,
normal: circle.normal,
})]
}
fn explode_ellipse(ellipse: &Ellipse) -> Vec<EntityType> {
let a = ellipse.major_axis_length();
let b = ellipse.minor_axis_length();
if a < 1e-10 {
return Vec::new();
}
let major_dir = ellipse.major_axis.normalize();
let minor_dir = ellipse.normal.cross(&major_dir).normalize();
let is_full = ellipse.is_full();
let num_points: usize = if is_full { 36 } else { 24 };
let (t_start, t_end) = if is_full {
(0.0, 2.0 * PI)
} else {
(ellipse.start_parameter, ellipse.end_parameter)
};
let mut points = Vec::with_capacity(num_points + 1);
for i in 0..=num_points {
let t = t_start + (t_end - t_start) * (i as f64 / num_points as f64);
let pt = ellipse.center + major_dir * (a * t.cos()) + minor_dir * (b * t.sin());
points.push(pt);
}
let spline = Spline::from_control_points(3, points);
let mut s = spline;
s.common = inherit_common(&ellipse.common);
if is_full {
s.flags.closed = true;
}
vec![EntityType::Spline(s)]
}
fn explode_solid(solid: &Solid) -> Vec<EntityType> {
let common = &solid.common;
let corners = solid.corners();
let n = corners.len();
let mut result = Vec::with_capacity(n);
for i in 0..n {
result.push(line_entity(
corners[i],
corners[(i + 1) % n],
solid.thickness,
solid.normal,
common,
));
}
result
}
fn explode_face3d(face: &Face3D) -> Vec<EntityType> {
let common = &face.common;
let corners = face.corners();
let n = corners.len();
let mut result = Vec::with_capacity(n);
let invisible = [
face.invisible_edges.is_first_invisible(),
face.invisible_edges.is_second_invisible(),
face.invisible_edges.is_third_invisible(),
face.invisible_edges.is_fourth_invisible(),
];
for i in 0..n {
if !invisible[i] {
result.push(line_entity(
corners[i],
corners[(i + 1) % n],
0.0,
Vector3::UNIT_Z,
common,
));
}
}
result
}
fn explode_spline(spline: &Spline) -> Vec<EntityType> {
let points = if !spline.fit_points.is_empty() {
&spline.fit_points
} else if !spline.control_points.is_empty() {
&spline.control_points
} else {
return Vec::new();
};
if points.len() < 2 {
return Vec::new();
}
let mut result = Vec::with_capacity(points.len() - 1);
for pair in points.windows(2) {
result.push(line_entity(
pair[0],
pair[1],
0.0,
spline.normal,
&spline.common,
));
}
if spline.flags.closed && points.len() > 2 {
result.push(line_entity(
*points.last().unwrap(),
points[0],
0.0,
spline.normal,
&spline.common,
));
}
result
}
fn explode_mtext(mtext: &MText) -> Vec<EntityType> {
let text = Text {
common: inherit_common(&mtext.common),
value: mtext.value.clone(),
insertion_point: mtext.insertion_point,
alignment_point: None,
height: mtext.height,
rotation: mtext.rotation,
width_factor: 1.0,
oblique_angle: 0.0,
style: mtext.style.clone(),
horizontal_alignment: TextHorizontalAlignment::Left,
vertical_alignment: TextVerticalAlignment::Baseline,
normal: mtext.normal,
};
vec![EntityType::Text(text)]
}
fn explode_leader(leader: &Leader) -> Vec<EntityType> {
let n = leader.vertices.len();
if n < 2 {
return Vec::new();
}
let mut result = Vec::with_capacity(n - 1);
for pair in leader.vertices.windows(2) {
result.push(line_entity(
pair[0],
pair[1],
0.0,
leader.normal,
&leader.common,
));
}
result
}
fn explode_multileader(ml: &MultiLeader) -> Vec<EntityType> {
let mut result = Vec::new();
for root in &ml.context.leader_roots {
for line in &root.lines {
if line.points.len() >= 2 {
for pair in line.points.windows(2) {
result.push(line_entity(
pair[0],
pair[1],
0.0,
Vector3::UNIT_Z,
&ml.common,
));
}
}
}
}
if ml.context.has_text_contents && !ml.context.text_string.is_empty() {
let text = MText {
common: inherit_common(&ml.common),
value: ml.context.text_string.clone(),
insertion_point: ml.context.text_location,
height: ml.context.text_height,
rectangle_width: ml.context.text_width,
rectangle_height: None,
rotation: ml.context.text_rotation,
style: "STANDARD".to_string(),
attachment_point: AttachmentPoint::TopLeft,
drawing_direction: DrawingDirection::LeftToRight,
line_spacing_factor: ml.context.line_spacing_factor,
normal: ml.context.text_normal,
};
result.push(EntityType::MText(text));
}
result
}
fn explode_mline(mline: &MLine) -> Vec<EntityType> {
let n = mline.vertices.len();
if n < 2 {
return Vec::new();
}
let mut result = Vec::with_capacity(n - 1);
for i in 0..n - 1 {
result.push(line_entity(
mline.vertices[i].position,
mline.vertices[i + 1].position,
0.0,
mline.normal,
&mline.common,
));
}
if mline.flags.contains(MLineFlags::CLOSED) && n > 2 {
result.push(line_entity(
mline.vertices[n - 1].position,
mline.vertices[0].position,
0.0,
mline.normal,
&mline.common,
));
}
result
}
fn explode_dimension(dim: &Dimension) -> Vec<EntityType> {
let base = dim.base();
let common = &base.common;
let text_value = if !base.text.is_empty() {
base.text.clone()
} else if let Some(ref ut) = base.user_text {
ut.clone()
} else {
format!("{:.4}", base.actual_measurement)
};
let mut result = Vec::new();
match dim {
Dimension::Linear(d) => {
result.push(line_entity(d.first_point, d.definition_point, 0.0, base.normal, common));
result.push(line_entity(d.second_point, d.definition_point, 0.0, base.normal, common));
result.push(line_entity(d.first_point, d.second_point, 0.0, base.normal, common));
}
Dimension::Aligned(d) => {
result.push(line_entity(d.first_point, d.definition_point, 0.0, base.normal, common));
result.push(line_entity(d.second_point, d.definition_point, 0.0, base.normal, common));
result.push(line_entity(d.first_point, d.second_point, 0.0, base.normal, common));
}
Dimension::Radius(d) => {
result.push(line_entity(d.angle_vertex, d.definition_point, 0.0, base.normal, common));
}
Dimension::Diameter(d) => {
result.push(line_entity(d.angle_vertex, d.definition_point, 0.0, base.normal, common));
}
Dimension::Angular2Ln(d) => {
result.push(line_entity(d.angle_vertex, d.first_point, 0.0, base.normal, common));
result.push(line_entity(d.angle_vertex, d.second_point, 0.0, base.normal, common));
}
Dimension::Angular3Pt(d) => {
result.push(line_entity(d.angle_vertex, d.first_point, 0.0, base.normal, common));
result.push(line_entity(d.angle_vertex, d.second_point, 0.0, base.normal, common));
}
Dimension::Ordinate(d) => {
result.push(line_entity(d.feature_location, d.leader_endpoint, 0.0, base.normal, common));
}
}
let text = Text {
common: inherit_common(common),
value: text_value,
insertion_point: base.text_middle_point,
alignment_point: None,
height: 2.5, rotation: base.text_rotation,
width_factor: 1.0,
oblique_angle: 0.0,
style: base.style_name.clone(),
horizontal_alignment: TextHorizontalAlignment::Center,
vertical_alignment: TextVerticalAlignment::Middle,
normal: base.normal,
};
result.push(EntityType::Text(text));
result
}
fn explode_hatch(hatch: &Hatch) -> Vec<EntityType> {
let common = &hatch.common;
let elevation = hatch.elevation;
let normal = hatch.normal;
let mut result = Vec::new();
for path in &hatch.paths {
for edge in &path.edges {
match edge {
BoundaryEdge::Line(le) => {
result.push(line_entity(
Vector3::new(le.start.x, le.start.y, elevation),
Vector3::new(le.end.x, le.end.y, elevation),
0.0,
normal,
common,
));
}
BoundaryEdge::CircularArc(arc_edge) => {
let (sa, ea) = if arc_edge.counter_clockwise {
(arc_edge.start_angle, arc_edge.end_angle)
} else {
(arc_edge.end_angle, arc_edge.start_angle)
};
result.push(EntityType::Arc(Arc {
common: inherit_common(common),
center: Vector3::new(arc_edge.center.x, arc_edge.center.y, elevation),
radius: arc_edge.radius,
start_angle: normalize_angle(sa),
end_angle: normalize_angle(ea),
thickness: 0.0,
normal,
}));
}
BoundaryEdge::EllipticArc(ea_edge) => {
result.push(EntityType::Ellipse(Ellipse {
common: inherit_common(common),
center: Vector3::new(ea_edge.center.x, ea_edge.center.y, elevation),
major_axis: Vector3::new(
ea_edge.major_axis_endpoint.x,
ea_edge.major_axis_endpoint.y,
0.0,
),
minor_axis_ratio: ea_edge.minor_axis_ratio,
start_parameter: ea_edge.start_angle,
end_parameter: ea_edge.end_angle,
normal,
}));
}
BoundaryEdge::Spline(sp_edge) => {
let control_points: Vec<Vector3> = sp_edge
.control_points
.iter()
.map(|cp| Vector3::new(cp.x, cp.y, elevation))
.collect();
if control_points.len() >= 2 {
let mut spline = Spline::from_control_points(sp_edge.degree, control_points);
spline.common = inherit_common(common);
spline.flags.rational = sp_edge.rational;
spline.flags.periodic = sp_edge.periodic;
spline.knots = sp_edge.knots.clone();
result.push(EntityType::Spline(spline));
}
}
BoundaryEdge::Polyline(pl_edge) => {
let verts = &pl_edge.vertices;
let n = verts.len();
let seg_count = if pl_edge.is_closed { n } else { n.saturating_sub(1) };
for i in 0..seg_count {
let v1 = &verts[i];
let v2 = &verts[(i + 1) % n];
let bulge = v1.z; if bulge.abs() < 1e-10 {
result.push(line_entity(
Vector3::new(v1.x, v1.y, elevation),
Vector3::new(v2.x, v2.y, elevation),
0.0,
normal,
common,
));
} else {
result.push(arc_from_bulge(
v1.x, v1.y, v2.x, v2.y, bulge, elevation, 0.0, normal, common,
));
}
}
}
}
}
}
result
}
fn explode_mesh(mesh: &Mesh) -> Vec<EntityType> {
let common = &mesh.common;
let mut result = Vec::with_capacity(mesh.faces.len());
for face in &mesh.faces {
let n = face.vertices.len();
if n < 3 {
continue;
}
let v = &face.vertices;
let get = |idx: usize| -> Vector3 {
mesh.vertices.get(idx).copied().unwrap_or(Vector3::ZERO)
};
if n == 3 {
result.push(EntityType::Face3D(Face3D::triangle(get(v[0]), get(v[1]), get(v[2]))));
} else {
for i in 1..n - 1 {
let mut face3d = Face3D::triangle(get(v[0]), get(v[i]), get(v[i + 1]));
face3d.common = inherit_common(common);
result.push(EntityType::Face3D(face3d));
}
continue;
}
if let Some(EntityType::Face3D(ref mut f)) = result.last_mut() {
f.common = inherit_common(common);
}
}
result
}
fn explode_polyface_mesh(mesh: &PolyfaceMesh) -> Vec<EntityType> {
let common = &mesh.common;
let mut result = Vec::with_capacity(mesh.faces.len());
let get_vertex = |idx: i16| -> Vector3 {
let abs_idx = idx.unsigned_abs() as usize;
if abs_idx == 0 || abs_idx > mesh.vertices.len() {
Vector3::ZERO
} else {
mesh.vertices[abs_idx - 1].location
}
};
for face in &mesh.faces {
let p1 = get_vertex(face.index1);
let p2 = get_vertex(face.index2);
let p3 = get_vertex(face.index3);
let mut face3d = if face.is_triangle() {
Face3D::triangle(p1, p2, p3)
} else {
let p4 = get_vertex(face.index4);
Face3D::new(p1, p2, p3, p4)
};
if face.index1 < 0 {
face3d.invisible_edges.set_first_invisible(true);
}
if face.index2 < 0 {
face3d.invisible_edges.set_second_invisible(true);
}
if face.index3 < 0 {
face3d.invisible_edges.set_third_invisible(true);
}
if face.index4 < 0 {
face3d.invisible_edges.set_fourth_invisible(true);
}
face3d.common = inherit_common(common);
result.push(EntityType::Face3D(face3d));
}
result
}
fn explode_polygon_mesh(mesh: &PolygonMeshEntity) -> Vec<EntityType> {
let common = &mesh.common;
let m = mesh.m_vertex_count as usize;
let n = mesh.n_vertex_count as usize;
if m < 2 || n < 2 || mesh.vertices.len() < m * n {
return Vec::new();
}
let get = |mi: usize, ni: usize| -> Vector3 {
mesh.vertices[mi * n + ni].location
};
let mut result = Vec::with_capacity((m - 1) * (n - 1));
for i in 0..m - 1 {
for j in 0..n - 1 {
let mut face = Face3D::new(
get(i, j),
get(i, j + 1),
get(i + 1, j + 1),
get(i + 1, j),
);
face.common = inherit_common(common);
result.push(EntityType::Face3D(face));
}
}
result
}
impl EntityType {
pub fn explode(&self) -> Vec<EntityType> {
match self {
EntityType::Circle(e) => explode_circle(e),
EntityType::Ellipse(e) => explode_ellipse(e),
EntityType::LwPolyline(e) => explode_lwpolyline(e),
EntityType::Polyline2D(e) => explode_polyline2d(e),
EntityType::Polyline(e) => explode_polyline3d(e),
EntityType::Polyline3D(e) => explode_polyline3d_new(e),
EntityType::Solid(e) => explode_solid(e),
EntityType::Face3D(e) => explode_face3d(e),
EntityType::Spline(e) => explode_spline(e),
EntityType::MText(e) => explode_mtext(e),
EntityType::Dimension(e) => explode_dimension(e),
EntityType::Leader(e) => explode_leader(e),
EntityType::MultiLeader(e) => explode_multileader(e),
EntityType::MLine(e) => explode_mline(e),
EntityType::Hatch(e) => explode_hatch(e),
EntityType::Mesh(e) => explode_mesh(e),
EntityType::PolyfaceMesh(e) => explode_polyface_mesh(e),
EntityType::PolygonMesh(e) => explode_polygon_mesh(e),
EntityType::Point(_)
| EntityType::Line(_)
| EntityType::Arc(_)
| EntityType::Text(_)
| EntityType::Ray(_)
| EntityType::XLine(_)
| EntityType::Viewport(_)
| EntityType::Block(_)
| EntityType::BlockEnd(_)
| EntityType::Seqend(_)
| EntityType::Insert(_)
| EntityType::AttributeDefinition(_)
| EntityType::AttributeEntity(_)
| EntityType::RasterImage(_)
| EntityType::Solid3D(_)
| EntityType::Region(_)
| EntityType::Body(_)
| EntityType::Table(_)
| EntityType::Tolerance(_)
| EntityType::Wipeout(_)
| EntityType::Shape(_)
| EntityType::Underlay(_)
| EntityType::Ole2Frame(_)
| EntityType::Unknown(_) => Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Vector2;
#[test]
fn test_atomic_entities_return_empty() {
let line = EntityType::Line(Line::from_coords(0.0, 0.0, 0.0, 1.0, 1.0, 0.0));
assert!(line.explode().is_empty());
let arc = EntityType::Arc(Arc::new());
assert!(arc.explode().is_empty());
let point = EntityType::Point(Point::new());
assert!(point.explode().is_empty());
let text = EntityType::Text(Text::new());
assert!(text.explode().is_empty());
}
#[test]
fn test_explode_circle() {
let circle = Circle::from_center_radius(Vector3::new(5.0, 5.0, 0.0), 10.0);
let entity = EntityType::Circle(circle);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
match &parts[0] {
EntityType::Arc(arc) => {
assert_eq!(arc.center, Vector3::new(5.0, 5.0, 0.0));
assert!((arc.radius - 10.0).abs() < 1e-10);
assert!((arc.start_angle - 0.0).abs() < 1e-10);
assert!((arc.end_angle - 2.0 * PI).abs() < 1e-10);
}
_ => panic!("Expected Arc from circle explosion"),
}
}
#[test]
fn test_explode_lwpolyline_lines_only() {
let mut pl = LwPolyline::new();
pl.add_point(Vector2::new(0.0, 0.0));
pl.add_point(Vector2::new(10.0, 0.0));
pl.add_point(Vector2::new(10.0, 10.0));
let entity = EntityType::LwPolyline(pl);
let parts = entity.explode();
assert_eq!(parts.len(), 2);
assert!(matches!(&parts[0], EntityType::Line(_)));
assert!(matches!(&parts[1], EntityType::Line(_)));
}
#[test]
fn test_explode_lwpolyline_closed() {
let mut pl = LwPolyline::new();
pl.add_point(Vector2::new(0.0, 0.0));
pl.add_point(Vector2::new(10.0, 0.0));
pl.add_point(Vector2::new(10.0, 10.0));
pl.close();
let entity = EntityType::LwPolyline(pl);
let parts = entity.explode();
assert_eq!(parts.len(), 3); }
#[test]
fn test_explode_lwpolyline_with_bulge() {
let mut pl = LwPolyline::new();
pl.add_point_with_bulge(Vector2::new(0.0, 0.0), 1.0); pl.add_point(Vector2::new(10.0, 0.0));
let entity = EntityType::LwPolyline(pl);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
assert!(matches!(&parts[0], EntityType::Arc(_)));
}
#[test]
fn test_explode_solid_quad() {
let solid = Solid::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(10.0, 10.0, 0.0),
Vector3::new(0.0, 10.0, 0.0),
);
let entity = EntityType::Solid(solid);
let parts = entity.explode();
assert_eq!(parts.len(), 4); for part in &parts {
assert!(matches!(part, EntityType::Line(_)));
}
}
#[test]
fn test_explode_solid_triangle() {
let solid = Solid::triangle(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(5.0, 10.0, 0.0),
);
let entity = EntityType::Solid(solid);
let parts = entity.explode();
assert_eq!(parts.len(), 3); }
#[test]
fn test_explode_face3d() {
let face = Face3D::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(10.0, 10.0, 0.0),
Vector3::new(0.0, 10.0, 0.0),
);
let entity = EntityType::Face3D(face);
let parts = entity.explode();
assert_eq!(parts.len(), 4); }
#[test]
fn test_explode_face3d_invisible_edges() {
let mut face = Face3D::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(10.0, 10.0, 0.0),
Vector3::new(0.0, 10.0, 0.0),
);
face.invisible_edges.set_first_invisible(true);
face.invisible_edges.set_third_invisible(true);
let entity = EntityType::Face3D(face);
let parts = entity.explode();
assert_eq!(parts.len(), 2); }
#[test]
fn test_explode_polyline3d() {
let pl = Polyline::from_points(vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(10.0, 10.0, 0.0),
]);
let entity = EntityType::Polyline(pl);
let parts = entity.explode();
assert_eq!(parts.len(), 2);
assert!(matches!(&parts[0], EntityType::Line(_)));
}
#[test]
fn test_explode_leader() {
let mut leader = Leader::new();
leader.vertices.push(Vector3::new(0.0, 0.0, 0.0));
leader.vertices.push(Vector3::new(10.0, 10.0, 0.0));
leader.vertices.push(Vector3::new(20.0, 10.0, 0.0));
let entity = EntityType::Leader(leader);
let parts = entity.explode();
assert_eq!(parts.len(), 2);
assert!(matches!(&parts[0], EntityType::Line(_)));
}
#[test]
fn test_explode_mtext() {
let mtext = MText::with_value("Hello World", Vector3::new(5.0, 5.0, 0.0));
let entity = EntityType::MText(mtext);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
match &parts[0] {
EntityType::Text(t) => {
assert_eq!(t.value, "Hello World");
assert_eq!(t.insertion_point, Vector3::new(5.0, 5.0, 0.0));
}
_ => panic!("Expected Text from MText explosion"),
}
}
#[test]
fn test_explode_mesh_triangles() {
let mesh = Mesh::from_triangles(
vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
Vector3::new(5.0, 10.0, 0.0),
],
&[(0, 1, 2)],
);
let entity = EntityType::Mesh(mesh);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
assert!(matches!(&parts[0], EntityType::Face3D(_)));
}
#[test]
fn test_explode_ellipse() {
let ellipse = Ellipse::from_center_axes(
Vector3::ZERO,
Vector3::new(10.0, 0.0, 0.0),
0.5,
);
let entity = EntityType::Ellipse(ellipse);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
assert!(matches!(&parts[0], EntityType::Spline(_)));
}
#[test]
fn test_explode_inherits_common_properties() {
let mut circle = Circle::from_center_radius(Vector3::ZERO, 5.0);
circle.common.layer = "WALLS".to_string();
circle.common.color = Color::Index(1);
circle.common.linetype = "DASHED".to_string();
let entity = EntityType::Circle(circle);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
let common = parts[0].common();
assert_eq!(common.layer, "WALLS");
assert_eq!(common.color, Color::Index(1));
assert_eq!(common.linetype, "DASHED");
assert!(common.handle.is_null()); }
#[test]
fn test_explode_spline() {
let pts = vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(3.0, 10.0, 0.0),
Vector3::new(7.0, 10.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
];
let spline = Spline::from_control_points(3, pts);
let entity = EntityType::Spline(spline);
let parts = entity.explode();
assert_eq!(parts.len(), 3); assert!(matches!(&parts[0], EntityType::Line(_)));
}
#[test]
fn test_explode_dimension() {
let dim = DimensionLinear::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(10.0, 0.0, 0.0),
);
let entity = EntityType::Dimension(Dimension::Linear(dim));
let parts = entity.explode();
assert!(parts.len() >= 2);
assert!(parts.iter().any(|p| matches!(p, EntityType::Text(_))));
assert!(parts.iter().any(|p| matches!(p, EntityType::Line(_))));
}
#[test]
fn test_explode_polyface_mesh() {
let mut mesh = PolyfaceMesh::new();
mesh.add_vertex(PolyfaceVertex::new(Vector3::new(0.0, 0.0, 0.0)));
mesh.add_vertex(PolyfaceVertex::new(Vector3::new(10.0, 0.0, 0.0)));
mesh.add_vertex(PolyfaceVertex::new(Vector3::new(5.0, 10.0, 0.0)));
mesh.add_face(PolyfaceFace::triangle(1, 2, 3));
let entity = EntityType::PolyfaceMesh(mesh);
let parts = entity.explode();
assert_eq!(parts.len(), 1);
assert!(matches!(&parts[0], EntityType::Face3D(_)));
}
#[test]
fn test_document_explode_entity_assigns_handles() {
use crate::document::CadDocument;
let mut doc = CadDocument::new();
let mut circle = Circle::from_center_radius(Vector3::ZERO, 5.0);
circle.common.owner_handle = Handle::new(0x100);
let entity = EntityType::Circle(circle);
let parts = doc.explode_entity(&entity);
assert_eq!(parts.len(), 1);
assert!(parts[0].common().handle.is_valid());
assert_eq!(parts[0].common().owner_handle, Handle::new(0x100));
}
}