use std::io::Write;
use crate::cad::svg_reader::{Point2D, SvgElement, parse_svg_elements};
use crate::error::Result;
#[derive(Clone, Copy, Debug)]
struct LineSeg {
p1: Point2D,
p2: Point2D,
}
pub fn write_svg_to_step<W: Write>(svg_content: &str, mut writer: W) -> Result<()> {
let doc = parse_svg_elements(svg_content)?;
let mut segments: Vec<LineSeg> = Vec::new();
for elem in doc.elements {
match elem {
SvgElement::Line { p1, p2, .. } => {
if p1.x.is_finite() && p1.y.is_finite() && p2.x.is_finite() && p2.y.is_finite() {
segments.push(LineSeg { p1, p2 });
}
}
SvgElement::Rect {
x,
y,
width,
height,
..
} => {
if x.is_finite()
&& y.is_finite()
&& width.is_finite()
&& height.is_finite()
&& width > 0.0
&& height > 0.0
{
let p1 = Point2D::new(x, y);
let p2 = Point2D::new(x + width, y);
let p3 = Point2D::new(x + width, y + height);
let p4 = Point2D::new(x, y + height);
segments.push(LineSeg { p1, p2 });
segments.push(LineSeg { p1: p2, p2: p3 });
segments.push(LineSeg { p1: p3, p2: p4 });
segments.push(LineSeg { p1: p4, p2: p1 });
}
}
SvgElement::Circle { center, radius, .. } => {
if center.x.is_finite()
&& center.y.is_finite()
&& radius.is_finite()
&& radius > 0.0
{
let segments_count = 32;
let mut pts = Vec::with_capacity(segments_count);
for i in 0..segments_count {
let theta = (i as f64) * std::f64::consts::TAU / (segments_count as f64);
pts.push(Point2D::new(
center.x + radius * theta.cos(),
center.y + radius * theta.sin(),
));
}
for i in 0..segments_count {
segments.push(LineSeg {
p1: pts[i],
p2: pts[(i + 1) % segments_count],
});
}
}
}
SvgElement::Polyline {
points, is_closed, ..
} => {
let valid_pts: Vec<_> = points
.into_iter()
.filter(|p| p.x.is_finite() && p.y.is_finite())
.collect();
if valid_pts.len() >= 2 {
for w in valid_pts.windows(2) {
segments.push(LineSeg { p1: w[0], p2: w[1] });
}
if is_closed
&& valid_pts.len() >= 3
&& let (Some(&first), Some(&last)) = (valid_pts.first(), valid_pts.last())
{
segments.push(LineSeg {
p1: last,
p2: first,
});
}
}
}
SvgElement::Text { .. } => {}
}
}
if segments.is_empty() {
let p1 = Point2D::new(0.0, 0.0);
let p2 = Point2D::new(100.0, 0.0);
let p3 = Point2D::new(100.0, 100.0);
let p4 = Point2D::new(0.0, 100.0);
segments.push(LineSeg { p1, p2 });
segments.push(LineSeg { p1: p2, p2: p3 });
segments.push(LineSeg { p1: p3, p2: p4 });
segments.push(LineSeg { p1: p4, p2: p1 });
}
writeln!(writer, "ISO-10303-21;")?;
writeln!(writer, "HEADER;")?;
writeln!(
writer,
"FILE_DESCRIPTION(('document-svg generated ISO 10303-21 wireframe model'),'2;1');"
)?;
writeln!(
writer,
"FILE_NAME('model.step','2026-09-11T00:00:00',('document-svg'),('CAD'),'document-svg STEP Generator','document-svg','');"
)?;
writeln!(writer, "FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));")?;
writeln!(writer, "ENDSEC;")?;
writeln!(writer, "DATA;")?;
let mut id_seq: u64 = 1;
for seg in &segments {
let dx = seg.p2.x - seg.p1.x;
let dy = seg.p2.y - seg.p1.y;
let len = (dx * dx + dy * dy).sqrt();
let (dir_x, dir_y) = if len > 1e-9 {
(dx / len, dy / len)
} else {
(1.0, 0.0)
};
let pt1_id = id_seq;
writeln!(
writer,
"#{pt1_id} = CARTESIAN_POINT('', ({:.4}, {:.4}, 0.0));",
seg.p1.x, seg.p1.y
)?;
id_seq += 1;
let pt2_id = id_seq;
writeln!(
writer,
"#{pt2_id} = CARTESIAN_POINT('', ({:.4}, {:.4}, 0.0));",
seg.p2.x, seg.p2.y
)?;
id_seq += 1;
let v1_id = id_seq;
writeln!(writer, "#{v1_id} = VERTEX_POINT('', #{pt1_id});")?;
id_seq += 1;
let v2_id = id_seq;
writeln!(writer, "#{v2_id} = VERTEX_POINT('', #{pt2_id});")?;
id_seq += 1;
let dir_id = id_seq;
writeln!(
writer,
"#{dir_id} = DIRECTION('', ({:.6}, {:.6}, 0.0));",
dir_x, dir_y
)?;
id_seq += 1;
let vec_id = id_seq;
writeln!(
writer,
"#{vec_id} = VECTOR('', #{dir_id}, {:.4});",
len.max(1e-4)
)?;
id_seq += 1;
let line_id = id_seq;
writeln!(writer, "#{line_id} = LINE('', #{pt1_id}, #{vec_id});")?;
id_seq += 1;
let edge_id = id_seq;
writeln!(
writer,
"#{edge_id} = EDGE_CURVE('', #{v1_id}, #{v2_id}, #{line_id}, .T.);"
)?;
id_seq += 1;
}
writeln!(writer, "ENDSEC;")?;
writeln!(writer, "END-ISO-10303-21;")?;
Ok(())
}