kcl_lib/std/
planes.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Standard library plane helpers.

use derive_docs::stdlib;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    errors::KclError,
    executor::{ExecState, KclValue, Metadata, Plane, UserVal},
    std::{sketch::PlaneData, Args},
};

/// One of the standard planes.
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum StandardPlane {
    /// The XY plane.
    #[serde(rename = "XY", alias = "xy")]
    XY,
    /// The opposite side of the XY plane.
    #[serde(rename = "-XY", alias = "-xy")]
    NegXY,
    /// The XZ plane.
    #[serde(rename = "XZ", alias = "xz")]
    XZ,
    /// The opposite side of the XZ plane.
    #[serde(rename = "-XZ", alias = "-xz")]
    NegXZ,
    /// The YZ plane.
    #[serde(rename = "YZ", alias = "yz")]
    YZ,
    /// The opposite side of the YZ plane.
    #[serde(rename = "-YZ", alias = "-yz")]
    NegYZ,
}

impl From<StandardPlane> for PlaneData {
    fn from(value: StandardPlane) -> Self {
        match value {
            StandardPlane::XY => PlaneData::XY,
            StandardPlane::NegXY => PlaneData::NegXY,
            StandardPlane::XZ => PlaneData::XZ,
            StandardPlane::NegXZ => PlaneData::NegXZ,
            StandardPlane::YZ => PlaneData::YZ,
            StandardPlane::NegYZ => PlaneData::NegYZ,
        }
    }
}

/// Offset a plane by a distance along its normal.
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
    let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;

    let plane = inner_offset_plane(std_plane, offset, exec_state).await?;

    Ok(KclValue::UserVal(UserVal::new(
        vec![Metadata {
            source_range: args.source_range,
        }],
        plane,
    )))
}

/// Offset a plane by a distance along its normal.
///
/// For example, if you offset the 'XZ' plane by 10, the new plane will be parallel to the 'XZ'
/// plane and 10 units away from it.
///
/// ```no_run
/// // Loft a square and a circle on the `XY` plane using offset.
/// const squareSketch = startSketchOn('XY')
///     |> startProfileAt([-100, 200], %)
///     |> line([200, 0], %)
///     |> line([0, -200], %)
///     |> line([-200, 0], %)
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
///     |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('XY', 150))
///     |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```no_run
/// // Loft a square and a circle on the `XZ` plane using offset.
/// const squareSketch = startSketchOn('XZ')
///     |> startProfileAt([-100, 200], %)
///     |> line([200, 0], %)
///     |> line([0, -200], %)
///     |> line([-200, 0], %)
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
///     |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('XZ', 150))
///     |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```no_run
/// // Loft a square and a circle on the `YZ` plane using offset.
/// const squareSketch = startSketchOn('YZ')
///     |> startProfileAt([-100, 200], %)
///     |> line([200, 0], %)
///     |> line([0, -200], %)
///     |> line([-200, 0], %)
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
///     |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('YZ', 150))
///     |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```no_run
/// // Loft a square and a circle on the `-XZ` plane using offset.
/// const squareSketch = startSketchOn('-XZ')
///     |> startProfileAt([-100, 200], %)
///     |> line([200, 0], %)
///     |> line([0, -200], %)
///     |> line([-200, 0], %)
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
///     |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('-XZ', -150))
///     |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
#[stdlib {
    name = "offsetPlane",
}]
async fn inner_offset_plane(
    std_plane: StandardPlane,
    offset: f64,
    exec_state: &mut ExecState,
) -> Result<PlaneData, KclError> {
    // Convert to the plane type.
    let plane_data: PlaneData = std_plane.into();
    // Convert to a plane.
    let mut plane = Plane::from_plane_data(plane_data, exec_state);

    match std_plane {
        StandardPlane::XY => {
            plane.origin.z += offset;
        }
        StandardPlane::XZ => {
            plane.origin.y -= offset;
        }
        StandardPlane::YZ => {
            plane.origin.x += offset;
        }
        StandardPlane::NegXY => {
            plane.origin.z -= offset;
        }
        StandardPlane::NegXZ => {
            plane.origin.y += offset;
        }
        StandardPlane::NegYZ => {
            plane.origin.x -= offset;
        }
    }

    Ok(PlaneData::Plane {
        origin: Box::new(plane.origin),
        x_axis: Box::new(plane.x_axis),
        y_axis: Box::new(plane.y_axis),
        z_axis: Box::new(plane.z_axis),
    })
}