kcl_lib/std/
import.rs

1//! Standard library functions involved in importing files.
2
3use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kcmc::{coord::System, format::InputFormat3d, units::UnitLength};
6use kittycad_modeling_cmds as kcmc;
7
8use crate::{
9    errors::{KclError, KclErrorDetails},
10    execution::{import_foreign, send_import_to_engine, ExecState, ImportedGeometry, KclValue, ZOO_COORD_SYSTEM},
11    std::Args,
12};
13
14/// Import format specifier
15#[derive(serde :: Serialize, serde :: Deserialize, PartialEq, Debug, Clone, schemars :: JsonSchema)]
16#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
17#[serde(tag = "format")]
18pub enum ImportFormat {
19    /// Autodesk Filmbox (FBX) format
20    #[serde(rename = "fbx")]
21    Fbx {},
22    /// Binary glTF 2.0. We refer to this as glTF since that is how our customers refer to
23    /// it, but this can also import binary glTF (glb).
24    #[serde(rename = "gltf")]
25    Gltf {},
26    /// Wavefront OBJ format.
27    #[serde(rename = "obj")]
28    Obj {
29        /// Co-ordinate system of input data.
30        /// Defaults to the [KittyCAD co-ordinate system.
31        coords: Option<System>,
32        /// The units of the input data. This is very important for correct scaling and when
33        /// calculating physics properties like mass, etc.
34        /// Defaults to millimeters.
35        units: UnitLength,
36    },
37    /// The PLY Polygon File Format.
38    #[serde(rename = "ply")]
39    Ply {
40        /// Co-ordinate system of input data.
41        /// Defaults to the [KittyCAD co-ordinate system.
42        coords: Option<System>,
43        /// The units of the input data. This is very important for correct scaling and when
44        /// calculating physics properties like mass, etc.
45        /// Defaults to millimeters.
46        units: UnitLength,
47    },
48    /// SolidWorks part (SLDPRT) format.
49    #[serde(rename = "sldprt")]
50    Sldprt {},
51    /// ISO 10303-21 (STEP) format.
52    #[serde(rename = "step")]
53    Step {},
54    /// *ST**ereo**L**ithography format.
55    #[serde(rename = "stl")]
56    Stl {
57        /// Co-ordinate system of input data.
58        /// Defaults to the [KittyCAD co-ordinate system.
59        coords: Option<System>,
60        /// The units of the input data. This is very important for correct scaling and when
61        /// calculating physics properties like mass, etc.
62        /// Defaults to millimeters.
63        units: UnitLength,
64    },
65}
66
67impl From<ImportFormat> for InputFormat3d {
68    fn from(format: ImportFormat) -> Self {
69        match format {
70            ImportFormat::Fbx {} => InputFormat3d::Fbx(Default::default()),
71            ImportFormat::Gltf {} => InputFormat3d::Gltf(Default::default()),
72            ImportFormat::Obj { coords, units } => InputFormat3d::Obj(kcmc::format::obj::import::Options {
73                coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
74                units,
75            }),
76            ImportFormat::Ply { coords, units } => InputFormat3d::Ply(kcmc::format::ply::import::Options {
77                coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
78                units,
79            }),
80            ImportFormat::Sldprt {} => InputFormat3d::Sldprt(kcmc::format::sldprt::import::Options {
81                split_closed_faces: false,
82            }),
83            ImportFormat::Step {} => InputFormat3d::Step(kcmc::format::step::import::Options {
84                split_closed_faces: false,
85            }),
86            ImportFormat::Stl { coords, units } => InputFormat3d::Stl(kcmc::format::stl::import::Options {
87                coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
88                units,
89            }),
90        }
91    }
92}
93
94/// Import a CAD file.
95/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
96/// Otherwise you can specify the unit by passing in the options parameter.
97/// If you import a gltf file, we will try to find the bin file and import it as well.
98///
99/// Import paths are relative to the current project directory. This only works in the desktop app
100/// not in browser.
101pub async fn import(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
102    let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
103
104    let imported_geometry = inner_import(file_path, options, exec_state, args).await?;
105    Ok(KclValue::ImportedGeometry(imported_geometry))
106}
107
108/// Import a CAD file.
109///
110/// **DEPRECATED** Prefer to use import statements.
111///
112/// For formats lacking unit data (such as STL, OBJ, or PLY files), the default
113/// unit of measurement is millimeters. Alternatively you may specify the unit
114/// by passing your desired measurement unit in the options parameter. When
115/// importing a GLTF file, the bin file will be imported as well. Import paths
116/// are relative to the current project directory.
117///
118/// Note: The import command currently only works when using the native
119/// Modeling App.
120///
121/// ```no_run
122/// model = import("tests/inputs/cube.obj")
123/// ```
124///
125/// ```no_run
126/// model = import("tests/inputs/cube.obj", {format: "obj", units: "m"})
127/// ```
128///
129/// ```no_run
130/// model = import("tests/inputs/cube.gltf")
131/// ```
132///
133/// ```no_run
134/// model = import("tests/inputs/cube.sldprt")
135/// ```
136///
137/// ```no_run
138/// model = import("tests/inputs/cube.step")
139/// ```
140///
141/// ```no_run
142/// import height, buildSketch from 'common.kcl'
143///
144/// plane = 'XZ'
145/// margin = 2
146/// s1 = buildSketch(plane, [0, 0])
147/// s2 = buildSketch(plane, [0, height() + margin])
148/// ```
149#[stdlib {
150    name = "import",
151    feature_tree_operation = true,
152    deprecated = true,
153    tags = [],
154}]
155async fn inner_import(
156    file_path: String,
157    options: Option<ImportFormat>,
158    exec_state: &mut ExecState,
159    args: Args,
160) -> Result<ImportedGeometry, KclError> {
161    if file_path.is_empty() {
162        return Err(KclError::Semantic(KclErrorDetails {
163            message: "No file path was provided.".to_string(),
164            source_ranges: vec![args.source_range],
165        }));
166    }
167
168    let format = options.map(InputFormat3d::from);
169    send_import_to_engine(
170        import_foreign(
171            std::path::Path::new(&file_path),
172            format,
173            exec_state,
174            &args.ctx,
175            args.source_range,
176        )
177        .await?,
178        &args.ctx,
179    )
180    .await
181}