1use std::collections::HashMap;
4
5use anyhow::Result;
6use kcmc::shared::Point3d as KPoint3d; use kcmc::{
8 ModelingCmd, each_cmd as mcmd,
9 length_unit::LengthUnit,
10 ok_response::OkModelingCmdResponse,
11 output::ExtrusionFaceInfo,
12 shared::{ExtrudeReference, ExtrusionFaceCapType, Opposite},
13 websocket::{ModelingCmdReq, OkWebSocketResponseData},
14};
15use kittycad_modeling_cmds::{
16 self as kcmc,
17 shared::{Angle, BodyType, ExtrudeMethod, Point2d},
18};
19use uuid::Uuid;
20
21use super::{DEFAULT_TOLERANCE_MM, args::TyF64, utils::point_to_mm};
22use crate::{
23 errors::{KclError, KclErrorDetails},
24 execution::{
25 ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, ModelingCmdMeta, Path, ProfileClosed, Sketch,
26 SketchSurface, Solid,
27 types::{PrimitiveType, RuntimeType},
28 },
29 parsing::ast::types::TagNode,
30 std::{Args, axis_or_reference::Point3dAxis3dOrGeometryReference},
31};
32
33pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
35 let sketches = args.get_unlabeled_kw_arg("sketches", &RuntimeType::sketches(), exec_state)?;
36 let length: Option<TyF64> = args.get_kw_arg_opt("length", &RuntimeType::length(), exec_state)?;
37 let to = args.get_kw_arg_opt(
38 "to",
39 &RuntimeType::Union(vec![
40 RuntimeType::point3d(),
41 RuntimeType::Primitive(PrimitiveType::Axis3d),
42 RuntimeType::Primitive(PrimitiveType::Edge),
43 RuntimeType::plane(),
44 RuntimeType::Primitive(PrimitiveType::Face),
45 RuntimeType::sketch(),
46 RuntimeType::Primitive(PrimitiveType::Solid),
47 RuntimeType::tagged_edge(),
48 RuntimeType::tagged_face(),
49 ]),
50 exec_state,
51 )?;
52 let symmetric = args.get_kw_arg_opt("symmetric", &RuntimeType::bool(), exec_state)?;
53 let bidirectional_length: Option<TyF64> =
54 args.get_kw_arg_opt("bidirectionalLength", &RuntimeType::length(), exec_state)?;
55 let tag_start = args.get_kw_arg_opt("tagStart", &RuntimeType::tag_decl(), exec_state)?;
56 let tag_end = args.get_kw_arg_opt("tagEnd", &RuntimeType::tag_decl(), exec_state)?;
57 let twist_angle: Option<TyF64> = args.get_kw_arg_opt("twistAngle", &RuntimeType::degrees(), exec_state)?;
58 let twist_angle_step: Option<TyF64> = args.get_kw_arg_opt("twistAngleStep", &RuntimeType::degrees(), exec_state)?;
59 let twist_center: Option<[TyF64; 2]> = args.get_kw_arg_opt("twistCenter", &RuntimeType::point2d(), exec_state)?;
60 let tolerance: Option<TyF64> = args.get_kw_arg_opt("tolerance", &RuntimeType::length(), exec_state)?;
61 let method: Option<String> = args.get_kw_arg_opt("method", &RuntimeType::string(), exec_state)?;
62 let body_type: Option<BodyType> = args.get_kw_arg_opt("bodyType", &RuntimeType::string(), exec_state)?;
63
64 let result = inner_extrude(
65 sketches,
66 length,
67 to,
68 symmetric,
69 bidirectional_length,
70 tag_start,
71 tag_end,
72 twist_angle,
73 twist_angle_step,
74 twist_center,
75 tolerance,
76 method,
77 body_type,
78 exec_state,
79 args,
80 )
81 .await?;
82
83 Ok(result.into())
84}
85
86#[allow(clippy::too_many_arguments)]
87async fn inner_extrude(
88 sketches: Vec<Sketch>,
89 length: Option<TyF64>,
90 to: Option<Point3dAxis3dOrGeometryReference>,
91 symmetric: Option<bool>,
92 bidirectional_length: Option<TyF64>,
93 tag_start: Option<TagNode>,
94 tag_end: Option<TagNode>,
95 twist_angle: Option<TyF64>,
96 twist_angle_step: Option<TyF64>,
97 twist_center: Option<[TyF64; 2]>,
98 tolerance: Option<TyF64>,
99 method: Option<String>,
100 body_type: Option<BodyType>,
101 exec_state: &mut ExecState,
102 args: Args,
103) -> Result<Vec<Solid>, KclError> {
104 let body_type = body_type.unwrap_or_default();
105
106 let mut solids = Vec::new();
108 let tolerance = LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM));
109
110 let extrude_method = match method.as_deref() {
111 Some("new" | "NEW") => ExtrudeMethod::New,
112 Some("merge" | "MERGE") => ExtrudeMethod::Merge,
113 None => ExtrudeMethod::default(),
114 Some(other) => {
115 return Err(KclError::new_semantic(KclErrorDetails::new(
116 format!("Unknown merge method {other}, try using `MERGE` or `NEW`"),
117 vec![args.source_range],
118 )));
119 }
120 };
121
122 if symmetric.unwrap_or(false) && bidirectional_length.is_some() {
123 return Err(KclError::new_semantic(KclErrorDetails::new(
124 "You cannot give both `symmetric` and `bidirectional` params, you have to choose one or the other"
125 .to_owned(),
126 vec![args.source_range],
127 )));
128 }
129
130 if (length.is_some() || twist_angle.is_some()) && to.is_some() {
131 return Err(KclError::new_semantic(KclErrorDetails::new(
132 "You cannot give `length` or `twist` params with the `to` param, you have to choose one or the other"
133 .to_owned(),
134 vec![args.source_range],
135 )));
136 }
137
138 let bidirection = bidirectional_length.map(|l| LengthUnit(l.to_mm()));
139
140 let opposite = match (symmetric, bidirection) {
141 (Some(true), _) => Opposite::Symmetric,
142 (None, None) => Opposite::None,
143 (Some(false), None) => Opposite::None,
144 (None, Some(length)) => Opposite::Other(length),
145 (Some(false), Some(length)) => Opposite::Other(length),
146 };
147
148 for sketch in &sketches {
149 let id = exec_state.next_uuid();
150 let cmd = match (&twist_angle, &twist_angle_step, &twist_center, length.clone(), &to) {
151 (Some(angle), angle_step, center, Some(length), None) => {
152 let center = center.clone().map(point_to_mm).map(Point2d::from).unwrap_or_default();
153 let total_rotation_angle = Angle::from_degrees(angle.to_degrees(exec_state, args.source_range));
154 let angle_step_size = Angle::from_degrees(
155 angle_step
156 .clone()
157 .map(|a| a.to_degrees(exec_state, args.source_range))
158 .unwrap_or(15.0),
159 );
160 ModelingCmd::from(mcmd::TwistExtrude {
161 target: sketch.id.into(),
162 distance: LengthUnit(length.to_mm()),
163 faces: Default::default(),
164 center_2d: center,
165 total_rotation_angle,
166 angle_step_size,
167 tolerance,
168 body_type,
169 })
170 }
171 (None, None, None, Some(length), None) => ModelingCmd::from(mcmd::Extrude {
172 target: sketch.id.into(),
173 distance: LengthUnit(length.to_mm()),
174 faces: Default::default(),
175 opposite: opposite.clone(),
176 extrude_method,
177 body_type,
178 }),
179 (None, None, None, None, Some(to)) => match to {
180 Point3dAxis3dOrGeometryReference::Point(point) => ModelingCmd::from(mcmd::ExtrudeToReference {
181 target: sketch.id.into(),
182 reference: ExtrudeReference::Point {
183 point: KPoint3d {
184 x: LengthUnit(point[0].to_mm()),
185 y: LengthUnit(point[1].to_mm()),
186 z: LengthUnit(point[2].to_mm()),
187 },
188 },
189 faces: Default::default(),
190 extrude_method,
191 body_type,
192 }),
193 Point3dAxis3dOrGeometryReference::Axis { direction, origin } => {
194 ModelingCmd::from(mcmd::ExtrudeToReference {
195 target: sketch.id.into(),
196 reference: ExtrudeReference::Axis {
197 axis: KPoint3d {
198 x: direction[0].to_mm(),
199 y: direction[1].to_mm(),
200 z: direction[2].to_mm(),
201 },
202 point: KPoint3d {
203 x: LengthUnit(origin[0].to_mm()),
204 y: LengthUnit(origin[1].to_mm()),
205 z: LengthUnit(origin[2].to_mm()),
206 },
207 },
208 faces: Default::default(),
209 extrude_method,
210 body_type,
211 })
212 }
213 Point3dAxis3dOrGeometryReference::Plane(plane) => {
214 let plane_id = if plane.value == crate::exec::PlaneType::Uninit {
215 if plane.info.origin.units.is_none() {
216 return Err(KclError::new_semantic(KclErrorDetails::new(
217 "Origin of plane has unknown units".to_string(),
218 vec![args.source_range],
219 )));
220 }
221 let sketch_plane = crate::std::sketch::make_sketch_plane_from_orientation(
222 plane.clone().info.into_plane_data(),
223 exec_state,
224 &args,
225 )
226 .await?;
227 sketch_plane.id
228 } else {
229 plane.id
230 };
231 ModelingCmd::from(mcmd::ExtrudeToReference {
232 target: sketch.id.into(),
233 reference: ExtrudeReference::EntityReference { entity_id: plane_id },
234 faces: Default::default(),
235 extrude_method,
236 body_type,
237 })
238 }
239 Point3dAxis3dOrGeometryReference::Edge(edge_ref) => {
240 let edge_id = edge_ref.get_engine_id(exec_state, &args)?;
241 ModelingCmd::from(mcmd::ExtrudeToReference {
242 target: sketch.id.into(),
243 reference: ExtrudeReference::EntityReference { entity_id: edge_id },
244 faces: Default::default(),
245 extrude_method,
246 body_type,
247 })
248 }
249 Point3dAxis3dOrGeometryReference::Face(face_tag) => {
250 let face_id = face_tag.get_face_id_from_tag(exec_state, &args, false).await?;
251 ModelingCmd::from(mcmd::ExtrudeToReference {
252 target: sketch.id.into(),
253 reference: ExtrudeReference::EntityReference { entity_id: face_id },
254 faces: Default::default(),
255 extrude_method,
256 body_type,
257 })
258 }
259 Point3dAxis3dOrGeometryReference::Sketch(sketch_ref) => ModelingCmd::from(mcmd::ExtrudeToReference {
260 target: sketch.id.into(),
261 reference: ExtrudeReference::EntityReference {
262 entity_id: sketch_ref.id,
263 },
264 faces: Default::default(),
265 extrude_method,
266 body_type,
267 }),
268 Point3dAxis3dOrGeometryReference::Solid(solid) => ModelingCmd::from(mcmd::ExtrudeToReference {
269 target: sketch.id.into(),
270 reference: ExtrudeReference::EntityReference { entity_id: solid.id },
271 faces: Default::default(),
272 extrude_method,
273 body_type,
274 }),
275 Point3dAxis3dOrGeometryReference::TaggedEdgeOrFace(tag) => {
276 let tagged_edge_or_face = args.get_tag_engine_info(exec_state, tag)?;
277 let tagged_edge_or_face_id = tagged_edge_or_face.id;
278 ModelingCmd::from(mcmd::ExtrudeToReference {
279 target: sketch.id.into(),
280 reference: ExtrudeReference::EntityReference {
281 entity_id: tagged_edge_or_face_id,
282 },
283 faces: Default::default(),
284 extrude_method,
285 body_type,
286 })
287 }
288 },
289 (Some(_), _, _, None, None) => {
290 return Err(KclError::new_semantic(KclErrorDetails::new(
291 "The `length` parameter must be provided when using twist angle for extrusion.".to_owned(),
292 vec![args.source_range],
293 )));
294 }
295 (_, _, _, None, None) => {
296 return Err(KclError::new_semantic(KclErrorDetails::new(
297 "Either `length` or `to` parameter must be provided for extrusion.".to_owned(),
298 vec![args.source_range],
299 )));
300 }
301 (_, _, _, Some(_), Some(_)) => {
302 return Err(KclError::new_semantic(KclErrorDetails::new(
303 "You cannot give both `length` and `to` params, you have to choose one or the other".to_owned(),
304 vec![args.source_range],
305 )));
306 }
307 (_, _, _, _, _) => {
308 return Err(KclError::new_semantic(KclErrorDetails::new(
309 "Invalid combination of parameters for extrusion.".to_owned(),
310 vec![args.source_range],
311 )));
312 }
313 };
314 let cmds = sketch.build_sketch_mode_cmds(exec_state, ModelingCmdReq { cmd_id: id.into(), cmd });
315 exec_state
316 .batch_modeling_cmds(ModelingCmdMeta::from_args_id(exec_state, &args, id), &cmds)
317 .await?;
318
319 solids.push(
320 do_post_extrude(
321 sketch,
322 id.into(),
323 false,
324 &NamedCapTags {
325 start: tag_start.as_ref(),
326 end: tag_end.as_ref(),
327 },
328 extrude_method,
329 exec_state,
330 &args,
331 None,
332 None,
333 body_type,
334 )
335 .await?,
336 );
337 }
338
339 Ok(solids)
340}
341
342#[derive(Debug, Default)]
343pub(crate) struct NamedCapTags<'a> {
344 pub start: Option<&'a TagNode>,
345 pub end: Option<&'a TagNode>,
346}
347
348#[allow(clippy::too_many_arguments)]
349pub(crate) async fn do_post_extrude<'a>(
350 sketch: &Sketch,
351 solid_id: ArtifactId,
352 sectional: bool,
353 named_cap_tags: &'a NamedCapTags<'a>,
354 extrude_method: ExtrudeMethod,
355 exec_state: &mut ExecState,
356 args: &Args,
357 edge_id: Option<Uuid>,
358 clone_id_map: Option<&HashMap<Uuid, Uuid>>, body_type: BodyType,
360) -> Result<Solid, KclError> {
361 exec_state
364 .batch_modeling_cmd(
365 ModelingCmdMeta::from_args(exec_state, args),
366 ModelingCmd::from(mcmd::ObjectBringToFront { object_id: sketch.id }),
367 )
368 .await?;
369
370 let any_edge_id = if let Some(edge_id) = sketch.mirror {
371 edge_id
372 } else if let Some(id) = edge_id {
373 id
374 } else {
375 let Some(any_edge_id) = sketch.paths.first().map(|edge| edge.get_base().geo_meta.id) else {
378 return Err(KclError::new_type(KclErrorDetails::new(
379 "Expected a non-empty sketch".to_owned(),
380 vec![args.source_range],
381 )));
382 };
383 any_edge_id
384 };
385
386 let mut extrusion_info_edge_id = any_edge_id;
388 if sketch.clone.is_some() && clone_id_map.is_some() {
389 extrusion_info_edge_id = if let Some(clone_map) = clone_id_map {
390 if let Some(new_edge_id) = clone_map.get(&extrusion_info_edge_id) {
391 *new_edge_id
392 } else {
393 extrusion_info_edge_id
394 }
395 } else {
396 any_edge_id
397 };
398 }
399
400 let mut sketch = sketch.clone();
401 match body_type {
402 BodyType::Solid => {
403 sketch.is_closed = ProfileClosed::Explicitly;
404 }
405 BodyType::Surface => {}
406 }
407
408 if let SketchSurface::Face(ref face) = sketch.on {
410 if extrude_method != ExtrudeMethod::New {
412 sketch.id = face.solid.sketch.id;
413 }
414 }
415
416 let sketch_id = if let Some(cloned_from) = sketch.clone
418 && clone_id_map.is_some()
419 {
420 cloned_from
421 } else {
422 sketch.id
423 };
424
425 let solid3d_info = exec_state
426 .send_modeling_cmd(
427 ModelingCmdMeta::from_args(exec_state, args),
428 ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo {
429 edge_id: extrusion_info_edge_id,
430 object_id: sketch_id,
431 }),
432 )
433 .await?;
434
435 let face_infos = if let OkWebSocketResponseData::Modeling {
436 modeling_response: OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(data),
437 } = solid3d_info
438 {
439 data.faces
440 } else {
441 vec![]
442 };
443
444 #[cfg(feature = "artifact-graph")]
446 {
447 if !sectional {
450 exec_state
451 .batch_modeling_cmd(
452 ModelingCmdMeta::from_args(exec_state, args),
453 ModelingCmd::from(mcmd::Solid3dGetAdjacencyInfo {
454 object_id: sketch.id,
455 edge_id: any_edge_id,
456 }),
457 )
458 .await?;
459 }
460 }
461
462 let Faces {
463 sides: mut face_id_map,
464 start_cap_id,
465 end_cap_id,
466 } = analyze_faces(exec_state, args, face_infos).await;
467
468 if sketch.clone.is_some()
470 && let Some(clone_id_map) = clone_id_map
471 {
472 face_id_map = face_id_map
473 .into_iter()
474 .filter_map(|(k, v)| {
475 let fe_key = clone_id_map.get(&k)?;
476 let fe_value = clone_id_map.get(&(v?)).copied();
477 Some((*fe_key, fe_value))
478 })
479 .collect::<HashMap<Uuid, Option<Uuid>>>();
480 }
481
482 let no_engine_commands = args.ctx.no_engine_commands().await;
484 let mut new_value: Vec<ExtrudeSurface> = Vec::with_capacity(sketch.paths.len() + sketch.inner_paths.len() + 2);
485 let outer_surfaces = sketch.paths.iter().flat_map(|path| {
486 if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
487 surface_of(path, *actual_face_id)
488 } else if no_engine_commands {
489 crate::log::logln!(
490 "No face ID found for path ID {:?}, but in no-engine-commands mode, so faking it",
491 path.get_base().geo_meta.id
492 );
493 fake_extrude_surface(exec_state, path)
495 } else if sketch.clone.is_some()
496 && let Some(clone_map) = clone_id_map
497 {
498 let new_path = clone_map.get(&(path.get_base().geo_meta.id));
499
500 if let Some(new_path) = new_path {
501 match face_id_map.get(new_path) {
502 Some(Some(actual_face_id)) => clone_surface_of(path, *new_path, *actual_face_id),
503 _ => {
504 let actual_face_id = face_id_map.iter().find_map(|(key, value)| {
505 if let Some(value) = value {
506 if value == new_path { Some(key) } else { None }
507 } else {
508 None
509 }
510 });
511 match actual_face_id {
512 Some(actual_face_id) => clone_surface_of(path, *new_path, *actual_face_id),
513 None => {
514 crate::log::logln!("No face ID found for clone path ID {:?}, so skipping it", new_path);
515 None
516 }
517 }
518 }
519 }
520 } else {
521 None
522 }
523 } else {
524 crate::log::logln!(
525 "No face ID found for path ID {:?}, and not in no-engine-commands mode, so skipping it",
526 path.get_base().geo_meta.id
527 );
528 None
529 }
530 });
531
532 new_value.extend(outer_surfaces);
533 let inner_surfaces = sketch.inner_paths.iter().flat_map(|path| {
534 if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
535 surface_of(path, *actual_face_id)
536 } else if no_engine_commands {
537 fake_extrude_surface(exec_state, path)
539 } else {
540 None
541 }
542 });
543 new_value.extend(inner_surfaces);
544
545 if let Some(tag_start) = named_cap_tags.start {
547 let Some(start_cap_id) = start_cap_id else {
548 return Err(KclError::new_type(KclErrorDetails::new(
549 format!(
550 "Expected a start cap ID for tag `{}` for extrusion of sketch {:?}",
551 tag_start.name, sketch.id
552 ),
553 vec![args.source_range],
554 )));
555 };
556
557 new_value.push(ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
558 face_id: start_cap_id,
559 tag: Some(tag_start.clone()),
560 geo_meta: GeoMeta {
561 id: start_cap_id,
562 metadata: args.source_range.into(),
563 },
564 }));
565 }
566 if let Some(tag_end) = named_cap_tags.end {
567 let Some(end_cap_id) = end_cap_id else {
568 return Err(KclError::new_type(KclErrorDetails::new(
569 format!(
570 "Expected an end cap ID for tag `{}` for extrusion of sketch {:?}",
571 tag_end.name, sketch.id
572 ),
573 vec![args.source_range],
574 )));
575 };
576
577 new_value.push(ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
578 face_id: end_cap_id,
579 tag: Some(tag_end.clone()),
580 geo_meta: GeoMeta {
581 id: end_cap_id,
582 metadata: args.source_range.into(),
583 },
584 }));
585 }
586
587 Ok(Solid {
588 id: sketch.id,
595 artifact_id: solid_id,
596 value: new_value,
597 meta: sketch.meta.clone(),
598 units: sketch.units,
599 sectional,
600 sketch,
601 start_cap_id,
602 end_cap_id,
603 edge_cuts: vec![],
604 })
605}
606
607#[derive(Default)]
608struct Faces {
609 sides: HashMap<Uuid, Option<Uuid>>,
611 end_cap_id: Option<Uuid>,
613 start_cap_id: Option<Uuid>,
615}
616
617async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
618 let mut faces = Faces {
619 sides: HashMap::with_capacity(face_infos.len()),
620 ..Default::default()
621 };
622 if args.ctx.no_engine_commands().await {
623 faces.start_cap_id = Some(exec_state.next_uuid());
625 faces.end_cap_id = Some(exec_state.next_uuid());
626 }
627 for face_info in face_infos {
628 match face_info.cap {
629 ExtrusionFaceCapType::Bottom => faces.start_cap_id = face_info.face_id,
630 ExtrusionFaceCapType::Top => faces.end_cap_id = face_info.face_id,
631 ExtrusionFaceCapType::Both => {
632 faces.end_cap_id = face_info.face_id;
633 faces.start_cap_id = face_info.face_id;
634 }
635 ExtrusionFaceCapType::None => {
636 if let Some(curve_id) = face_info.curve_id {
637 faces.sides.insert(curve_id, face_info.face_id);
638 }
639 }
640 }
641 }
642 faces
643}
644fn surface_of(path: &Path, actual_face_id: Uuid) -> Option<ExtrudeSurface> {
645 match path {
646 Path::Arc { .. }
647 | Path::TangentialArc { .. }
648 | Path::TangentialArcTo { .. }
649 | Path::Ellipse { .. }
651 | Path::Conic {.. }
652 | Path::Circle { .. }
653 | Path::CircleThreePoint { .. } => {
654 let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::execution::ExtrudeArc {
655 face_id: actual_face_id,
656 tag: path.get_base().tag.clone(),
657 geo_meta: GeoMeta {
658 id: path.get_base().geo_meta.id,
659 metadata: path.get_base().geo_meta.metadata,
660 },
661 });
662 Some(extrude_surface)
663 }
664 Path::Base { .. } | Path::ToPoint { .. } | Path::Horizontal { .. } | Path::AngledLineTo { .. } => {
665 let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
666 face_id: actual_face_id,
667 tag: path.get_base().tag.clone(),
668 geo_meta: GeoMeta {
669 id: path.get_base().geo_meta.id,
670 metadata: path.get_base().geo_meta.metadata,
671 },
672 });
673 Some(extrude_surface)
674 }
675 Path::ArcThreePoint { .. } => {
676 let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::execution::ExtrudeArc {
677 face_id: actual_face_id,
678 tag: path.get_base().tag.clone(),
679 geo_meta: GeoMeta {
680 id: path.get_base().geo_meta.id,
681 metadata: path.get_base().geo_meta.metadata,
682 },
683 });
684 Some(extrude_surface)
685 }
686 }
687}
688
689fn clone_surface_of(path: &Path, clone_path_id: Uuid, actual_face_id: Uuid) -> Option<ExtrudeSurface> {
690 match path {
691 Path::Arc { .. }
692 | Path::TangentialArc { .. }
693 | Path::TangentialArcTo { .. }
694 | Path::Ellipse { .. }
696 | Path::Conic {.. }
697 | Path::Circle { .. }
698 | Path::CircleThreePoint { .. } => {
699 let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::execution::ExtrudeArc {
700 face_id: actual_face_id,
701 tag: path.get_base().tag.clone(),
702 geo_meta: GeoMeta {
703 id: clone_path_id,
704 metadata: path.get_base().geo_meta.metadata,
705 },
706 });
707 Some(extrude_surface)
708 }
709 Path::Base { .. } | Path::ToPoint { .. } | Path::Horizontal { .. } | Path::AngledLineTo { .. } => {
710 let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
711 face_id: actual_face_id,
712 tag: path.get_base().tag.clone(),
713 geo_meta: GeoMeta {
714 id: clone_path_id,
715 metadata: path.get_base().geo_meta.metadata,
716 },
717 });
718 Some(extrude_surface)
719 }
720 Path::ArcThreePoint { .. } => {
721 let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::execution::ExtrudeArc {
722 face_id: actual_face_id,
723 tag: path.get_base().tag.clone(),
724 geo_meta: GeoMeta {
725 id: clone_path_id,
726 metadata: path.get_base().geo_meta.metadata,
727 },
728 });
729 Some(extrude_surface)
730 }
731 }
732}
733
734fn fake_extrude_surface(exec_state: &mut ExecState, path: &Path) -> Option<ExtrudeSurface> {
736 let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
737 face_id: exec_state.next_uuid(),
739 tag: path.get_base().tag.clone(),
740 geo_meta: GeoMeta {
741 id: path.get_base().geo_meta.id,
742 metadata: path.get_base().geo_meta.metadata,
743 },
744 });
745 Some(extrude_surface)
746}