//! Standard library mirror. use anyhow::Result; use kcmc::{each_cmd as mcmd, ModelingCmd}; use kittycad_modeling_cmds::{ self as kcmc, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, output::EntityGetAllChildUuids, shared::Point3d, websocket::OkWebSocketResponseData, }; use crate::{ errors::{KclError, KclErrorDetails}, execution::{ types::{PrimitiveType, RuntimeType}, ExecState, KclValue, Sketch, }, std::{axis_or_reference::Axis2dOrEdgeReference, Args}, }; /// Mirror a sketch. pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result { let sketches = args.get_unlabeled_kw_arg("sketches", &RuntimeType::sketches(), exec_state)?; let axis = args.get_kw_arg( "axis", &RuntimeType::Union(vec![ RuntimeType::Primitive(PrimitiveType::Edge), RuntimeType::Primitive(PrimitiveType::Axis2d), ]), exec_state, )?; let sketches = inner_mirror_2d(sketches, axis, exec_state, args).await?; Ok(sketches.into()) } /// Mirror a sketch. async fn inner_mirror_2d( sketches: Vec, axis: Axis2dOrEdgeReference, exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { let mut starting_sketches = sketches.clone(); // Update all to have a mirror. starting_sketches.iter_mut().for_each(|sketch| { sketch.mirror = Some(exec_state.next_uuid()); }); if args.ctx.no_engine_commands().await { return Ok(starting_sketches); } match axis { Axis2dOrEdgeReference::Axis { direction, origin } => { exec_state .batch_modeling_cmd( (&args).into(), ModelingCmd::from(mcmd::EntityMirror { ids: starting_sketches.iter().map(|sketch| sketch.id).collect(), axis: Point3d { x: direction[0].to_mm(), y: direction[1].to_mm(), z: 0.0, }, point: Point3d { x: LengthUnit(origin[0].to_mm()), y: LengthUnit(origin[1].to_mm()), z: LengthUnit(0.0), }, }), ) .await?; } Axis2dOrEdgeReference::Edge(edge) => { let edge_id = edge.get_engine_id(exec_state, &args)?; exec_state .batch_modeling_cmd( (&args).into(), ModelingCmd::from(mcmd::EntityMirrorAcrossEdge { ids: starting_sketches.iter().map(|sketch| sketch.id).collect(), edge_id, }), ) .await?; } }; // After the mirror, get the first child uuid for the path. // The "get extrusion face info" API call requires *any* edge on the sketch being extruded. // But if you mirror2d a sketch these IDs might change so we need to get the children versus // using the IDs we already have. // We only do this with mirrors because otherwise it is a waste of a websocket call. for sketch in &mut starting_sketches { let response = exec_state .send_modeling_cmd( (&args).into(), ModelingCmd::from(mcmd::EntityGetAllChildUuids { entity_id: sketch.id }), ) .await?; let OkWebSocketResponseData::Modeling { modeling_response: OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids { entity_ids: child_ids }), } = response else { return Err(KclError::new_internal(KclErrorDetails::new( "Expected a successful response from EntityGetAllChildUuids".to_string(), vec![args.source_range], ))); }; if child_ids.len() >= 2 { // The first child is the original sketch, the second is the mirrored sketch. let child_id = child_ids[1]; sketch.mirror = Some(child_id); } else { return Err(KclError::new_type(KclErrorDetails::new( "Expected child uuids to be >= 2".to_string(), vec![args.source_range], ))); } } Ok(starting_sketches) }