kittycad_modeling_cmds/
coord.rs

1use parse_display::{Display, FromStr};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5/// Co-ordinate axis specifier.
6///
7/// See [cglearn.eu] for background reading.
8///
9/// [cglearn.eu]: https://cglearn.eu/pub/computer-graphics/introduction-to-geometry#material-coordinate-systems-1
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
11#[serde(rename_all = "snake_case")]
12#[display(style = "snake_case")]
13#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
14#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
15#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass_enum)]
16pub enum Axis {
17    /// 'Y' axis.
18    Y = 1,
19    /// 'Z' axis.
20    Z = 2,
21}
22
23/// Specifies the sign of a co-ordinate axis.
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
25#[serde(rename_all = "snake_case")]
26#[display(style = "snake_case")]
27#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
28#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
29#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass_enum)]
30pub enum Direction {
31    /// Increasing numbers.
32    Positive = 1,
33    /// Decreasing numbers.
34    Negative = -1,
35}
36
37impl std::ops::Mul for Direction {
38    type Output = Self;
39    fn mul(self, rhs: Self) -> Self::Output {
40        match self as i32 * rhs as i32 {
41            1 => Direction::Positive,
42            -1 => Direction::Negative,
43            _ => unreachable!(),
44        }
45    }
46}
47
48/// An [`Axis`] paired with a [`Direction`].
49#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
50#[display("({axis}, {direction})")]
51#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
52#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
53#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass)]
54pub struct AxisDirectionPair {
55    /// Axis specifier.
56    pub axis: Axis,
57
58    /// Specifies which direction the axis is pointing.
59    pub direction: Direction,
60}
61
62/// Co-ordinate system definition.
63///
64/// The `up` axis must be orthogonal to the `forward` axis.
65///
66/// See [cglearn.eu] for background reading.
67///
68/// [cglearn.eu](https://cglearn.eu/pub/computer-graphics/introduction-to-geometry#material-coordinate-systems-1)
69#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
70#[display("forward: {forward}, up: {up}")]
71#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
72#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
73#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass)]
74pub struct System {
75    /// Axis the front face of a model looks along.
76    pub forward: AxisDirectionPair,
77    /// Axis pointing up and away from a model.
78    pub up: AxisDirectionPair,
79}
80
81/// KittyCAD co-ordinate system.
82///
83/// * Forward: -Y
84/// * Up: +Z
85/// * Handedness: Right
86pub const KITTYCAD: &System = &System {
87    // -Y
88    forward: AxisDirectionPair {
89        axis: Axis::Y,
90        direction: Direction::Negative,
91    },
92    // +Z
93    up: AxisDirectionPair {
94        axis: Axis::Z,
95        direction: Direction::Positive,
96    },
97};
98
99/// OpenGL co-ordinate system.
100///
101/// * Forward: +Z
102/// * Up: +Y
103/// * Handedness: Right
104pub const OPENGL: &System = &System {
105    // +Z
106    forward: AxisDirectionPair {
107        axis: Axis::Z,
108        direction: Direction::Positive,
109    },
110    // +Y
111    up: AxisDirectionPair {
112        axis: Axis::Y,
113        direction: Direction::Positive,
114    },
115};
116
117/// Vulkan co-ordinate system.
118///
119/// * Forward: +Z
120/// * Up: -Y
121/// * Handedness: Left
122pub const VULKAN: &System = &System {
123    // +Z
124    forward: AxisDirectionPair {
125        axis: Axis::Z,
126        direction: Direction::Positive,
127    },
128    // -Y
129    up: AxisDirectionPair {
130        axis: Axis::Y,
131        direction: Direction::Negative,
132    },
133};
134
135/// Perform co-ordinate system transform.
136///
137/// # Examples
138///
139/// KittyCAD (+Z up, -Y forward) to OpenGL (+Y up, +Z forward):
140///
141/// ```
142/// # use kittycad_modeling_cmds::coord::*;
143/// let a = [1.0, 2.0, 3.0];
144/// let b = transform(a, KITTYCAD, OPENGL);
145/// assert_eq!(b, [1.0, 3.0, -2.0]);
146/// ```
147///
148/// OpenGL (+Y up, +Z forward) to KittyCAD (+Z up, -Y forward):
149///
150/// ```
151/// # use kittycad_modeling_cmds::coord::*;
152/// let a = [1.0, 2.0, 3.0];
153/// let b = transform(a, OPENGL, KITTYCAD);
154/// assert_eq!(b, [1.0, -3.0, 2.0]);
155/// ```
156///
157/// KittyCAD (+Z up, -Y forward) to Vulkan (-Y up, +Z forward):
158///
159/// ```
160/// # use kittycad_modeling_cmds::coord::*;
161/// let a = [1.0, 2.0, 3.0];
162/// let b = transform(a, KITTYCAD, VULKAN);
163/// assert_eq!(b, [1.0, -3.0, -2.0]);
164/// ```
165///
166/// OpenGL (+Y up, +Z forward) to Vulkan (-Y up, +Z forward):
167///
168/// ```
169/// # use kittycad_modeling_cmds::coord::*;
170/// let a = [1.0, 2.0, 3.0];
171/// let b = transform(a, OPENGL, VULKAN);
172/// assert_eq!(b, [1.0, -2.0, 3.0]);
173/// ```
174#[inline]
175pub fn transform(a: [f32; 3], from: &System, to: &System) -> [f32; 3] {
176    let mut b = a;
177    b[to.forward.axis as usize] =
178        (from.forward.direction * to.forward.direction) as i32 as f32 * a[from.forward.axis as usize];
179    b[to.up.axis as usize] = (from.up.direction * to.up.direction) as i32 as f32 * a[from.up.axis as usize];
180    b
181}