kcl_lib/std/
mirror.rs

1//! Standard library mirror.
2
3use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kcmc::{each_cmd as mcmd, ModelingCmd};
6use kittycad_modeling_cmds::{self as kcmc};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    errors::KclError,
12    execution::{ExecState, KclValue, Sketch, SketchSet},
13    std::{axis_or_reference::Axis2dOrEdgeReference, Args},
14};
15
16/// Data for a mirror.
17#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
18#[ts(export)]
19#[serde(rename_all = "camelCase")]
20pub struct Mirror2dData {
21    /// Axis to use as mirror.
22    pub axis: Axis2dOrEdgeReference,
23}
24
25/// Mirror a sketch.
26///
27/// Only works on unclosed sketches for now.
28pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
29    let (data, sketch_set): (Mirror2dData, SketchSet) = args.get_data_and_sketch_set()?;
30
31    let sketches = inner_mirror_2d(data, sketch_set, exec_state, args).await?;
32    Ok(sketches.into())
33}
34
35/// Mirror a sketch.
36///
37/// Only works on unclosed sketches for now.
38///
39/// Mirror occurs around a local sketch axis rather than a global axis.
40///
41/// ```no_run
42/// // Mirror an un-closed sketch across the Y axis.
43/// sketch001 = startSketchOn('XZ')
44///     |> startProfileAt([0, 10], %)
45///     |> line(end = [15, 0])
46///     |> line(end = [-7, -3])
47///     |> line(end = [9, -1])
48///     |> line(end = [-8, -5])
49///     |> line(end = [9, -3])
50///     |> line(end = [-8, -3])
51///     |> line(end = [9, -1])
52///     |> line(end = [-19, -0])
53///     |> mirror2d({axis = 'Y'}, %)
54///
55/// example = extrude(sketch001, length = 10)
56/// ```
57///
58/// ```no_run
59/// // Mirror a un-closed sketch across the Y axis.
60/// sketch001 = startSketchOn('XZ')
61///     |> startProfileAt([0, 8.5], %)
62///     |> line(end = [20, -8.5])
63///     |> line(end = [-20, -8.5])
64///     |> mirror2d({axis = 'Y'}, %)
65///
66/// example = extrude(sketch001, length = 10)
67/// ```
68///
69/// ```no_run
70/// // Mirror a un-closed sketch across an edge.
71/// helper001 = startSketchOn('XZ')
72///  |> startProfileAt([0, 0], %)
73///  |> line(end = [0, 10], tag = $edge001)
74///
75/// sketch001 = startSketchOn('XZ')
76///     |> startProfileAt([0, 8.5], %)
77///     |> line(end = [20, -8.5])
78///     |> line(end = [-20, -8.5])
79///     |> mirror2d({axis = edge001}, %)
80///
81/// // example = extrude(sketch001, length = 10)
82/// ```
83///
84/// ```no_run
85/// // Mirror an un-closed sketch across a custom axis.
86/// sketch001 = startSketchOn('XZ')
87///     |> startProfileAt([0, 8.5], %)
88///     |> line(end = [20, -8.5])
89///     |> line(end = [-20, -8.5])
90///     |> mirror2d({
91///   axis = {
92///     custom = {
93///       axis = [0.0, 1.0],
94///       origin = [0.0, 0.0]
95///     }
96///   }
97/// }, %)
98///
99/// example = extrude(sketch001, length = 10)
100/// ```
101#[stdlib {
102    name = "mirror2d",
103}]
104async fn inner_mirror_2d(
105    data: Mirror2dData,
106    sketch_set: SketchSet,
107    exec_state: &mut ExecState,
108    args: Args,
109) -> Result<Vec<Box<Sketch>>, KclError> {
110    let starting_sketches = match sketch_set {
111        SketchSet::Sketch(sketch) => vec![sketch],
112        SketchSet::Sketches(sketches) => sketches,
113    };
114
115    if args.ctx.no_engine_commands().await {
116        return Ok(starting_sketches);
117    }
118
119    match data.axis {
120        Axis2dOrEdgeReference::Axis(axis) => {
121            let (axis, origin) = axis.axis_and_origin()?;
122
123            args.batch_modeling_cmd(
124                exec_state.next_uuid(),
125                ModelingCmd::from(mcmd::EntityMirror {
126                    ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
127                    axis,
128                    point: origin,
129                }),
130            )
131            .await?;
132        }
133        Axis2dOrEdgeReference::Edge(edge) => {
134            let edge_id = edge.get_engine_id(exec_state, &args)?;
135
136            args.batch_modeling_cmd(
137                exec_state.next_uuid(),
138                ModelingCmd::from(mcmd::EntityMirrorAcrossEdge {
139                    ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
140                    edge_id,
141                }),
142            )
143            .await?;
144        }
145    };
146
147    Ok(starting_sketches)
148}