Skip to main content

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