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]
19 PreserveOriginal,
20 Inline,
22 External,
24}
25
26pub struct SliceWriteOptions {
28 pub slice_mode: SliceMode,
30 pub strict: bool,
32}
33
34impl Default for SliceWriteOptions {
35 fn default() -> Self {
36 Self {
37 slice_mode: SliceMode::PreserveOriginal,
38 strict: false,
39 }
40 }
41}
42
43pub fn write_slice_stack<W: Write>(
56 writer: &mut XmlWriter<W>,
57 stack: &SliceStack,
58 opts: &SliceWriteOptions,
59) -> Result<()> {
60 match opts.slice_mode {
62 SliceMode::PreserveOriginal => {}
63 SliceMode::Inline => {
64 return Err(Lib3mfError::Validation(
65 "SliceMode::Inline is not yet implemented".to_string(),
66 ));
67 }
68 SliceMode::External => {
69 return Err(Lib3mfError::Validation(
70 "SliceMode::External is not yet implemented".to_string(),
71 ));
72 }
73 }
74
75 if stack.slices.is_empty() && stack.refs.is_empty() {
77 eprintln!(
78 "Warning: slice stack id={} has no slices and no slicerefs",
79 stack.id.0
80 );
81 if opts.strict {
82 return Err(Lib3mfError::Validation(format!(
83 "Empty slice stack id={}",
84 stack.id.0
85 )));
86 }
87 }
88
89 writer
90 .start_element("slicestack")
91 .attr("id", &stack.id.0.to_string())
92 .attr("zbottom", &stack.z_bottom.to_string())
93 .write_start()?;
94
95 for slice in &stack.slices {
97 write_slice(writer, slice)?;
98 }
99
100 for sref in &stack.refs {
102 write_sliceref(writer, sref)?;
103 }
104
105 writer.end_element("slicestack")?;
106 Ok(())
107}
108
109fn write_slice<W: Write>(writer: &mut XmlWriter<W>, slice: &Slice) -> Result<()> {
114 if !slice.vertices.is_empty() && slice.polygons.is_empty() {
116 eprintln!(
117 "Warning: slice ztop={} has {} vertices but no polygons, skipping",
118 slice.z_top,
119 slice.vertices.len()
120 );
121 return Ok(());
122 }
123
124 writer
125 .start_element("slice")
126 .attr("ztop", &slice.z_top.to_string())
127 .write_start()?;
128
129 if !slice.vertices.is_empty() {
131 writer.start_element("vertices").write_start()?;
132 for v in &slice.vertices {
133 write_vertex(writer, v)?;
134 }
135 writer.end_element("vertices")?;
136 }
137
138 for polygon in &slice.polygons {
140 write_polygon(writer, polygon)?;
141 }
142
143 writer.end_element("slice")?;
144 Ok(())
145}
146
147fn write_vertex<W: Write>(writer: &mut XmlWriter<W>, v: &Vertex2D) -> Result<()> {
149 writer
150 .start_element("vertex")
151 .attr("x", &v.x.to_string())
152 .attr("y", &v.y.to_string())
153 .write_empty()?;
154 Ok(())
155}
156
157fn write_polygon<W: Write>(writer: &mut XmlWriter<W>, polygon: &Polygon) -> Result<()> {
162 if polygon.segments.is_empty() {
163 eprintln!(
164 "Warning: polygon start={} has zero segments (degenerate)",
165 polygon.start_segment
166 );
167 }
168
169 writer
170 .start_element("polygon")
171 .attr("start", &polygon.start_segment.to_string())
172 .write_start()?;
173
174 for segment in &polygon.segments {
175 write_segment(writer, segment)?;
176 }
177
178 writer.end_element("polygon")?;
179 Ok(())
180}
181
182fn write_segment<W: Write>(writer: &mut XmlWriter<W>, segment: &Segment) -> Result<()> {
184 let mut elem = writer
185 .start_element("segment")
186 .attr("v2", &segment.v2.to_string());
187
188 if let Some(p1) = segment.p1 {
189 elem = elem.attr("p1", &p1.to_string());
190 }
191 if let Some(p2) = segment.p2 {
192 elem = elem.attr("p2", &p2.to_string());
193 }
194 if let Some(pid) = segment.pid {
195 elem = elem.attr("pid", &pid.0.to_string());
196 }
197
198 elem.write_empty()?;
199 Ok(())
200}
201
202fn write_sliceref<W: Write>(writer: &mut XmlWriter<W>, sref: &SliceRef) -> Result<()> {
204 writer
205 .start_element("sliceref")
206 .attr("slicestackid", &sref.slice_stack_id.0.to_string())
207 .attr("slicepath", &sref.slice_path)
208 .write_empty()?;
209 Ok(())
210}