mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
New node: Morph (#1576)
* Add morph node * Range slider time parameter, better lerping * Lerp more fill and stroke parameters --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
768bbe820d
commit
484acbcde3
10 changed files with 240 additions and 29 deletions
|
@ -2580,7 +2580,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
DocumentInputType::value("Start", TaggedValue::DVec2(DVec2::new(0., 0.5)), false),
|
||||
DocumentInputType::value("End", TaggedValue::DVec2(DVec2::new(1., 0.5)), false),
|
||||
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
|
||||
DocumentInputType::value("Positions", TaggedValue::GradientPositions(vec![(0., Some(Color::BLACK)), (1., Some(Color::WHITE))]), false),
|
||||
DocumentInputType::value("Positions", TaggedValue::GradientPositions(vec![(0., Color::BLACK), (1., Color::WHITE)]), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::fill_properties,
|
||||
|
@ -2715,6 +2715,21 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
properties: node_properties::node_no_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
name: "Morph",
|
||||
category: "Vector",
|
||||
implementation: NodeImplementation::proto("graphene_core::vector::MorphNode<_, _, _, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::value("Source", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
||||
DocumentInputType::value("Target", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
||||
DocumentInputType::value("Start Index", TaggedValue::U32(0), false),
|
||||
DocumentInputType::value("Time", TaggedValue::F64(0.5), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
manual_composition: Some(concrete!(Footprint)),
|
||||
properties: node_properties::morph_properties,
|
||||
..Default::default()
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
DocumentNodeDefinition {
|
||||
name: "Image Segmentation",
|
||||
|
|
|
@ -623,18 +623,18 @@ fn gradient_type_widget(document_node: &DocumentNode, node_id: NodeId, index: us
|
|||
LayoutGroup::Row { widgets }
|
||||
}
|
||||
|
||||
fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Option<Color>)>, index: usize, node_id: NodeId, input_index: usize) {
|
||||
fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Color)>, index: usize, node_id: NodeId, input_index: usize) {
|
||||
let label = TextLabel::new(format!("Gradient: {:.0}%", positions[index].0 * 100.)).tooltip("Adjustable by dragging the gradient stops in the viewport with the Gradient tool active");
|
||||
row.push(label.widget_holder());
|
||||
let on_update = {
|
||||
let positions = positions.clone();
|
||||
move |color_button: &ColorButton| {
|
||||
let mut new_positions = positions.clone();
|
||||
new_positions[index].1 = color_button.value;
|
||||
new_positions[index].1 = color_button.value.unwrap();
|
||||
TaggedValue::GradientPositions(new_positions)
|
||||
}
|
||||
};
|
||||
let color = ColorButton::new(positions[index].1).on_update(update_value(on_update, node_id, input_index));
|
||||
let color = ColorButton::new(Some(positions[index].1)).on_update(update_value(on_update, node_id, input_index)).allow_none(false);
|
||||
add_blank_assist(row);
|
||||
row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
row.push(color.widget_holder());
|
||||
|
@ -668,10 +668,9 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Option<Color>
|
|||
let mut new_positions = positions.clone();
|
||||
|
||||
// Blend linearly between the two colors.
|
||||
let get_color = |index: usize| match (new_positions[index].1, new_positions.get(index + 1).and_then(|x| x.1)) {
|
||||
(Some(a), Some(b)) => Color::from_rgbaf32((a.r() + b.r()) / 2., (a.g() + b.g()) / 2., (a.b() + b.b()) / 2., ((a.a() + b.a()) / 2.).clamp(0., 1.)),
|
||||
(Some(v), _) | (_, Some(v)) => Some(v),
|
||||
_ => Some(Color::WHITE),
|
||||
let get_color = |index: usize| match (new_positions[index].1, new_positions.get(index + 1).map(|x| x.1)) {
|
||||
(a, Some(b)) => Color::from_rgbaf32_unchecked((a.r() + b.r()) / 2., (a.g() + b.g()) / 2., (a.b() + b.b()) / 2., ((a.a() + b.a()) / 2.).clamp(0., 1.)),
|
||||
(_, None) => Color::WHITE,
|
||||
};
|
||||
let get_pos = |index: usize| (new_positions[index].0 + new_positions.get(index + 1).map(|v| v.0).unwrap_or(1.)) / 2.;
|
||||
|
||||
|
@ -2056,6 +2055,16 @@ pub fn sample_points_properties(document_node: &DocumentNode, node_id: NodeId, _
|
|||
]
|
||||
}
|
||||
|
||||
pub fn morph_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let start_index = number_widget(document_node, node_id, 2, "Start Index", NumberInput::default().min(0.), true);
|
||||
let time = number_widget(document_node, node_id, 3, "Time", NumberInput::default().min(0.).max(1.).mode_range(), true);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: start_index }.with_tooltip("The index of point on the target that morphs to the first point of the source"),
|
||||
LayoutGroup::Row { widgets: time }.with_tooltip("Linear time of transition - 0. is source, 1. is target"),
|
||||
]
|
||||
}
|
||||
|
||||
/// Fill Node Widgets LayoutGroup
|
||||
pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let fill_type_index = 1;
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
|||
use crate::messages::tool::common_functionality::graph_modification_utils::get_gradient;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
|
||||
use graphene_core::raster::color::Color;
|
||||
use graphene_core::vector::style::{Fill, Gradient, GradientType};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -335,7 +334,7 @@ impl Fsm for GradientToolFsmState {
|
|||
if selected_gradient.gradient.positions.len() == 1 {
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: selected_gradient.layer,
|
||||
fill: Fill::Solid(selected_gradient.gradient.positions[0].1.unwrap_or(Color::BLACK)),
|
||||
fill: Fill::Solid(selected_gradient.gradient.positions[0].1),
|
||||
});
|
||||
return self;
|
||||
}
|
||||
|
@ -344,7 +343,7 @@ impl Fsm for GradientToolFsmState {
|
|||
let min_position = selected_gradient.gradient.positions.iter().map(|(pos, _)| *pos).reduce(f64::min).expect("No min");
|
||||
let max_position = selected_gradient.gradient.positions.iter().map(|(pos, _)| *pos).reduce(f64::max).expect("No max");
|
||||
|
||||
// Recompute the start and end posiiton of the gradient (in viewport transform)
|
||||
// Recompute the start and end position of the gradient (in viewport transform)
|
||||
let transform = selected_gradient.transform;
|
||||
let (start, end) = (transform.transform_point2(selected_gradient.gradient.start), transform.transform_point2(selected_gradient.gradient.end));
|
||||
let (new_start, new_end) = (start.lerp(end, min_position), start.lerp(end, max_position));
|
||||
|
|
|
@ -112,7 +112,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
&self.manipulator_groups
|
||||
}
|
||||
|
||||
/// Returns a mutable vec of the [ManipulatorGroup]s in the `Subpath`.
|
||||
/// Returns a mutable reference to the [ManipulatorGroup]s in the `Subpath`.
|
||||
pub fn manipulator_groups_mut(&mut self) -> &mut Vec<ManipulatorGroup<ManipulatorGroupId>> {
|
||||
&mut self.manipulator_groups
|
||||
}
|
||||
|
|
|
@ -111,6 +111,12 @@ impl<ManipulatorGroupId: crate::Identifier> ManipulatorGroup<ManipulatorGroupId>
|
|||
pub fn is_finite(&self) -> bool {
|
||||
self.anchor.is_finite() && self.in_handle.map_or(true, |handle| handle.is_finite()) && self.out_handle.map_or(true, |handle| handle.is_finite())
|
||||
}
|
||||
|
||||
/// Reverse directions of handles
|
||||
pub fn flip(mut self) -> Self {
|
||||
std::mem::swap(&mut self.in_handle, &mut self.out_handle);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -853,7 +853,7 @@ impl Color {
|
|||
///
|
||||
/// T must be between 0 and 1.
|
||||
#[inline(always)]
|
||||
pub fn lerp(self, other: Color, t: f32) -> Self {
|
||||
pub fn lerp(&self, other: &Color, t: f32) -> Self {
|
||||
assert!((0. ..=1.).contains(&t));
|
||||
Color::from_rgbaf32_unchecked(
|
||||
self.red + ((other.red - self.red) * t),
|
||||
|
|
|
@ -36,9 +36,10 @@ pub struct Gradient {
|
|||
pub start: DVec2,
|
||||
pub end: DVec2,
|
||||
pub transform: DAffine2,
|
||||
pub positions: Vec<(f64, Option<Color>)>,
|
||||
pub positions: Vec<(f64, Color)>,
|
||||
pub gradient_type: GradientType,
|
||||
}
|
||||
|
||||
impl core::hash::Hash for Gradient {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.positions.len().hash(state);
|
||||
|
@ -52,18 +53,44 @@ impl core::hash::Hash for Gradient {
|
|||
self.gradient_type.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Gradient {
|
||||
/// Constructs a new gradient with the colors at 0 and 1 specified.
|
||||
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, gradient_type: GradientType) -> Self {
|
||||
Gradient {
|
||||
start,
|
||||
end,
|
||||
positions: vec![(0., Some(start_color)), (1., Some(end_color))],
|
||||
positions: vec![(0., start_color), (1., end_color)],
|
||||
transform,
|
||||
gradient_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lerp(&self, other: &Self, time: f64) -> Self {
|
||||
let start = self.start + (other.start - self.start) * time;
|
||||
let end = self.end + (other.end - self.end) * time;
|
||||
let transform = self.transform;
|
||||
let positions = self
|
||||
.positions
|
||||
.iter()
|
||||
.zip(other.positions.iter())
|
||||
.map(|((a_pos, a_color), (b_pos, b_color))| {
|
||||
let position = a_pos + (b_pos - a_pos) * time;
|
||||
let color = a_color.lerp(b_color, time as f32);
|
||||
(position, color)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let gradient_type = if time < 0.5 { self.gradient_type } else { other.gradient_type };
|
||||
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
transform,
|
||||
positions,
|
||||
gradient_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the gradient def through mutating the first argument, returning the gradient ID.
|
||||
fn render_defs(&self, svg_defs: &mut String, multiplied_transform: DAffine2, bounds: [DVec2; 2], transformed_bounds: [DVec2; 2]) -> u64 {
|
||||
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||
|
@ -71,7 +98,7 @@ impl Gradient {
|
|||
let updated_transform = multiplied_transform * bound_transform;
|
||||
|
||||
let mut positions = String::new();
|
||||
for (position, color) in self.positions.iter().filter_map(|(pos, color)| color.map(|color| (pos, color))) {
|
||||
for (position, color) in self.positions.iter() {
|
||||
let _ = write!(positions, r##"<stop offset="{}" stop-color="#{}" />"##, position, color.with_alpha(color.a()).rgba_hex());
|
||||
}
|
||||
|
||||
|
@ -124,15 +151,14 @@ impl Gradient {
|
|||
}
|
||||
|
||||
// Compute the color of the inserted stop
|
||||
let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).and_then(|x| x.1)) {
|
||||
let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).map(|(_, c)| *c)) {
|
||||
// Lerp between the nearest colors if applicable
|
||||
(Some(a), Some(b)) => a.lerp(
|
||||
b,
|
||||
(a, Some(b)) => a.lerp(
|
||||
&b,
|
||||
((time - self.positions[index].0) / self.positions.get(index + 1).map(|end| end.0 - self.positions[index].0).unwrap_or_default()) as f32,
|
||||
),
|
||||
// Use the start or the end color if applicable
|
||||
(Some(v), _) | (_, Some(v)) => v,
|
||||
_ => Color::WHITE,
|
||||
(v, _) => v,
|
||||
};
|
||||
|
||||
// Compute the correct index to keep the positions in order
|
||||
|
@ -144,7 +170,7 @@ impl Gradient {
|
|||
let new_color = get_color(index - 1, new_position);
|
||||
|
||||
// Insert the new stop
|
||||
self.positions.insert(index, (new_position, Some(new_color)));
|
||||
self.positions.insert(index, (new_position, new_color));
|
||||
|
||||
Some(index)
|
||||
}
|
||||
|
@ -174,7 +200,31 @@ impl Fill {
|
|||
Self::None => Color::BLACK,
|
||||
Self::Solid(color) => *color,
|
||||
// TODO: Should correctly sample the gradient
|
||||
Self::Gradient(Gradient { positions, .. }) => positions[0].1.unwrap_or(Color::BLACK),
|
||||
Self::Gradient(Gradient { positions, .. }) => positions[0].1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lerp(&self, other: &Self, time: f64) -> Self {
|
||||
let transparent = Self::solid(Color::TRANSPARENT);
|
||||
let a = if *self == Self::None { &transparent } else { self };
|
||||
let b = if *other == Self::None { &transparent } else { other };
|
||||
|
||||
match (a, b) {
|
||||
(Self::Solid(a), Self::Solid(b)) => Self::Solid(a.lerp(b, time as f32)),
|
||||
(Self::Solid(a), Self::Gradient(b)) => {
|
||||
let mut solid_to_gradient = b.clone();
|
||||
solid_to_gradient.positions.iter_mut().for_each(|(_, color)| *color = *a);
|
||||
let a = &solid_to_gradient;
|
||||
Self::Gradient(a.lerp(b, time))
|
||||
}
|
||||
(Self::Gradient(a), Self::Solid(b)) => {
|
||||
let mut gradient_to_solid = a.clone();
|
||||
gradient_to_solid.positions.iter_mut().for_each(|(_, color)| *color = *b);
|
||||
let b = &gradient_to_solid;
|
||||
Self::Gradient(a.lerp(b, time))
|
||||
}
|
||||
(Self::Gradient(a), Self::Gradient(b)) => Self::Gradient(a.lerp(b, time)),
|
||||
_ => Self::None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,6 +340,18 @@ impl Stroke {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lerp(&self, other: &Self, time: f64) -> Self {
|
||||
Self {
|
||||
color: self.color.map(|color| color.lerp(&other.color.unwrap_or(color), time as f32)),
|
||||
weight: self.weight + (other.weight - self.weight) * time,
|
||||
dash_lengths: self.dash_lengths.iter().zip(other.dash_lengths.iter()).map(|(a, b)| a + (b - a) * time as f32).collect(),
|
||||
dash_offset: self.dash_offset + (other.dash_offset - self.dash_offset) * time,
|
||||
line_cap: if time < 0.5 { self.line_cap } else { other.line_cap },
|
||||
line_join: if time < 0.5 { self.line_join } else { other.line_join },
|
||||
line_join_miter_limit: self.line_join_miter_limit + (other.line_join_miter_limit - self.line_join_miter_limit) * time,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current stroke color.
|
||||
pub fn color(&self) -> Option<Color> {
|
||||
self.color
|
||||
|
@ -422,6 +484,30 @@ impl PathStyle {
|
|||
Self { stroke, fill }
|
||||
}
|
||||
|
||||
pub fn lerp(&self, other: &Self, time: f64) -> Self {
|
||||
Self {
|
||||
fill: self.fill.lerp(&other.fill, time),
|
||||
stroke: match (self.stroke.as_ref(), other.stroke.as_ref()) {
|
||||
(Some(a), Some(b)) => Some(a.lerp(b, time)),
|
||||
(Some(a), None) => {
|
||||
if time < 0.5 {
|
||||
Some(a.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(None, Some(b)) => {
|
||||
if time < 0.5 {
|
||||
Some(b.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(None, None) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current path's [Fill].
|
||||
///
|
||||
/// # Example
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::transform::{Footprint, Transform, TransformMut};
|
|||
use crate::{Color, GraphicGroup, Node};
|
||||
use core::future::Future;
|
||||
|
||||
use bezier_rs::{Subpath, TValue};
|
||||
use bezier_rs::{Subpath, SubpathTValue, TValue};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -28,7 +28,7 @@ fn set_vector_data_fill(
|
|||
start: DVec2,
|
||||
end: DVec2,
|
||||
transform: DAffine2,
|
||||
positions: Vec<(f64, Option<Color>)>,
|
||||
positions: Vec<(f64, Color)>,
|
||||
) -> VectorData {
|
||||
vector_data.style.set_fill(match fill_type {
|
||||
FillType::Solid => solid_color.map_or(Fill::None, Fill::Solid),
|
||||
|
@ -309,3 +309,98 @@ fn splines_from_points(mut vector_data: VectorData) -> VectorData {
|
|||
|
||||
vector_data
|
||||
}
|
||||
|
||||
pub struct MorphNode<Source, Target, StartIndex, Time> {
|
||||
source: Source,
|
||||
target: Target,
|
||||
start_index: StartIndex,
|
||||
time: Time,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(MorphNode)]
|
||||
async fn morph<SourceFuture: Future<Output = VectorData>, TargetFuture: Future<Output = VectorData>>(
|
||||
footprint: Footprint,
|
||||
source: impl Node<Footprint, Output = SourceFuture>,
|
||||
target: impl Node<Footprint, Output = TargetFuture>,
|
||||
start_index: u32,
|
||||
time: f64,
|
||||
) -> VectorData {
|
||||
let mut source = self.source.eval(footprint).await;
|
||||
let mut target = self.target.eval(footprint).await;
|
||||
|
||||
// Lerp styles
|
||||
let style = source.style.lerp(&target.style, time);
|
||||
|
||||
for (source_path, target_path) in source.subpaths.iter_mut().zip(target.subpaths.iter_mut()) {
|
||||
// Deal with mistmatched transforms
|
||||
source_path.apply_transform(source.transform);
|
||||
target_path.apply_transform(target.transform);
|
||||
|
||||
// Deal with mismatched start index
|
||||
for _ in 0..start_index {
|
||||
let first = target_path.remove_manipulator_group(0);
|
||||
target_path.push_manipulator_group(first);
|
||||
}
|
||||
|
||||
// Deal with mismatched closed state
|
||||
if source_path.closed() && !target_path.closed() {
|
||||
source_path.set_closed(false);
|
||||
source_path.push_manipulator_group(source_path.manipulator_groups()[0].flip());
|
||||
}
|
||||
if !source_path.closed() && target_path.closed() {
|
||||
target_path.set_closed(false);
|
||||
target_path.push_manipulator_group(target_path.manipulator_groups()[0].flip());
|
||||
}
|
||||
|
||||
// Mismatched subpath items
|
||||
'outer: loop {
|
||||
for segment_index in (0..(source_path.len() - 1)).rev() {
|
||||
if target_path.len() <= source_path.len() {
|
||||
break 'outer;
|
||||
}
|
||||
source_path.insert(SubpathTValue::Parametric { segment_index, t: 0.5 })
|
||||
}
|
||||
}
|
||||
'outer: loop {
|
||||
for segment_index in (0..(target_path.len() - 1)).rev() {
|
||||
if source_path.len() <= target_path.len() {
|
||||
break 'outer;
|
||||
}
|
||||
target_path.insert(SubpathTValue::Parametric { segment_index, t: 0.5 })
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mismatched subpath count
|
||||
for source_path in source.subpaths.iter_mut().skip(target.subpaths.len()) {
|
||||
source_path.apply_transform(source.transform);
|
||||
target.subpaths.push(Subpath::from_anchors(
|
||||
std::iter::repeat(source_path.manipulator_groups().first().map(|group| group.anchor).unwrap_or_default()).take(source_path.len()),
|
||||
source_path.closed,
|
||||
))
|
||||
}
|
||||
for target_path in target.subpaths.iter_mut().skip(source.subpaths.len()) {
|
||||
target_path.apply_transform(target.transform);
|
||||
source.subpaths.push(Subpath::from_anchors(
|
||||
std::iter::repeat(target_path.manipulator_groups().first().map(|group| group.anchor).unwrap_or_default()).take(target_path.len()),
|
||||
target_path.closed,
|
||||
))
|
||||
}
|
||||
|
||||
// Lerp points
|
||||
for (subpath, target) in source.subpaths.iter_mut().zip(target.subpaths.iter()) {
|
||||
for (manipulator, target) in subpath.manipulator_groups_mut().iter_mut().zip(target.manipulator_groups()) {
|
||||
manipulator.in_handle = Some(manipulator.in_handle.unwrap_or(manipulator.anchor).lerp(target.in_handle.unwrap_or(target.anchor), time));
|
||||
manipulator.out_handle = Some(manipulator.out_handle.unwrap_or(manipulator.anchor).lerp(target.out_handle.unwrap_or(target.anchor), time));
|
||||
manipulator.anchor = manipulator.anchor.lerp(target.anchor, time);
|
||||
}
|
||||
}
|
||||
|
||||
// Create result
|
||||
let subpaths = std::mem::take(&mut source.subpaths);
|
||||
let mut current = if time < 0.5 { source } else { target };
|
||||
current.style = style;
|
||||
current.subpaths = subpaths;
|
||||
current.transform = DAffine2::IDENTITY;
|
||||
|
||||
current
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ pub enum TaggedValue {
|
|||
LineJoin(graphene_core::vector::style::LineJoin),
|
||||
FillType(graphene_core::vector::style::FillType),
|
||||
GradientType(graphene_core::vector::style::GradientType),
|
||||
GradientPositions(Vec<(f64, Option<graphene_core::Color>)>),
|
||||
GradientPositions(Vec<(f64, graphene_core::Color)>),
|
||||
Quantization(graphene_core::quantization::QuantizationChannels),
|
||||
OptionalColor(Option<graphene_core::raster::color::Color>),
|
||||
ManipulatorGroupIds(Vec<graphene_core::uuid::ManipulatorGroupId>),
|
||||
|
@ -273,7 +273,7 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::LineJoin(_) => concrete!(graphene_core::vector::style::LineJoin),
|
||||
TaggedValue::FillType(_) => concrete!(graphene_core::vector::style::FillType),
|
||||
TaggedValue::GradientType(_) => concrete!(graphene_core::vector::style::GradientType),
|
||||
TaggedValue::GradientPositions(_) => concrete!(Vec<(f64, Option<graphene_core::Color>)>),
|
||||
TaggedValue::GradientPositions(_) => concrete!(Vec<(f64, graphene_core::Color)>),
|
||||
TaggedValue::Quantization(_) => concrete!(graphene_core::quantization::QuantizationChannels),
|
||||
TaggedValue::OptionalColor(_) => concrete!(Option<graphene_core::Color>),
|
||||
TaggedValue::ManipulatorGroupIds(_) => concrete!(Vec<graphene_core::uuid::ManipulatorGroupId>),
|
||||
|
@ -337,7 +337,7 @@ impl<'a> TaggedValue {
|
|||
x if x == TypeId::of::<graphene_core::vector::style::LineJoin>() => Ok(TaggedValue::LineJoin(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::vector::style::FillType>() => Ok(TaggedValue::FillType(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::vector::style::GradientType>() => Ok(TaggedValue::GradientType(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Vec<(f64, Option<graphene_core::Color>)>>() => Ok(TaggedValue::GradientPositions(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Vec<(f64, graphene_core::Color)>>() => Ok(TaggedValue::GradientPositions(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::quantization::QuantizationChannels>() => Ok(TaggedValue::Quantization(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Option<graphene_core::Color>>() => Ok(TaggedValue::OptionalColor(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Vec<graphene_core::uuid::ManipulatorGroupId>>() => Ok(TaggedValue::ManipulatorGroupIds(*downcast(input).unwrap())),
|
||||
|
|
|
@ -685,7 +685,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
|
||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData, params: [DAffine2]),
|
||||
register_node!(graphene_core::transform::SetTransformNode<_>, input: ImageFrame<Color>, params: [DAffine2]),
|
||||
register_node!(graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>, input: VectorData, params: [graphene_core::vector::style::FillType, Option<graphene_core::Color>, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, Option<graphene_core::Color>)>]),
|
||||
register_node!(graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>, input: VectorData, params: [graphene_core::vector::style::FillType, Option<graphene_core::Color>, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, graphene_core::Color)>]),
|
||||
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option<graphene_core::Color>, f32, Vec<f32>, f32, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f32]),
|
||||
register_node!(graphene_core::vector::RepeatNode<_, _>, input: VectorData, params: [DVec2, u32]),
|
||||
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
||||
|
@ -737,6 +737,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::vector::SamplePoints<_, _, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, () => f32, () => f32, () => f32, () => bool, Footprint => Vec<Vec<f64>>]),
|
||||
register_node!(graphene_core::vector::LengthsOfSegmentsOfSubpaths, input: VectorData, params: []),
|
||||
register_node!(graphene_core::vector::SplinesFromPointsNode, input: VectorData, params: []),
|
||||
async_node!(graphene_core::vector::MorphNode<_, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, Footprint => VectorData, () => u32, () => f64]),
|
||||
register_node!(graphene_core::vector::generator_nodes::CircleGenerator<_>, input: (), params: [f32]),
|
||||
register_node!(graphene_core::vector::generator_nodes::EllipseGenerator<_, _>, input: (), params: [f32, f32]),
|
||||
register_node!(graphene_core::vector::generator_nodes::RectangleGenerator<_, _>, input: (), params: [f32, f32]),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue