Skip to content

Commit cde7d5f

Browse files
authored
New nodes: Colors to Gradient, Flatten Color, and Flatten Gradient (#3835)
* New nodes: Colors to Gradient, Flatten Color, and Flatten Gradient * Fix Data panel not showing GradientStops * Fix wrong category
1 parent da7437c commit cde7d5f

File tree

3 files changed

+167
-36
lines changed

3 files changed

+167
-36
lines changed

editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,12 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
166166
Table<Raster<GPU>>,
167167
Table<Color>,
168168
Table<GradientStops>,
169-
Vec<String>,
169+
GradientStops,
170170
f64,
171171
u32,
172172
u64,
173173
bool,
174+
Vec<String>,
174175
String,
175176
Option<f64>,
176177
DVec2,

node-graph/libraries/graphic-types/src/graphic.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,102 @@ pub trait IntoGraphicTable {
204204
flatten_table(&mut output, content);
205205
output
206206
}
207+
208+
/// Deeply flattens any color content within a graphic table, discarding non-color content, and returning a table of only color elements.
209+
fn into_flattened_color_table(self) -> Table<Color>
210+
where
211+
Self: std::marker::Sized,
212+
{
213+
let content = self.into_graphic_table();
214+
215+
fn flatten_table(output_color_table: &mut Table<Color>, current_graphic_table: Table<Graphic>) {
216+
for current_graphic_row in current_graphic_table.iter() {
217+
let current_graphic = current_graphic_row.element.clone();
218+
let source_node_id = *current_graphic_row.source_node_id;
219+
220+
match current_graphic {
221+
// If we're allowed to recurse, flatten any tables we encounter
222+
Graphic::Graphic(mut current_graphic_table) => {
223+
// Apply the parent graphic's transform to all child elements
224+
for graphic in current_graphic_table.iter_mut() {
225+
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
226+
}
227+
228+
flatten_table(output_color_table, current_graphic_table);
229+
}
230+
// Push any leaf Color elements we encounter
231+
Graphic::Color(color_table) => {
232+
for current_color_row in color_table.iter() {
233+
output_color_table.push(TableRow {
234+
element: *current_color_row.element,
235+
transform: *current_graphic_row.transform * *current_color_row.transform,
236+
alpha_blending: AlphaBlending {
237+
blend_mode: current_color_row.alpha_blending.blend_mode,
238+
opacity: current_graphic_row.alpha_blending.opacity * current_color_row.alpha_blending.opacity,
239+
fill: current_color_row.alpha_blending.fill,
240+
clip: current_color_row.alpha_blending.clip,
241+
},
242+
source_node_id,
243+
});
244+
}
245+
}
246+
_ => {}
247+
}
248+
}
249+
}
250+
251+
let mut output = Table::new();
252+
flatten_table(&mut output, content);
253+
output
254+
}
255+
256+
/// Deeply flattens any gradient content within a graphic table, discarding non-gradient content, and returning a table of only gradient elements.
257+
fn into_flattened_gradient_table(self) -> Table<GradientStops>
258+
where
259+
Self: std::marker::Sized,
260+
{
261+
let content = self.into_graphic_table();
262+
263+
fn flatten_table(output_gradient_table: &mut Table<GradientStops>, current_graphic_table: Table<Graphic>) {
264+
for current_graphic_row in current_graphic_table.iter() {
265+
let current_graphic = current_graphic_row.element.clone();
266+
let source_node_id = *current_graphic_row.source_node_id;
267+
268+
match current_graphic {
269+
// If we're allowed to recurse, flatten any tables we encounter
270+
Graphic::Graphic(mut current_graphic_table) => {
271+
// Apply the parent graphic's transform to all child elements
272+
for graphic in current_graphic_table.iter_mut() {
273+
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
274+
}
275+
276+
flatten_table(output_gradient_table, current_graphic_table);
277+
}
278+
// Push any leaf GradientStops elements we encounter
279+
Graphic::Gradient(gradient_table) => {
280+
for current_gradient_row in gradient_table.iter() {
281+
output_gradient_table.push(TableRow {
282+
element: current_gradient_row.element.clone(),
283+
transform: *current_graphic_row.transform * *current_gradient_row.transform,
284+
alpha_blending: AlphaBlending {
285+
blend_mode: current_gradient_row.alpha_blending.blend_mode,
286+
opacity: current_graphic_row.alpha_blending.opacity * current_gradient_row.alpha_blending.opacity,
287+
fill: current_gradient_row.alpha_blending.fill,
288+
clip: current_gradient_row.alpha_blending.clip,
289+
},
290+
source_node_id,
291+
});
292+
}
293+
}
294+
_ => {}
295+
}
296+
}
297+
}
298+
299+
let mut output = Table::new();
300+
flatten_table(&mut output, content);
301+
output
302+
}
207303
}
208304

