1use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
6use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
7use schemars::JsonSchema;
8use serde::Serialize;
9
10use super::{args::TyF64, DEFAULT_TOLERANCE};
11use crate::{
12 errors::KclError,
13 execution::{
14 types::{NumericType, RuntimeType},
15 ExecState, Helix, KclValue, Sketch, Solid,
16 },
17 parsing::ast::types::TagNode,
18 std::{extrude::do_post_extrude, Args},
19};
20
21#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
23#[ts(export)]
24#[serde(untagged)]
25pub enum SweepPath {
26 Sketch(Sketch),
27 Helix(Box<Helix>),
28}
29
30pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
32 let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
33 let path: SweepPath = args.get_kw_arg_typed(
34 "path",
35 &RuntimeType::Union(vec![RuntimeType::sketch(), RuntimeType::helix()]),
36 exec_state,
37 )?;
38 let sectional = args.get_kw_arg_opt("sectional")?;
39 let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
40 let relative_to: Option<String> = args.get_kw_arg_opt_typed("relativeTo", &RuntimeType::string(), exec_state)?;
41 let tag_start = args.get_kw_arg_opt("tagStart")?;
42 let tag_end = args.get_kw_arg_opt("tagEnd")?;
43
44 let value = inner_sweep(
45 sketches,
46 path,
47 sectional,
48 tolerance,
49 relative_to,
50 tag_start,
51 tag_end,
52 exec_state,
53 args,
54 )
55 .await?;
56 Ok(value.into())
57}
58
59#[stdlib {
161 name = "sweep",
162 feature_tree_operation = true,
163 keywords = true,
164 unlabeled_first = true,
165 args = {
166 sketches = { docs = "The sketch or set of sketches that should be swept in space" },
167 path = { docs = "The path to sweep the sketch along" },
168 sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
169 tolerance = { docs = "Tolerance for this operation" },
170 relative_to = { docs = "What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. Defaults to trajectoryCurve."},
171 tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" },
172 tag_end = { docs = "A named tag for the face at the end of the sweep" },
173 },
174 tags = ["sketch"]
175}]
176#[allow(clippy::too_many_arguments)]
177async fn inner_sweep(
178 sketches: Vec<Sketch>,
179 path: SweepPath,
180 sectional: Option<bool>,
181 tolerance: Option<TyF64>,
182 relative_to: Option<String>,
183 tag_start: Option<TagNode>,
184 tag_end: Option<TagNode>,
185 exec_state: &mut ExecState,
186 args: Args,
187) -> Result<Vec<Solid>, KclError> {
188 let trajectory = match path {
189 SweepPath::Sketch(sketch) => sketch.id.into(),
190 SweepPath::Helix(helix) => helix.value.into(),
191 };
192 let relative_to = match relative_to.as_deref() {
193 Some("sketchPlane") => RelativeTo::SketchPlane,
194 Some("trajectoryCurve") | None => RelativeTo::TrajectoryCurve,
195 Some(_) => {
196 return Err(KclError::Syntax(crate::errors::KclErrorDetails {
197 source_ranges: vec![args.source_range],
198 message: "If you provide relativeTo, it must either be 'sketchPlane' or 'trajectoryCurve'".to_owned(),
199 }))
200 }
201 };
202
203 let mut solids = Vec::new();
204 for sketch in &sketches {
205 let id = exec_state.next_uuid();
206 args.batch_modeling_cmd(
207 id,
208 ModelingCmd::from(mcmd::Sweep {
209 target: sketch.id.into(),
210 trajectory,
211 sectional: sectional.unwrap_or(false),
212 tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
213 relative_to,
214 }),
215 )
216 .await?;
217
218 solids.push(
219 do_post_extrude(
220 sketch,
221 #[cfg(feature = "artifact-graph")]
222 id.into(),
223 TyF64::new(0.0, NumericType::mm()),
224 sectional.unwrap_or(false),
225 &super::extrude::NamedCapTags {
226 start: tag_start.as_ref(),
227 end: tag_end.as_ref(),
228 },
229 exec_state,
230 &args,
231 )
232 .await?,
233 );
234 }
235
236 args.batch_modeling_cmd(
238 exec_state.next_uuid(),
239 ModelingCmd::from(mcmd::ObjectVisible {
240 object_id: trajectory.into(),
241 hidden: true,
242 }),
243 )
244 .await?;
245
246 Ok(solids)
247}