mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
WIP
This commit is contained in:
@@ -5,7 +5,7 @@ use core_types::blending::BlendMode;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::list::{ATTR_FILL, ATTR_STROKE, Item, List, ListDyn};
|
||||
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
|
||||
use core_types::transform::{Footprint, Transform};
|
||||
use core_types::transform::{ApplyTransform, Footprint, Transform};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{
|
||||
ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, CloneVarArgs, Color, Context, Ctx, ExtractAll,
|
||||
@@ -38,6 +38,9 @@ trait VectorListIterMut {
|
||||
fn for_each_vector_mut(&mut self, f: impl FnMut(&mut Vector, DAffine2));
|
||||
|
||||
fn vector_count(&self) -> usize;
|
||||
|
||||
/// Iterates over a mutable reference to each vector item's transform, used by the transform-assignment nodes.
|
||||
fn vector_transforms_iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut DAffine2>;
|
||||
}
|
||||
|
||||
impl VectorListIterMut for List<Graphic> {
|
||||
@@ -54,6 +57,13 @@ impl VectorListIterMut for List<Graphic> {
|
||||
fn vector_count(&self) -> usize {
|
||||
self.iter_element_values().filter_map(|element| element.as_vector()).map(|list| list.len()).sum()
|
||||
}
|
||||
|
||||
fn vector_transforms_iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut DAffine2> {
|
||||
// Grab only the direct children
|
||||
self.iter_element_values_mut()
|
||||
.filter_map(|graphic| graphic.as_vector_mut())
|
||||
.flat_map(|vector_list| vector_list.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM))
|
||||
}
|
||||
}
|
||||
|
||||
impl VectorListIterMut for List<Vector> {
|
||||
@@ -67,6 +77,10 @@ impl VectorListIterMut for List<Vector> {
|
||||
fn vector_count(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn vector_transforms_iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut DAffine2> {
|
||||
self.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM)
|
||||
}
|
||||
}
|
||||
|
||||
/// Uniquely sets the fill and/or stroke style of every vector element to individual colors sampled along a chosen gradient.
|
||||
@@ -134,6 +148,239 @@ where
|
||||
content
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(core_types::vector))]
|
||||
async fn assign_rotations<T: VectorListIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(List<Graphic>, List<Vector>)] mut content: T,
|
||||
#[range((0., 360.))]
|
||||
#[default(360.)]
|
||||
angle: Angle,
|
||||
/// Whether to reverse the order.
|
||||
reverse: bool,
|
||||
/// Whether to randomize the rotation selection for each element.
|
||||
#[default(true)]
|
||||
randomize: bool,
|
||||
/// The seed used for randomization.
|
||||
seed: SeedValue,
|
||||
#[range((-50., 50.))] bias: f64,
|
||||
/// The number of elements to span across before repeating. A 0 value will span the entire range once.
|
||||
repeat_every: u32,
|
||||
) -> T {
|
||||
let count = content.vector_count() as u32;
|
||||
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||
|
||||
let apply = |(index, transform): (usize, &mut DAffine2)| {
|
||||
let factor = match randomize {
|
||||
true => {
|
||||
if bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(bias))).log2() / bias
|
||||
}
|
||||
}
|
||||
false => match repeat_every {
|
||||
0 => index as f64 / (count - 1).max(1) as f64,
|
||||
1 => 0.,
|
||||
_ => index as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
|
||||
},
|
||||
};
|
||||
let rotation_transform = DAffine2::from_angle(factor * angle.to_radians());
|
||||
transform.left_apply_transform(&rotation_transform);
|
||||
};
|
||||
|
||||
if reverse {
|
||||
content.vector_transforms_iter_mut().rev().enumerate().for_each(apply);
|
||||
} else {
|
||||
content.vector_transforms_iter_mut().enumerate().for_each(apply);
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(core_types::vector))]
|
||||
async fn assign_translations<T: VectorListIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(List<Graphic>, List<Vector>)] mut content: T,
|
||||
x_min: f64,
|
||||
x_max: f64,
|
||||
y_min: f64,
|
||||
y_max: f64,
|
||||
/// Whether to reverse the order.
|
||||
reverse: bool,
|
||||
/// Whether to randomize the translation selection for each element.
|
||||
#[default(true)]
|
||||
randomize: bool,
|
||||
/// The seed used for randomization.
|
||||
seed: SeedValue,
|
||||
#[range((-50., 50.))] x_bias: f64,
|
||||
#[range((-50., 50.))] y_bias: f64,
|
||||
/// The number of elements to span across before repeating. A 0 value will span the entire range once.
|
||||
repeat_every: u32,
|
||||
) -> T {
|
||||
let count = content.vector_count() as u32;
|
||||
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||
|
||||
let apply = |(index, transform): (usize, &mut DAffine2)| {
|
||||
let (factor_x, factor_y) = match randomize {
|
||||
true => {
|
||||
let factor_x = if x_bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(x_bias))).log2() / x_bias
|
||||
};
|
||||
let factor_y = if y_bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(y_bias))).log2() / y_bias
|
||||
};
|
||||
(factor_x, factor_y)
|
||||
}
|
||||
false => match repeat_every {
|
||||
0 => {
|
||||
let factor = index as f64 / (count - 1).max(1) as f64;
|
||||
(factor, factor)
|
||||
}
|
||||
1 => (0., 0.),
|
||||
_ => {
|
||||
let factor = index as f64 % repeat_every as f64 / (repeat_every - 1) as f64;
|
||||
(factor, factor)
|
||||
}
|
||||
},
|
||||
};
|
||||
let translation = DVec2::new(x_min + (x_max - x_min) * factor_x, y_min + (y_max - y_min) * factor_y);
|
||||
let translation_transform = DAffine2::from_translation(translation);
|
||||
transform.left_apply_transform(&translation_transform);
|
||||
};
|
||||
|
||||
if reverse {
|
||||
content.vector_transforms_iter_mut().rev().enumerate().for_each(apply);
|
||||
} else {
|
||||
content.vector_transforms_iter_mut().enumerate().for_each(apply);
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
// TODO: decide if we want to keep both circular and non-circular versions
|
||||
#[node_macro::node(category("Instancing"), path(core_types::vector))]
|
||||
async fn assign_translations_circular<T: VectorListIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(List<Graphic>, List<Vector>)] mut content: T,
|
||||
radius_min: f64,
|
||||
radius_max: f64,
|
||||
#[default(-180.)] angle_min: Angle,
|
||||
#[default(180.)] angle_max: Angle,
|
||||
/// Whether to reverse the order.
|
||||
reverse: bool,
|
||||
/// Whether to randomize the translation selection for each element.
|
||||
#[default(true)]
|
||||
randomize: bool,
|
||||
/// The seed used for randomization.
|
||||
seed: SeedValue,
|
||||
#[range((-50., 50.))] radius_bias: f64,
|
||||
#[range((-50., 50.))] angle_bias: f64,
|
||||
/// The number of elements to span across before repeating. A 0 value will span the entire range once.
|
||||
repeat_every: u32,
|
||||
) -> T {
|
||||
let count = content.vector_count() as u32;
|
||||
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||
|
||||
let apply = |(index, transform): (usize, &mut DAffine2)| {
|
||||
let (factor_angle, factor_radius) = match randomize {
|
||||
true => {
|
||||
let factor_angle = if angle_bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(angle_bias))).log2() / angle_bias
|
||||
};
|
||||
let factor_radius = if radius_bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(radius_bias))).log2() / radius_bias
|
||||
};
|
||||
(factor_angle, factor_radius)
|
||||
}
|
||||
false => match repeat_every {
|
||||
0 => {
|
||||
let factor = index as f64 / (count - 1).max(1) as f64;
|
||||
(factor, factor)
|
||||
}
|
||||
1 => (0., 0.),
|
||||
_ => {
|
||||
let factor = index as f64 % repeat_every as f64 / (repeat_every - 1) as f64;
|
||||
(factor, factor)
|
||||
}
|
||||
},
|
||||
};
|
||||
let angle = factor_angle * (angle_max.to_radians() - angle_min.to_radians()) + angle_min.to_radians();
|
||||
let radius = radius_min + (radius_max - radius_min) * factor_radius.sqrt();
|
||||
let translation = DVec2::new(angle.cos() * radius, angle.sin() * radius);
|
||||
let translation_transform = DAffine2::from_translation(translation);
|
||||
transform.left_apply_transform(&translation_transform);
|
||||
};
|
||||
|
||||
if reverse {
|
||||
content.vector_transforms_iter_mut().rev().enumerate().for_each(apply);
|
||||
} else {
|
||||
content.vector_transforms_iter_mut().enumerate().for_each(apply);
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(core_types::vector))]
|
||||
async fn assign_scales<T: VectorListIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(List<Graphic>, List<Vector>)] mut content: T,
|
||||
#[default(0.5)] min_scale: f64,
|
||||
#[default(2.)] max_scale: f64,
|
||||
/// Whether to reverse the order.
|
||||
reverse: bool,
|
||||
/// Whether to randomize the scale selection for each element.
|
||||
#[default(true)]
|
||||
randomize: bool,
|
||||
/// The seed used for randomization.
|
||||
seed: SeedValue,
|
||||
#[range((-50., 50.))] bias: f64,
|
||||
/// The number of elements to span across before repeating. A 0 value will span the entire range once.
|
||||
repeat_every: u32,
|
||||
) -> T {
|
||||
let count = content.vector_count() as u32;
|
||||
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||
|
||||
let apply = |(index, transform): (usize, &mut DAffine2)| {
|
||||
let factor = match randomize {
|
||||
true => {
|
||||
if bias.abs() < 1e-6 {
|
||||
rng.random::<f64>()
|
||||
} else {
|
||||
(1. - rng.random::<f64>() * (1. - 2_f64.powf(bias))).log2() / bias
|
||||
}
|
||||
}
|
||||
false => match repeat_every {
|
||||
0 => index as f64 / (count - 1).max(1) as f64,
|
||||
1 => 0.,
|
||||
_ => index as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
|
||||
},
|
||||
};
|
||||
let scale_value = min_scale + (max_scale - min_scale) * factor;
|
||||
let scale_transform = DAffine2::from_scale(DVec2::splat(scale_value));
|
||||
transform.apply_transform(&scale_transform);
|
||||
};
|
||||
|
||||
if reverse {
|
||||
content.vector_transforms_iter_mut().rev().enumerate().for_each(apply);
|
||||
} else {
|
||||
content.vector_transforms_iter_mut().enumerate().for_each(apply);
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
/// Applies a fill style to the vector content, giving an appearance to the area within the interior of the geometry.
|
||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
||||
async fn fill<F: Into<Fill> + 'n + Send, V: VectorListIterMut + 'n + Send>(
|
||||
|
||||
Reference in New Issue
Block a user