lib3mf_core/writer/
slice_writer.rs1use crate::error::{Lib3mfError, Result};
2use crate::model::{Polygon, Segment, Slice, SliceRef, SliceStack, Vertex2D};
3use crate::writer::xml_writer::XmlWriter;
4use std::io::Write;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum SliceMode {
17 #[default]
18 PreserveOriginal,
19 Inline,
20 External,
21}
22
23pub struct SliceWriteOptions {
25 pub slice_mode: SliceMode,
27 pub strict: bool,
29}
30
31impl Default for SliceWriteOptions {
32 fn default() -> Self {
33 Self {
34 slice_mode: SliceMode::PreserveOriginal,
35 strict: false,
36 }
37 }
38}
39
40pub fn write_slice_stack<W: Write>(
53 writer: &mut XmlWriter<W>,
54 stack: &SliceStack,
55 opts: &SliceWriteOptions,
56) -> Result<()> {
57 match opts.slice_mode {
59 SliceMode::PreserveOriginal => {}
60 SliceMode::Inline => {
61 return Err(Lib3mfError::Validation(
62 "SliceMode::Inline is not yet implemented".to_string(),
63 ));
64 }
65 SliceMode::External => {
66 return Err(Lib3mfError::Validation(
67 "SliceMode::External is not yet implemented".to_string(),
68 ));
69 }
70 }
71
72 if stack.slices.is_empty() && stack.refs.is_empty() {
74 eprintln!(
75 "Warning: slice stack id={} has no slices and no slicerefs",
76 stack.id.0
77 );
78 if opts.strict {
79 return Err(Lib3mfError::Validation(format!(
80 "Empty slice stack id={}",
81 stack.id.0
82 )));
83 }
84 }
85
86 writer
87 .start_element("slicestack")
88 .attr("id", &stack.id.0.to_string())
89 .attr("zbottom", &stack.z_bottom.to_string())
90 .write_start()?;
91
92 for slice in &stack.slices {
94 write_slice(writer, slice)?;
95 }
96
97 for sref in &stack.refs {
99 write_sliceref(writer, sref)?;
100 }
101
102 writer.end_element("slicestack")?;
103 Ok(())
104}
105
106fn write_slice<W: Write>(writer: &mut XmlWriter<W>, slice: &Slice) -> Result<()> {
111 if !slice.vertices.is_empty() && slice.polygons.is_empty() {
113 eprintln!(
114 "Warning: slice ztop={} has {} vertices but no polygons, skipping",
115 slice.z_top,
116 slice.vertices.len()
117 );
118 return Ok(());
119 }
120
121 writer
122 .start_element("slice")
123 .attr("ztop", &slice.z_top.to_string())
124 .write_start()?;
125
126 if !slice.vertices.is_empty() {
128 writer.start_element("vertices").write_start()?;
129 for v in &slice.vertices {
130 write_vertex(writer, v)?;
131 }
132 writer.end_element("vertices")?;
133 }
134
135 for polygon in &slice.polygons {
137 write_polygon(writer, polygon)?;
138 }
139
140 writer.end_element("slice")?;
141 Ok(())
142}
143
144fn write_vertex<W: Write>(writer: &mut XmlWriter<W>, v: &Vertex2D) -> Result<()> {
146 writer
147 .start_element("vertex")
148 .attr("x", &v.x.to_string())
149 .attr("y", &v.y.to_string())
150 .write_empty()?;
151 Ok(())
152}
153
154fn write_polygon<W: Write>(writer: &mut XmlWriter<W>, polygon: &Polygon) -> Result<()> {
159 if polygon.segments.is_empty() {
160 eprintln!(
161 "Warning: polygon start={} has zero segments (degenerate)",
162 polygon.start_segment
163 );
164 }
165
166 writer
167 .start_element("polygon")
168 .attr("start", &polygon.start_segment.to_string())
169 .write_start()?;
170
171 for segment in &polygon.segments {
172 write_segment(writer, segment)?;
173 }
174
175 writer.end_element("polygon")?;
176 Ok(())
177}
178
179fn write_segment<W: Write>(writer: &mut XmlWriter<W>, segment: &Segment) -> Result<()> {
181 let mut elem = writer
182 .start_element("segment")
183 .attr("v2", &segment.v2.to_string());
184
185 if let Some(p1) = segment.p1 {
186 elem = elem.attr("p1", &p1.to_string());
187 }
188 if let Some(p2) = segment.p2 {
189 elem = elem.attr("p2", &p2.to_string());
190 }
191 if let Some(pid) = segment.pid {
192 elem = elem.attr("pid", &pid.0.to_string());
193 }
194
195 elem.write_empty()?;
196 Ok(())
197}
198
199fn write_sliceref<W: Write>(writer: &mut XmlWriter<W>, sref: &SliceRef) -> Result<()> {
201 writer
202 .start_element("sliceref")
203 .attr("slicestackid", &sref.slice_stack_id.0.to_string())
204 .attr("slicepath", &sref.slice_path)
205 .write_empty()?;
206 Ok(())
207}