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> {
21 let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
22 let axis = args.get_kw_arg_typed(
23 "axis",
24 &RuntimeType::Union(vec![
25 RuntimeType::Primitive(PrimitiveType::Edge),
26 RuntimeType::Primitive(PrimitiveType::Axis2d),
27 ]),
28 exec_state,
29 )?;
30
31 let sketches = inner_mirror_2d(sketches, axis, exec_state, args).await?;
32 Ok(sketches.into())
33}
34
35async fn inner_mirror_2d(
37 sketches: Vec<Sketch>,
38 axis: Axis2dOrEdgeReference,
39 exec_state: &mut ExecState,
40 args: Args,
41) -> Result<Vec<Sketch>, KclError> {
42 let mut starting_sketches = sketches.clone();
43
44 starting_sketches.iter_mut().for_each(|sketch| {
46 sketch.mirror = Some(exec_state.next_uuid());
47 });
48
49 if args.ctx.no_engine_commands().await {
50 return Ok(starting_sketches);
51 }
52
53 match axis {
54 Axis2dOrEdgeReference::Axis { direction, origin } => {
55 args.batch_modeling_cmd(
56 exec_state.next_uuid(),
57 ModelingCmd::from(mcmd::EntityMirror {
58 ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
59 axis: Point3d {
60 x: direction[0].to_mm(),
61 y: direction[1].to_mm(),
62 z: 0.0,
63 },
64 point: Point3d {
65 x: LengthUnit(origin[0].to_mm()),
66 y: LengthUnit(origin[1].to_mm()),
67 z: LengthUnit(0.0),
68 },
69 }),
70 )
71 .await?;
72 }
73 Axis2dOrEdgeReference::Edge(edge) => {
74 let edge_id = edge.get_engine_id(exec_state, &args)?;
75
76 args.batch_modeling_cmd(
77 exec_state.next_uuid(),
78 ModelingCmd::from(mcmd::EntityMirrorAcrossEdge {
79 ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
80 edge_id,
81 }),
82 )
83 .await?;
84 }
85 };
86
87 for sketch in &mut starting_sketches {
93 let response = args
94 .send_modeling_cmd(
95 exec_state.next_uuid(),
96 ModelingCmd::from(mcmd::EntityGetAllChildUuids { entity_id: sketch.id }),
97 )
98 .await?;
99 let OkWebSocketResponseData::Modeling {
100 modeling_response:
101 OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids { entity_ids: child_ids }),
102 } = response
103 else {
104 return Err(KclError::Internal(KclErrorDetails::new(
105 "Expected a successful response from EntityGetAllChildUuids".to_string(),
106 vec![args.source_range],
107 )));
108 };
109
110 if child_ids.len() >= 2 {
111 let child_id = child_ids[1];
113 sketch.mirror = Some(child_id);
114 } else {
115 return Err(KclError::Type(KclErrorDetails::new(
116 "Expected child uuids to be >= 2".to_string(),
117 vec![args.source_range],
118 )));
119 }
120 }
121
122 Ok(starting_sketches)
123}