1use bon::Builder;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::shared::{FileExportFormat, FileExportFormat2d, FileImportFormat};
6
7pub mod dxf;
9pub mod fbx;
11pub mod gltf;
16pub mod obj;
18pub mod ply;
20pub mod step;
22pub mod stl;
24
25pub mod sldprt;
27
28#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
30#[serde(tag = "type", rename_all = "snake_case")]
31#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
32#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
33#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
34#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
35pub enum OutputFormat2d {
36 Dxf(dxf::export::Options),
38}
39
40#[deprecated(since = "0.2.96", note = "use `OutputFormat3d` instead")]
42pub type OutputFormat = OutputFormat3d;
43
44#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
46#[serde(tag = "type", rename_all = "snake_case")]
47#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
48#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
49#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
50#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
51pub enum OutputFormat3d {
52 Fbx(fbx::export::Options),
54 Gltf(gltf::export::Options),
59 Obj(obj::export::Options),
61 Ply(ply::export::Options),
63 Step(step::export::Options),
65 Stl(stl::export::Options),
67}
68
69#[deprecated(since = "0.2.96", note = "use `InputFormat3d` instead")]
71pub type InputFormat = InputFormat3d;
72
73#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
75#[serde(tag = "type", rename_all = "snake_case")]
76#[cfg_attr(
77 feature = "python",
78 pyo3::pyclass,
79 pyo3_stub_gen::derive::gen_stub_pyclass_complex_enum
80)]
81#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
82#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
83#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
84#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
85pub enum InputFormat3d {
86 Fbx(fbx::import::Options),
88 Gltf(gltf::import::Options),
92 Obj(obj::import::Options),
94 Ply(ply::import::Options),
96 Sldprt(sldprt::import::Options),
98 Step(step::import::Options),
100 Stl(stl::import::Options),
102}
103
104impl InputFormat3d {
105 pub fn name(&self) -> &'static str {
107 match self {
108 InputFormat3d::Fbx(_) => "fbx",
109 InputFormat3d::Gltf(_) => "gltf",
110 InputFormat3d::Obj(_) => "obj",
111 InputFormat3d::Ply(_) => "ply",
112 InputFormat3d::Sldprt(_) => "sldprt",
113 InputFormat3d::Step(_) => "step",
114 InputFormat3d::Stl(_) => "stl",
115 }
116 }
117}
118
119#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, JsonSchema, Deserialize, Serialize)]
121#[serde(rename_all = "snake_case", tag = "type")]
122#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
123#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
124#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
125#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
126pub enum Selection {
127 #[default]
129 DefaultScene,
130
131 SceneByIndex {
133 index: usize,
135 },
136
137 SceneByName {
139 name: String,
141 },
142
143 MeshByIndex {
145 index: usize,
147 },
148
149 MeshByName {
151 name: String,
153 },
154}
155
156#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Builder)]
158#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
159pub struct VirtualFile {
160 pub path: std::path::PathBuf,
162 pub data: Vec<u8>,
164}
165
166impl VirtualFile {
167 pub fn has_extension(&self, required_extension: &str) -> bool {
169 self.path
170 .extension()
171 .and_then(std::ffi::OsStr::to_str)
172 .map(|extension| extension.eq_ignore_ascii_case(required_extension))
173 .unwrap_or(false)
174 }
175
176 fn read_fs_impl(path: std::path::PathBuf) -> std::io::Result<Self> {
177 let data = std::fs::read(&path)?;
178 Ok(Self { path, data })
179 }
180
181 pub fn read_fs<P>(path: P) -> std::io::Result<Self>
183 where
184 P: Into<std::path::PathBuf>,
185 {
186 Self::read_fs_impl(path.into())
187 }
188}
189
190impl From<OutputFormat3d> for FileExportFormat {
191 fn from(output_format: OutputFormat3d) -> Self {
192 match output_format {
193 OutputFormat3d::Fbx(_) => Self::Fbx,
194 OutputFormat3d::Gltf(_) => Self::Gltf,
195 OutputFormat3d::Obj(_) => Self::Obj,
196 OutputFormat3d::Ply(_) => Self::Ply,
197 OutputFormat3d::Step(_) => Self::Step,
198 OutputFormat3d::Stl(_) => Self::Stl,
199 }
200 }
201}
202
203impl From<OutputFormat2d> for FileExportFormat2d {
204 fn from(output_format: OutputFormat2d) -> Self {
205 match output_format {
206 OutputFormat2d::Dxf(_) => Self::Dxf,
207 }
208 }
209}
210
211impl From<FileExportFormat2d> for OutputFormat2d {
212 fn from(export_format: FileExportFormat2d) -> Self {
213 match export_format {
214 FileExportFormat2d::Dxf => OutputFormat2d::Dxf(Default::default()),
215 }
216 }
217}
218
219impl From<FileExportFormat> for OutputFormat3d {
220 fn from(export_format: FileExportFormat) -> Self {
221 match export_format {
222 FileExportFormat::Fbx => OutputFormat3d::Fbx(Default::default()),
223 FileExportFormat::Glb => OutputFormat3d::Gltf(gltf::export::Options {
224 storage: gltf::export::Storage::Binary,
225 ..Default::default()
226 }),
227 FileExportFormat::Gltf => OutputFormat3d::Gltf(gltf::export::Options {
228 storage: gltf::export::Storage::Embedded,
229 presentation: gltf::export::Presentation::Pretty,
230 }),
231 FileExportFormat::Obj => OutputFormat3d::Obj(Default::default()),
232 FileExportFormat::Ply => OutputFormat3d::Ply(Default::default()),
233 FileExportFormat::Step => OutputFormat3d::Step(Default::default()),
234 FileExportFormat::Stl => OutputFormat3d::Stl(stl::export::Options {
235 storage: stl::export::Storage::Ascii,
236 ..Default::default()
237 }),
238 }
239 }
240}
241
242impl From<InputFormat3d> for FileImportFormat {
243 fn from(input_format: InputFormat3d) -> Self {
244 match input_format {
245 InputFormat3d::Fbx(_) => Self::Fbx,
246 InputFormat3d::Gltf(_) => Self::Gltf,
247 InputFormat3d::Obj(_) => Self::Obj,
248 InputFormat3d::Ply(_) => Self::Ply,
249 InputFormat3d::Sldprt(_) => Self::Sldprt,
250 InputFormat3d::Step(_) => Self::Step,
251 InputFormat3d::Stl(_) => Self::Stl,
252 }
253 }
254}
255
256impl From<FileImportFormat> for InputFormat3d {
257 fn from(import_format: FileImportFormat) -> Self {
258 match import_format {
259 FileImportFormat::Fbx => InputFormat3d::Fbx(Default::default()),
260 FileImportFormat::Gltf => InputFormat3d::Gltf(Default::default()),
261 FileImportFormat::Obj => InputFormat3d::Obj(Default::default()),
262 FileImportFormat::Ply => InputFormat3d::Ply(Default::default()),
263 FileImportFormat::Sldprt => InputFormat3d::Sldprt(Default::default()),
264 FileImportFormat::Step => InputFormat3d::Step(Default::default()),
265 FileImportFormat::Stl => InputFormat3d::Stl(Default::default()),
266 }
267 }
268}
269
270pub struct OutputFormat3dOptions {
272 src_unit: crate::units::UnitLength,
273}
274
275impl OutputFormat3dOptions {
276 pub fn new(src_unit: crate::units::UnitLength) -> Self {
278 Self { src_unit }
279 }
280}
281
282impl OutputFormat3d {
283 pub fn new(format: &FileExportFormat, options: OutputFormat3dOptions) -> Self {
285 let OutputFormat3dOptions { src_unit } = options;
286 let coords = crate::coord::System {
292 forward: crate::coord::AxisDirectionPair {
293 axis: crate::coord::Axis::Y,
294 direction: crate::coord::Direction::Negative,
295 },
296 up: crate::coord::AxisDirectionPair {
297 axis: crate::coord::Axis::Z,
298 direction: crate::coord::Direction::Positive,
299 },
300 };
301
302 match format {
303 FileExportFormat::Fbx => Self::Fbx(fbx::export::Options {
304 storage: fbx::export::Storage::Binary,
305 created: None,
306 }),
307 FileExportFormat::Glb => Self::Gltf(gltf::export::Options {
308 storage: gltf::export::Storage::Binary,
309 presentation: gltf::export::Presentation::Compact,
310 }),
311 FileExportFormat::Gltf => Self::Gltf(gltf::export::Options {
312 storage: gltf::export::Storage::Embedded,
313 presentation: gltf::export::Presentation::Pretty,
314 }),
315 FileExportFormat::Obj => Self::Obj(obj::export::Options {
316 coords,
317 units: src_unit,
318 }),
319 FileExportFormat::Ply => Self::Ply(ply::export::Options {
320 storage: ply::export::Storage::Ascii,
321 coords,
322 selection: Selection::DefaultScene,
323 units: src_unit,
324 }),
325 FileExportFormat::Step => Self::Step(step::export::Options {
326 coords,
327 created: None,
328 units: src_unit,
329 presentation: step::export::Presentation::Pretty,
330 }),
331 FileExportFormat::Stl => Self::Stl(stl::export::Options {
332 storage: stl::export::Storage::Ascii,
333 coords,
334 units: src_unit,
335 selection: Selection::DefaultScene,
336 }),
337 }
338 }
339}