Export the current 3D view as 2D DXF with optional hidden lines (#1449)
Adds File > Export > Current View as DXF: the visible bodies projected from the current camera onto 2D LINE/LWPOLYLINE entities, with hidden edges omitted or dashed. Co-authored-by: cronos <19868002+crono2250@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
20 files changed
+798
-3
No files matched your search
@@ -22,6 +22,8 @@ pub enum DxfLinetype {
|
||||
Continuous,
|
||||
/// AutoCAD's DASHED: a dash of 12.7 mm and a gap of 6.35 mm.
|
||||
Dashed,
|
||||
/// Short 2 mm dashes and 1 mm gaps for hidden edges in millimetre drawings.
|
||||
Hidden,
|
||||
}
|
||||
|
||||
impl DxfLinetype {
|
||||
@@ -29,6 +31,7 @@ impl DxfLinetype {
|
||||
match self {
|
||||
DxfLinetype::Continuous => "Continuous",
|
||||
DxfLinetype::Dashed => "DASHED",
|
||||
DxfLinetype::Hidden => "HIDDEN",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +60,10 @@ impl DxfLayer {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DxfShape {
|
||||
Line(Vec2, Vec2),
|
||||
/// Connected straight segments, with continuous linetype generation through the vertices.
|
||||
Polyline {
|
||||
points: Vec<Vec2>,
|
||||
},
|
||||
Circle {
|
||||
c: Vec2,
|
||||
r: f64,
|
||||
@@ -207,6 +214,7 @@ impl DxfShape {
|
||||
fn sample(&self, out: &mut Vec<Vec2>) {
|
||||
match self {
|
||||
DxfShape::Line(a, b) => out.extend([*a, *b]),
|
||||
DxfShape::Polyline { points } => out.extend(points.iter().copied()),
|
||||
DxfShape::Circle { c, r } => out.extend([*c - Vec2::new(*r, *r), *c + Vec2::new(*r, *r)]),
|
||||
DxfShape::Arc { c, r, a0, a1 } => {
|
||||
let sweep = (a1 - a0).rem_euclid(360.0);
|
||||
@@ -348,12 +356,15 @@ pub fn write_dxf_drawing(layers: &[DxfLayer], entities: &[DxfEntity]) -> String
|
||||
o.code(2, "TABLES");
|
||||
// The three every drawing has, and DASHED, which the dashed layers name: dash and gap lengths
|
||||
// (a gap is negative) after the pattern's total.
|
||||
let linetypes: [(&str, &str, &[f64]); 4] = [
|
||||
let mut linetypes: Vec<(&str, &str, &[f64])> = vec![
|
||||
("ByBlock", "", &[]),
|
||||
("ByLayer", "", &[]),
|
||||
("Continuous", "Solid line", &[]),
|
||||
("DASHED", "Dashed __ __ __ __ __ __ __ __ __ __", &[12.7, -6.35]),
|
||||
];
|
||||
if layer_list.iter().any(|l| l.linetype == DxfLinetype::Hidden) {
|
||||
linetypes.push(("HIDDEN", "Hidden edges", &[2.0, -1.0]));
|
||||
}
|
||||
let t = o.table("LTYPE", linetypes.len());
|
||||
for (name, description, pattern) in linetypes {
|
||||
o.record("LTYPE", "AcDbLinetypeTableRecord", &t);
|
||||
@@ -414,6 +425,7 @@ pub fn write_dxf_drawing(layers: &[DxfLayer], entities: &[DxfEntity]) -> String
|
||||
for e in entities {
|
||||
let kind = match &e.shape {
|
||||
DxfShape::Line(..) => "LINE",
|
||||
DxfShape::Polyline { .. } => "LWPOLYLINE",
|
||||
DxfShape::Circle { .. } => "CIRCLE",
|
||||
DxfShape::Arc { .. } => "ARC",
|
||||
DxfShape::Ellipse { .. } => "ELLIPSE",
|
||||
@@ -427,6 +439,16 @@ pub fn write_dxf_drawing(layers: &[DxfLayer], entities: &[DxfEntity]) -> String
|
||||
o.code(100, "AcDbEntity");
|
||||
o.code(8, if e.layer.trim().is_empty() { "0" } else { e.layer.as_str() });
|
||||
match &e.shape {
|
||||
DxfShape::Polyline { points } => {
|
||||
o.code(100, "AcDbPolyline");
|
||||
o.code(90, points.len());
|
||||
// Autodesk DXF reference: bit 128 (PLINEGEN) continues the dash pattern.
|
||||
o.code(70, 128);
|
||||
for p in points {
|
||||
o.num(10, p.x);
|
||||
o.num(20, p.y);
|
||||
}
|
||||
}
|
||||
DxfShape::Line(a, b) => {
|
||||
o.code(100, "AcDbLine");
|
||||
o.point(10, *a);
|
||||
@@ -811,6 +833,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hidden_polylines_continue_short_dashes_across_chords_and_round_trip() {
|
||||
let points = vec![Vec2::ZERO, Vec2::new(1., 0.), Vec2::new(2., 1.), Vec2::new(3., 1.)];
|
||||
let layer = DxfLayer { linetype: DxfLinetype::Hidden, ..DxfLayer::new("HIDDEN", 8) };
|
||||
let text = write_dxf_drawing(&[layer], &[DxfEntity { layer: "HIDDEN".into(), shape: DxfShape::Polyline { points } }]);
|
||||
let records = entities(&text);
|
||||
assert_eq!(records[0].0, "LWPOLYLINE");
|
||||
assert_eq!(field(&records[0].1, 70), "128");
|
||||
assert_eq!(field(&records[0].1, 90), "4");
|
||||
let types = table_records(&pairs(&text), "LTYPE");
|
||||
let hidden = types.iter().find(|r| field(r, 2) == "HIDDEN").unwrap();
|
||||
assert_eq!(hidden.iter().filter(|(c, _)| *c == 49).map(|(_, v)| v.parse::<f64>().unwrap()).collect::<Vec<_>>(), [2., -1.]);
|
||||
assert_eq!(read_dxf(&text).unwrap().len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_is_centred_on_its_point_and_the_reader_gets_it_back() {
|
||||
let at = Vec2::new(30.0, 12.0);
|
||||
|
||||
Reference in new issue
Block a user