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