1use anyhow::Result;
4use kcmc::{each_cmd as mcmd, ModelingCmd};
5use kittycad_modeling_cmds::{
6 self as kcmc, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, output::EntityGetAllChildUuids,
7 shared::Point3d, websocket::OkWebSocketResponseData,
8};
9
10use crate::{
11 errors::{KclError, KclErrorDetails},
12 execution::{
13 types::{PrimitiveType, RuntimeType},
14 ExecState, KclValue, Sketch,
15 },
16 std::{axis_or_reference::Axis2dOrEdgeReference, Args},
17};
18
19pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
23 let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
24 let axis = args.get_kw_arg_typed(
25 "axis",
26 &RuntimeType::Union(vec![
27 RuntimeType::Primitive(PrimitiveType::Edge),
28 RuntimeType::Primitive(PrimitiveType::Axis2d),
29 ]),
30 exec_state,
31 )?;
32
33 let sketches = inner_mirror_2d(sketches, axis, exec_state, args).await?;
34 Ok(sketches.into())
35}
36
37async fn inner_mirror_2d(
41 sketches: Vec<Sketch>,
42 axis: Axis2dOrEdgeReference,
43 exec_state: &mut ExecState,
44 args: Args,
45) -> Result<Vec<Sketch>, KclError> {
46 let mut starting_sketches = sketches.clone();
47
48 starting_sketches.iter_mut().for_each(|sketch| {
50 sketch.mirror = Some(exec_state.next_uuid());
51 });
52
53 if args.ctx.no_engine_commands().await {
54 return Ok(starting_sketches);
55 }
56
57 match axis {
58 Axis2dOrEdgeReference::Axis { direction, origin } => {
59 args.batch_modeling_cmd(
60 exec_state.next_uuid(),
61 ModelingCmd::from(mcmd::EntityMirror {
62 ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
63 axis: Point3d {
64 x: direction[0].n,
65 y: direction[1].n,
66 z: 0.0,
67 },
68 point: Point3d {
69 x: LengthUnit(origin[0].n),
70 y: LengthUnit(origin[1].n),
71 z: LengthUnit(0.0),
72 },
73 }),
74 )
75 .await?;
76 }
77 Axis2dOrEdgeReference::Edge(edge) => {
78 let edge_id = edge.get_engine_id(exec_state, &args)?;
79
80 args.batch_modeling_cmd(
81 exec_state.next_uuid(),
82 ModelingCmd::from(mcmd::EntityMirrorAcrossEdge {
83 ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
84 edge_id,
85 }),
86 )
87 .await?;
88 }
89 };
90
91 for sketch in &mut starting_sketches {
97 let response = args
98 .send_modeling_cmd(
99 exec_state.next_uuid(),
100 ModelingCmd::from(mcmd::EntityGetAllChildUuids { entity_id: sketch.id }),
101 )
102 .await?;
103 let OkWebSocketResponseData::Modeling {
104 modeling_response:
105 OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids { entity_ids: child_ids }),
106 } = response
107 else {
108 return Err(KclError::Internal(KclErrorDetails {
109 message: "Expected a successful response from EntityGetAllChildUuids".to_string(),
110 source_ranges: vec![args.source_range],
111 }));
112 };
113
114 if child_ids.len() >= 2 {
115 let child_id = child_ids[1];
117 sketch.mirror = Some(child_id);
118 } else {
119 return Err(KclError::Type(KclErrorDetails {
120 message: "Expected child uuids to be >= 2".to_string(),
121 source_ranges: vec![args.source_range],
122 }));
123 }
124 }
125
126 Ok(starting_sketches)
127}