-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathtransform_nodes.rs
More file actions
166 lines (152 loc) · 5.28 KB
/
transform_nodes.rs
File metadata and controls
166 lines (152 loc) · 5.28 KB
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
use core::f64;
use core_types::color::Color;
use core_types::table::Table;
use core_types::transform::{ApplyTransform, ScaleType, Transform};
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, InjectFootprint, ModifyFootprint, OwnedContextImpl};
use glam::{DAffine2, DMat2, DVec2};
use graphic_types::Graphic;
use graphic_types::Vector;
use graphic_types::raster_types::{CPU, GPU, Raster};
use vector_types::GradientStops;
/// Applies the specified transform to the input value, which may be a graphic type or another transform.
#[node_macro::node(category(""))]
async fn transform<T: ApplyTransform + 'n + 'static>(
ctx: impl Ctx + CloneVarArgs + ExtractAll + ModifyFootprint,
#[implementations(
Context -> DAffine2,
Context -> DVec2,
Context -> Table<Graphic>,
Context -> Table<Vector>,
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<GradientStops>,
)]
content: impl Node<Context<'static>, Output = T>,
translation: DVec2,
rotation: f64,
scale: DVec2,
skew: DVec2,
) -> T {
let trs = DAffine2::from_scale_angle_translation(scale, rotation.to_radians(), translation);
let skew = DAffine2::from_cols_array(&[1., skew.y.to_radians().tan(), skew.x.to_radians().tan(), 1., 0., 0.]);
let matrix = trs * skew;
let footprint = ctx.try_footprint().copied();
let mut ctx = OwnedContextImpl::from(ctx);
if let Some(mut footprint) = footprint {
footprint.apply_transform(&matrix);
ctx = ctx.with_footprint(footprint);
}
let mut transform_target = content.eval(ctx.into_context()).await;
transform_target.left_apply_transform(&matrix);
transform_target
}
/// Resets the desired components of the input transform to their default values. If all components are reset, the output will be set to the identity transform.
/// Shear is represented jointly by rotation and scale, so resetting both will also remove any shear.
#[node_macro::node(category("Math: Transform"))]
fn reset_transform<T>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
mut content: Table<T>,
#[default(true)] reset_translation: bool,
reset_rotation: bool,
reset_scale: bool,
) -> Table<T> {
for row in content.iter_mut() {
// Translation
if reset_translation {
row.transform.translation = DVec2::ZERO;
}
// (Rotation, Scale)
match (reset_rotation, reset_scale) {
(true, true) => {
row.transform.matrix2 = DMat2::IDENTITY;
}
(true, false) => {
let scale = row.transform.scale_magnitudes();
row.transform.matrix2 = DMat2::from_diagonal(scale);
}
(false, true) => {
let rotation = row.transform.decompose_rotation();
let rotation_matrix = DMat2::from_angle(rotation);
row.transform.matrix2 = rotation_matrix;
}
(false, false) => {}
}
}
content
}
/// Overwrites the transform of each element in the input table with the specified transform.
#[node_macro::node(category("Math: Transform"))]
fn replace_transform<T>(
_: impl Ctx + InjectFootprint,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
mut content: Table<T>,
transform: DAffine2,
) -> Table<T> {
for row in content.iter_mut() {
*row.transform = transform.transform();
}
content
}
// TODO: Figure out how this node should behave once #2982 is implemented.
/// Obtains the transform of the first element in the input table, if present.
#[node_macro::node(category("Math: Transform"), path(core_types::vector))]
async fn extract_transform<T>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
content: Table<T>,
) -> DAffine2 {
content.iter().next().map(|row| *row.transform).unwrap_or_default()
}
/// Produces the inverse of the input transform, which is the transform that undoes the effect of the original transform.
#[node_macro::node(category("Math: Transform"))]
fn invert_transform(_: impl Ctx, transform: DAffine2) -> DAffine2 {
transform.inverse()
}
/// Extracts the translation component from the input transform.
#[node_macro::node(category("Math: Transform"))]
fn decompose_translation(_: impl Ctx, transform: DAffine2) -> DVec2 {
transform.translation
}
/// Extracts the rotation component (in degrees) from the input transform.
#[node_macro::node(category("Math: Transform"))]
fn decompose_rotation(_: impl Ctx, transform: DAffine2) -> f64 {
transform.decompose_rotation().to_degrees()
}
/// Extracts the scale component from the input transform.
/// **Magnitude** returns the visual length of each axis (always positive, includes any skew contribution).
/// **Pure** returns the isolated scale factors with rotation and skew stripped away (can be negative for flipped axes).
#[node_macro::node(category("Math: Transform"))]
fn decompose_scale(_: impl Ctx, transform: DAffine2, scale_type: ScaleType) -> DVec2 {
match scale_type {
ScaleType::Magnitude => transform.scale_magnitudes(),
ScaleType::Pure => transform.decompose_scale(),
}
}
/// Extracts the skew coefficient (in degrees) from the input transform.
#[node_macro::node(category("Math: Transform"))]
fn decompose_skew(_: impl Ctx, transform: DAffine2) -> f64 {
transform.decompose_skew().atan().to_degrees()
}