1use anyhow::Result;
4use kcmc::{ModelingCmd, each_cmd as mcmd, length_unit::LengthUnit};
5use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
6use schemars::JsonSchema;
7use serde::Serialize;
8
9use super::{DEFAULT_TOLERANCE_MM, args::TyF64};
10use crate::{
11 errors::KclError,
12 execution::{
13 ExecState, Helix, KclValue, ModelingCmdMeta, Sketch, Solid,
14 types::{NumericType, RuntimeType},
15 },
16 parsing::ast::types::TagNode,
17 std::{Args, extrude::do_post_extrude},
18};
19
20#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
22#[ts(export)]
23#[serde(untagged)]
24#[allow(clippy::large_enum_variant)]
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("sketches", &RuntimeType::sketches(), exec_state)?;
33 let path: SweepPath = args.get_kw_arg(
34 "path",
35 &RuntimeType::Union(vec![RuntimeType::sketch(), RuntimeType::helix()]),
36 exec_state,
37 )?;
38 let sectional = args.get_kw_arg_opt("sectional", &RuntimeType::bool(), exec_state)?;
39 let tolerance: Option<TyF64> = args.get_kw_arg_opt("tolerance", &RuntimeType::length(), exec_state)?;
40 let relative_to: Option<String> = args.get_kw_arg_opt("relativeTo", &RuntimeType::string(), exec_state)?;
41 let tag_start = args.get_kw_arg_opt("tagStart", &RuntimeType::tag_decl(), exec_state)?;
42 let tag_end = args.get_kw_arg_opt("tagEnd", &RuntimeType::tag_decl(), exec_state)?;
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#[allow(clippy::too_many_arguments)]
60async fn inner_sweep(
61 sketches: Vec<Sketch>,
62 path: SweepPath,
63 sectional: Option<bool>,
64 tolerance: Option<TyF64>,
65 relative_to: Option<String>,
66 tag_start: Option<TagNode>,
67 tag_end: Option<TagNode>,
68 exec_state: &mut ExecState,
69 args: Args,
70) -> Result<Vec<Solid>, KclError> {
71 let trajectory = match path {
72 SweepPath::Sketch(sketch) => sketch.id.into(),
73 SweepPath::Helix(helix) => helix.value.into(),
74 };
75 let relative_to = match relative_to.as_deref() {
76 Some("sketchPlane") => RelativeTo::SketchPlane,
77 Some("trajectoryCurve") | None => RelativeTo::TrajectoryCurve,
78 Some(_) => {
79 return Err(KclError::new_syntax(crate::errors::KclErrorDetails::new(
80 "If you provide relativeTo, it must either be 'sketchPlane' or 'trajectoryCurve'".to_owned(),
81 vec![args.source_range],
82 )));
83 }
84 };
85
86 let mut solids = Vec::new();
87 for sketch in &sketches {
88 let id = exec_state.next_uuid();
89 exec_state
90 .batch_modeling_cmd(
91 ModelingCmdMeta::from_args_id(&args, id),
92 ModelingCmd::from(mcmd::Sweep {
93 target: sketch.id.into(),
94 trajectory,
95 sectional: sectional.unwrap_or(false),
96 tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
97 relative_to,
98 }),
99 )
100 .await?;
101
102 solids.push(
103 do_post_extrude(
104 sketch,
105 id.into(),
106 TyF64::new(0.0, NumericType::mm()),
107 sectional.unwrap_or(false),
108 &super::extrude::NamedCapTags {
109 start: tag_start.as_ref(),
110 end: tag_end.as_ref(),
111 },
112 exec_state,
113 &args,
114 None,
115 )
116 .await?,
117 );
118 }
119
120 exec_state
122 .batch_modeling_cmd(
123 (&args).into(),
124 ModelingCmd::from(mcmd::ObjectVisible {
125 object_id: trajectory.into(),
126 hidden: true,
127 }),
128 )
129 .await?;
130
131 Ok(solids)
132}