Skip to main content

bymsdfgen_core/generator/
projection.rs

1//! Spatial and distance-value transformations.
2//! Ports of `Projection`, `DistanceMapping`, `SDFTransformation`.
3
4use crate::math::typed::{PixelPoint, ShapePoint};
5use crate::math::{Range, Vector2};
6
7/// Affine transformation between shape space and pixel space.
8#[derive(Debug, Clone, Copy)]
9pub struct Projection {
10    scale: Vector2,
11    translate: Vector2,
12}
13
14impl Default for Projection {
15    fn default() -> Self {
16        Projection {
17            scale: Vector2::splat(1.0),
18            translate: Vector2::ZERO,
19        }
20    }
21}
22
23impl Projection {
24    pub fn new(scale: Vector2, translate: Vector2) -> Self {
25        Projection { scale, translate }
26    }
27
28    /// Shape coordinate → pixel coordinate (typed boundary crossing).
29    #[inline]
30    pub fn project(&self, coord: ShapePoint) -> PixelPoint {
31        PixelPoint::from_raw(self.scale * (coord.raw() + self.translate))
32    }
33
34    /// Pixel coordinate → shape coordinate (typed boundary crossing).
35    #[inline]
36    pub fn unproject(&self, coord: PixelPoint) -> ShapePoint {
37        ShapePoint::from_raw(coord.raw() / self.scale - self.translate)
38    }
39
40    #[inline]
41    pub fn project_vector(&self, v: Vector2) -> Vector2 {
42        self.scale * v
43    }
44
45    #[inline]
46    pub fn unproject_vector(&self, v: Vector2) -> Vector2 {
47        v / self.scale
48    }
49
50    #[inline]
51    pub fn project_x(&self, x: f64) -> f64 {
52        self.scale.x * (x + self.translate.x)
53    }
54    #[inline]
55    pub fn project_y(&self, y: f64) -> f64 {
56        self.scale.y * (y + self.translate.y)
57    }
58    #[inline]
59    pub fn unproject_x(&self, x: f64) -> f64 {
60        x / self.scale.x - self.translate.x
61    }
62    #[inline]
63    pub fn unproject_y(&self, y: f64) -> f64 {
64        y / self.scale.y - self.translate.y
65    }
66
67    #[inline]
68    pub fn scale(&self) -> Vector2 {
69        self.scale
70    }
71    #[inline]
72    pub fn translate(&self) -> Vector2 {
73        self.translate
74    }
75}
76
77/// Linear mapping of signed distance values into normalized field values.
78#[derive(Debug, Clone, Copy)]
79pub struct DistanceMapping {
80    scale: f64,
81    translate: f64,
82}
83
84impl Default for DistanceMapping {
85    fn default() -> Self {
86        DistanceMapping {
87            scale: 1.0,
88            translate: 0.0,
89        }
90    }
91}
92
93impl DistanceMapping {
94    /// Maps `[range.lower, range.upper]` onto `[0, 1]`.
95    pub fn from_range(range: Range) -> Self {
96        DistanceMapping {
97            scale: 1.0 / (range.upper - range.lower),
98            translate: -range.lower,
99        }
100    }
101
102    /// The inverse mapping that takes a `range` back from normalized values.
103    pub fn inverse_of_range(range: Range) -> Self {
104        let range_width = range.upper - range.lower;
105        DistanceMapping {
106            scale: range_width,
107            translate: range.lower / if range_width != 0.0 { range_width } else { 1.0 },
108        }
109    }
110
111    fn raw(scale: f64, translate: f64) -> Self {
112        DistanceMapping { scale, translate }
113    }
114
115    /// Map an absolute distance.
116    #[inline]
117    pub fn map(&self, d: f64) -> f64 {
118        self.scale * (d + self.translate)
119    }
120
121    /// Map a distance *delta* (no translation applied).
122    #[inline]
123    pub fn map_delta(&self, d: f64) -> f64 {
124        self.scale * d
125    }
126
127    pub fn inverse(&self) -> Self {
128        DistanceMapping::raw(1.0 / self.scale, -self.scale * self.translate)
129    }
130}
131
132/// Combined spatial + distance-value transformation.
133#[derive(Debug, Clone, Copy, Default)]
134pub struct SdfTransformation {
135    pub projection: Projection,
136    pub distance_mapping: DistanceMapping,
137}
138
139impl SdfTransformation {
140    pub fn new(projection: Projection, distance_mapping: DistanceMapping) -> Self {
141        SdfTransformation {
142            projection,
143            distance_mapping,
144        }
145    }
146}