209305
impl IntoGraphicTable for Table<Graphic> {

node-graph/nodes/graphic/src/graphic.rs

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,44 @@ use glam::{DAffine2, DVec2};
77
use graphic_types::graphic::{Graphic, IntoGraphicTable};
88
use graphic_types::{Artboard, Vector};
99
use raster_types::{CPU, GPU, Raster};
10-
use vector_types::{GradientStops, ReferencePoint};
10+
use vector_types::{GradientStop, GradientStops, ReferencePoint};
11+
12+
/// Returns the value at the specified index in the collection.
13+
/// If no value exists at that index, the type's default value is returned.
14+
#[node_macro::node(category("General"))]
15+
pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
16+
_: impl Ctx,
17+
/// The collection of data, such as a list or table.
18+
#[implementations(
19+
Vec<f64>,
20+
Vec<u32>,
21+
Vec<u64>,
22+
Vec<DVec2>,
23+
Vec<String>,
24+
Table<Artboard>,
25+
Table<Graphic>,
26+
Table<Vector>,
27+
Table<Raster<CPU>>,
28+
Table<Raster<GPU>>,
29+
Table<Color>,
30+
Table<GradientStops>,
31+
)]
32+
collection: T,
33+
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the collection, starting from -1 for the last item.
34+
index: SignedInteger,
35+
) -> T::Output
36+
where
37+
T::Output: Clone + Default,
38+
{
39+
let index = index as i32;
40+
41+
if index < 0 {
42+
collection.at_index_from_end(-index as usize)
43+
} else {
44+
collection.at_index(index as usize)
45+
}
46+
.unwrap_or_default()
47+
}
1148

1249
#[node_macro::node(category("General"))]
1350
async fn map<Item: AnyHash + Send + Sync + std::hash::Hash>(
@@ -261,45 +298,42 @@ pub async fn flatten_vector<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx
261298
content.into_flattened_vector_table()
262299
}
263300

264-
/// Converts a graphic table into a vector table by deeply flattening any vector content it contains, and discarding any non-vector content.
265-
#[node_macro::node(category("Vector"))]
301+
/// Converts a graphic table into a raster table by deeply flattening any raster content it contains, and discarding any non-raster content.
302+
#[node_macro::node(category("Raster"))]
266303
pub async fn flatten_raster<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Raster<CPU>>)] content: T) -> Table<Raster<CPU>> {
267304
content.into_flattened_raster_table()
268305
}
269306

270-
/// Returns the value at the specified index in the collection.
271-
/// If no value exists at that index, the type's default value is returned.
307+
/// Converts a graphic table into a color table by deeply flattening any color content it contains, and discarding any non-color content.
272308
#[node_macro::node(category("General"))]
273-
pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
274-
_: impl Ctx,
275-
/// The collection of data, such as a list or table.
276-
#[implementations(
277-
Vec<f64>,
278-
Vec<u32>,
279-
Vec<u64>,
280-
Vec<DVec2>,
281-
Vec<String>,
282-
Table<Artboard>,
283-
Table<Graphic>,
284-
Table<Vector>,
285-
Table<Raster<CPU>>,
286-
Table<Raster<GPU>>,
287-
Table<Color>,
288-
Table<GradientStops>,
289-
)]
290-
collection: T,
291-
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the collection, starting from -1 for the last item.
292-
index: SignedInteger,
293-
) -> T::Output
294-
where
295-
T::Output: Clone + Default,
296-
{
297-
let index = index as i32;
309+
pub async fn flatten_color<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] content: T) -> Table<Color> {
310+
content.into_flattened_color_table()
311+
}
298312

299-
if index < 0 {
300-
collection.at_index_from_end(-index as usize)
301-
} else {
302-
collection.at_index(index as usize)
313+
/// Converts a graphic table into a gradient table by deeply flattening any gradient content it contains, and discarding any non-gradient content.
314+
#[node_macro::node(category("General"))]
315+
pub async fn flatten_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<GradientStops>)] content: T) -> Table<GradientStops> {
316+
content.into_flattened_gradient_table()
317+
}
318+
319+
/// Constructs a gradient from a table of colors, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
320+
#[node_macro::node(category("Color"))]
321+
fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] colors: T) -> GradientStops {
322+
let colors = colors.into_flattened_color_table();
323+
let total_colors = colors.len();
324+
325+
if total_colors == 0 {
326+
return GradientStops::new(vec![GradientStop {
327+
position: 0.,
328+
midpoint: 0.5,
329+
color: Color::BLACK,
330+
}]);
303331
}
304-
.unwrap_or_default()
332+
333+
let colors = colors.into_iter().enumerate().map(|(index, row)| GradientStop {
334+
position: index as f64 / (total_colors - 1).max(1) as f64,
335+
midpoint: 0.5,
336+
color: row.element,
337+
});
338+
GradientStops::new(colors)
305339
}

0 commit comments

Comments
 (0)