1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use crate::float_types::Real;
use crate::mesh::Mesh;
use crate::mesh::polygon::Polygon;
use crate::mesh::vertex::Vertex;
use crate::sketch::Sketch;
use geo::{Polygon as GeoPolygon, line_string};
use nalgebra::{Point3, Vector3};
use std::error::Error;
use std::fmt::Debug;
#[cfg(any(feature = "stl-io", feature = "dxf-io"))]
use core2::io::Cursor;
#[cfg(feature = "dxf-io")]
use dxf::Drawing;
#[cfg(feature = "dxf-io")]
use dxf::entities::*;
impl<S: Clone + Debug + Send + Sync> Mesh<S> {
/// Import a Mesh object from DXF data.
///
/// ## Parameters
/// - `dxf_data`: A byte slice containing the DXF file data.
/// - `metadata`: metadata that will be attached to all polygons of the resulting `Sketch`
///
/// ## Returns
/// A `Result` containing the Mesh object or an error if parsing fails.
#[cfg(feature = "dxf-io")]
pub fn from_dxf(dxf_data: &[u8], metadata: Option<S>) -> Result<Mesh<S>, Box<dyn Error>> {
// Load the DXF drawing from the provided data
let drawing = Drawing::load(&mut Cursor::new(dxf_data))?;
let mut polygons = Vec::new();
for entity in drawing.entities() {
match &entity.specific {
EntityType::Line(_line) => {
// Convert a line to a thin rectangular polygon (optional)
// Alternatively, skip lines if they don't form closed loops
// Here, we'll skip standalone lines
// To form polygons from lines, you'd need to group connected lines into loops
},
EntityType::Polyline(polyline) => {
// Handle POLYLINE entities (which can be 2D or 3D)
if polyline.is_closed() {
let mut verts = Vec::new();
for vertex in polyline.vertices() {
verts.push(Vertex::new(
Point3::new(
vertex.location.x as Real,
vertex.location.y as Real,
vertex.location.z as Real,
),
Vector3::z(), // Assuming flat in XY
));
}
// Create a polygon from the polyline vertices
if verts.len() >= 3 {
polygons.push(Polygon::new(verts, None));
}
}
},
EntityType::Circle(circle) => {
// Approximate circles with regular polygons
let center = Point3::new(
circle.center.x as Real,
circle.center.y as Real,
circle.center.z as Real,
);
let radius = circle.radius as Real;
// FIXME: this seems a bit low maybe make it relative to the radius
let segments = 32; // Number of segments to approximate the circle
let mut verts = Vec::with_capacity(segments + 1);
let normal = Vector3::new(
circle.normal.x as Real,
circle.normal.y as Real,
circle.normal.z as Real,
)
.normalize();
for i in 0..segments {
let theta =
2.0 * crate::float_types::PI * (i as Real) / (segments as Real);
let x = center.x as Real + radius * theta.cos();
let y = center.y as Real + radius * theta.sin();
let z = center.z as Real;
verts.push(Vertex::new(Point3::new(x, y, z), normal));
}
// Create a polygon from the approximated circle vertices
polygons.push(Polygon::new(verts, metadata.clone()));
},
EntityType::Solid(solid) => {
let thickness = solid.thickness as Real;
let extrusion_direction = Vector3::new(
solid.extrusion_direction.x as Real,
solid.extrusion_direction.y as Real,
solid.extrusion_direction.z as Real,
);
let extruded = Sketch::from_geo(
GeoPolygon::new(line_string![
(x: solid.first_corner.x as Real, y: solid.first_corner.y as Real),
(x: solid.second_corner.x as Real, y: solid.second_corner.y as Real),
(x: solid.third_corner.x as Real, y: solid.third_corner.y as Real),
(x: solid.fourth_corner.x as Real, y: solid.fourth_corner.y as Real),
(x: solid.first_corner.x as Real, y: solid.first_corner.y as Real),
], Vec::new()).into(),
None,
)
.extrude_vector(extrusion_direction * thickness).polygons;
polygons.extend(extruded);
},
// todo convert image to work with `from_image`
// EntityType::Image(image) => {}
// todo convert image to work with `text`, also try using system fonts for a better chance of having the font
// EntityType::Text(text) => {}
// Handle other entity types as needed (e.g., Line, Spline)
_ => {
// Ignore unsupported entity types for now
},
}
}
Ok(Mesh::from_polygons(&polygons, metadata))
}
/// Export the Mesh object to DXF format.
///
/// # Returns
///
/// A `Result` containing the DXF file as a byte vector or an error if exporting fails.
#[cfg(feature = "dxf-io")]
pub fn to_dxf(&self) -> Result<Vec<u8>, Box<dyn Error>> {
let mut drawing = Drawing::new();
for poly in &self.polygons {
// Triangulate the polygon if it has more than 3 vertices
let triangles = if poly.vertices.len() > 3 {
poly.triangulate()
} else {
vec![[
poly.vertices[0].clone(),
poly.vertices[1].clone(),
poly.vertices[2].clone(),
]]
};
for tri in triangles {
// Create a 3DFACE entity for each triangle
#[allow(clippy::unnecessary_cast)]
let face = dxf::entities::Face3D::new(
// 3DFACE expects four vertices, but for triangles, the fourth is the same as the third
dxf::Point::new(
tri[0].pos.x as f64,
tri[0].pos.y as f64,
tri[0].pos.z as f64,
),
dxf::Point::new(
tri[1].pos.x as f64,
tri[1].pos.y as f64,
tri[1].pos.z as f64,
),
dxf::Point::new(
tri[2].pos.x as f64,
tri[2].pos.y as f64,
tri[2].pos.z as f64,
),
dxf::Point::new(
tri[2].pos.x as f64,
tri[2].pos.y as f64,
tri[2].pos.z as f64,
), // Duplicate for triangular face
);
let entity =
dxf::entities::Entity::new(dxf::entities::EntityType::Face3D(face));
// Add the 3DFACE entity to the drawing
drawing.add_entity(entity);
}
}
// Serialize the DXF drawing to bytes
let mut buffer = Vec::new();
drawing.save(&mut buffer)?;
Ok(buffer)
}
}