New node: Assign Colors (#1938)

* New node: Assign Colors

* Simplify the Procedural String Lights demo artwork

* Add "Group" node to the node catalog

* Better comment styling in profiling action
This commit is contained in:
Keavon Chambers
2024-08-15 05:52:46 -07:00
committed by GitHub
parent 12ebc6f972
commit fa981a0897
12 changed files with 573 additions and 231 deletions

View File

@@ -55,6 +55,22 @@ impl core::hash::Hash for GraphicGroup {
}
}
impl GraphicGroup {
pub const EMPTY: Self = Self {
elements: Vec::new(),
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::new(),
};
pub fn new(elements: Vec<GraphicElement>) -> Self {
Self {
elements,
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::new(),
}
}
}
/// The possible forms of graphical content held in a Vec by the `elements` field of [`GraphicElement`].
/// Can be another recursively nested [`GraphicGroup`], a [`VectorData`] shape, an [`ImageFrame`], or an [`Artboard`].
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
@@ -74,6 +90,50 @@ impl Default for GraphicElement {
}
}
impl GraphicElement {
pub fn as_group(&self) -> Option<&GraphicGroup> {
match self {
GraphicElement::GraphicGroup(group) => Some(group),
_ => None,
}
}
pub fn as_group_mut(&mut self) -> Option<&mut GraphicGroup> {
match self {
GraphicElement::GraphicGroup(group) => Some(group),
_ => None,
}
}
pub fn as_vector_data(&self) -> Option<&VectorData> {
match self {
GraphicElement::VectorData(data) => Some(data),
_ => None,
}
}
pub fn as_vector_data_mut(&mut self) -> Option<&mut VectorData> {
match self {
GraphicElement::VectorData(data) => Some(data),
_ => None,
}
}
pub fn as_raster(&self) -> Option<&Raster> {
match self {
GraphicElement::Raster(raster) => Some(raster),
_ => None,
}
}
pub fn as_raster_mut(&mut self) -> Option<&mut Raster> {
match self {
GraphicElement::Raster(raster) => Some(raster),
_ => None,
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
pub enum Raster {
/// A bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
@@ -301,11 +361,3 @@ where
}
}
}
impl GraphicGroup {
pub const EMPTY: Self = Self {
elements: Vec::new(),
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::new(),
};
}

View File

@@ -60,6 +60,10 @@ impl GradientStops {
Color::BLACK
}
pub fn reversed(&self) -> Self {
Self(self.0.iter().rev().map(|(position, color)| (1. - position, *color)).collect())
}
}
/// A gradient fill.

View File

@@ -1,5 +1,5 @@
use super::misc::CentroidType;
use super::style::{Fill, Stroke};
use super::style::{Fill, GradientStops, Stroke};
use super::{PointId, SegmentId, StrokeId, VectorData};
use crate::renderer::GraphicElementRendered;
use crate::transform::{Footprint, Transform, TransformMut};
@@ -9,6 +9,78 @@ use bezier_rs::{Cap, Join, Subpath, SubpathTValue, TValue};
use glam::{DAffine2, DVec2};
use rand::{Rng, SeedableRng};
#[derive(Debug, Clone, Copy)]
pub struct AssignColorsNode<Fill, Stroke, Gradient, Reverse, Randomize, RepeatEvery> {
fill: Fill,
stroke: Stroke,
gradient: Gradient,
reverse: Reverse,
randomize: Randomize,
repeat_every: RepeatEvery,
}
#[node_macro::node_fn(AssignColorsNode)]
fn assign_colors_node(group: GraphicGroup, fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) -> GraphicGroup {
let mut group = group;
let vector_data_list: Vec<_> = group.iter_mut().filter_map(|element| element.as_vector_data_mut()).collect();
let list = (vector_data_list.len(), vector_data_list.into_iter());
assign_colors(list, fill, stroke, gradient, reverse, randomize, repeat_every);
group
}
#[node_macro::node_impl(AssignColorsNode)]
fn assign_colors_node(vector_data: VectorData, fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) -> GraphicGroup {
let mut vector_data_list: Vec<_> = vector_data
.region_bezier_paths()
.map(|(_, subpath)| {
let mut vector = VectorData::from_subpath(subpath);
vector.style = vector_data.style.clone();
crate::GraphicElement::VectorData(Box::new(vector))
})
.collect();
let list = (vector_data_list.len(), vector_data_list.iter_mut().map(|element| element.as_vector_data_mut().unwrap()));
assign_colors(list, fill, stroke, gradient, reverse, randomize, repeat_every);
let mut group = GraphicGroup::new(vector_data_list);
group.transform = vector_data.transform;
group.alpha_blending = vector_data.alpha_blending;
group
}
fn assign_colors<'a>((length, vector_data): (usize, impl Iterator<Item = &'a mut VectorData>), fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) {
let gradient = if reverse { gradient.reversed() } else { gradient };
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
for (i, vector_data) in vector_data.enumerate() {
let factor = match randomize {
true => rng.gen::<f64>(),
false => match repeat_every {
0 => i as f64 / (length - 1) as f64,
1 => 0.,
_ => i as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
},
};
let color = gradient.evalute(factor);
if fill {
vector_data.style.set_fill(Fill::Solid(color));
}
if stroke {
if let Some(stroke) = vector_data.style.stroke().and_then(|stroke| stroke.with_color(&Some(color))) {
vector_data.style.set_stroke(stroke);
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SetFillNode<Fill> {
fill: Fill,