mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-31 02:07:21 +00:00
New nodes: shape/curve primitives (#1389)
* Add new Primitive Shape/Curve Nodes * Elipse Node and Debug * N-input Spline node * Debuging * Debug * fmt * remov debug * Changes from code review * Debug: Empty Spline Input * Changes from code review * Fix spelling of ellipse * Rename polygon to regular polygon --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
9d27bf49cf
commit
226b96260c
7 changed files with 263 additions and 15 deletions
|
@ -1915,11 +1915,90 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||
},
|
||||
(*IMAGINATE_NODE).clone(),
|
||||
DocumentNodeType {
|
||||
name: "Unit Circle Generator",
|
||||
name: "Circle",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::UnitCircleGenerator"),
|
||||
inputs: vec![DocumentInputType::none()],
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::CircleGenerator<_>"),
|
||||
inputs: vec![DocumentInputType::none(), DocumentInputType::value("Radius", TaggedValue::F32(50.), false)],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::circle_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Ellipse",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::EllipseGenerator<_, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Radius X", TaggedValue::F32(50.), false),
|
||||
DocumentInputType::value("Radius Y", TaggedValue::F32(25.), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::ellipse_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Rectangle",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::RectangleGenerator<_, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Size X", TaggedValue::F32(100.), false),
|
||||
DocumentInputType::value("Size Y", TaggedValue::F32(100.), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::rectangle_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Regular Polygon",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::RegularPolygonGenerator<_, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Sides", TaggedValue::U32(6), false),
|
||||
DocumentInputType::value("Radius", TaggedValue::F32(50.), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::regular_polygon_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Star",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::StarGenerator<_, _, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Sides", TaggedValue::U32(5), false),
|
||||
DocumentInputType::value("Radius", TaggedValue::F32(50.), false),
|
||||
DocumentInputType::value("Inner Radius", TaggedValue::F32(25.), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::star_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Line",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::LineGenerator<_, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Start", TaggedValue::DVec2(DVec2::new(0., -50.)), false),
|
||||
DocumentInputType::value("End", TaggedValue::DVec2(DVec2::new(0., 50.)), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::line_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Spline",
|
||||
category: "Vector",
|
||||
identifier: NodeImplementation::proto("graphene_core::vector::generator_nodes::SplineGenerator<_>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::none(),
|
||||
DocumentInputType::value("Points", TaggedValue::VecDVec2(vec![DVec2::new(0., -50.), DVec2::new(25., 0.), DVec2::new(0., 50.)]), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
|
||||
properties: node_properties::spline_properties,
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNodeType {
|
||||
|
|
|
@ -217,6 +217,36 @@ fn vec_f32_input(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, text_props: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Color, blank_assist);
|
||||
|
||||
let from_string = |string: &str| {
|
||||
string
|
||||
.split(|c: char| !c.is_alphanumeric() && !matches!(c, '.' | '+' | '-'))
|
||||
.filter(|x| !x.is_empty())
|
||||
.map(|x| x.parse::<f64>().ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.map(|numbers| numbers.chunks_exact(2).map(|vals| DVec2::new(vals[0], vals[1])).collect())
|
||||
.map(TaggedValue::VecDVec2)
|
||||
};
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::VecDVec2(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
text_props
|
||||
.value(x.iter().map(|v| format!("({}, {})", v.x, v.y)).collect::<Vec<_>>().join(", "))
|
||||
.on_update(optionally_update_value(move |x: &TextInput| from_string(&x.value), node_id, index))
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> (Vec<WidgetHolder>, Option<Vec<WidgetHolder>>) {
|
||||
let mut first_widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
let mut second_widgets = None;
|
||||
|
@ -1139,6 +1169,55 @@ pub fn modulo_properties(document_node: &DocumentNode, node_id: NodeId, _context
|
|||
vec![operand("Modulo", 1)]
|
||||
}
|
||||
|
||||
pub fn circle_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: number_widget(document_node, node_id, 1, "Radius", NumberInput::default(), true),
|
||||
}]
|
||||
}
|
||||
|
||||
pub fn ellipse_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let operand = |name: &str, index| {
|
||||
let widgets = number_widget(document_node, node_id, index, name, NumberInput::default(), true);
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
vec![operand("Radius X", 1), operand("Radius Y", 2)]
|
||||
}
|
||||
|
||||
pub fn rectangle_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let operand = |name: &str, index| {
|
||||
let widgets = number_widget(document_node, node_id, index, name, NumberInput::default(), true);
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
vec![operand("Size X", 1), operand("Size Y", 2)]
|
||||
}
|
||||
|
||||
pub fn regular_polygon_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let points = number_widget(document_node, node_id, 1, "Points", NumberInput::default().min(3.), true);
|
||||
let radius = number_widget(document_node, node_id, 2, "Radius", NumberInput::default(), true);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: points }, LayoutGroup::Row { widgets: radius }]
|
||||
}
|
||||
|
||||
pub fn star_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let points = number_widget(document_node, node_id, 1, "Points", NumberInput::default().min(2.), true);
|
||||
let radius = number_widget(document_node, node_id, 2, "Radius", NumberInput::default(), true);
|
||||
let inner_radius = number_widget(document_node, node_id, 3, "Inner Radius", NumberInput::default(), true);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: points }, LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: inner_radius }]
|
||||
}
|
||||
|
||||
pub fn line_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let operand = |name: &str, index| vec2_widget(document_node, node_id, index, name, "X", "Y", "px", add_blank_assist);
|
||||
vec![operand("Start", 1), operand("End", 2)]
|
||||
}
|
||||
pub fn spline_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: vec_dvec2_input(document_node, node_id, 1, "Points", TextInput::default().centered(true), true),
|
||||
}]
|
||||
}
|
||||
|
||||
pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let translation_assist = |widgets: &mut Vec<WidgetHolder>| {
|
||||
let pivot_index = 5;
|
||||
|
|
|
@ -265,7 +265,7 @@ impl Fsm for PolygonToolFsmState {
|
|||
|
||||
let subpath = match tool_options.primitive_shape_type {
|
||||
PrimitiveShapeType::Polygon => bezier_rs::Subpath::new_regular_polygon(DVec2::ZERO, tool_options.vertices as u64, 1.),
|
||||
PrimitiveShapeType::Star => bezier_rs::Subpath::new_regular_star_polygon(DVec2::ZERO, tool_options.vertices as u64, 1., 0.5),
|
||||
PrimitiveShapeType::Star => bezier_rs::Subpath::new_star_polygon(DVec2::ZERO, tool_options.vertices as u64, 1., 0.5),
|
||||
};
|
||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||
|
||||
|
|
|
@ -235,8 +235,8 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
Self::from_anchors(anchor_positions, true)
|
||||
}
|
||||
|
||||
/// Constructs a regular star polygon (n-star). See [new_regular_polygon], but with interspersed vertices at an `inner_radius`.
|
||||
pub fn new_regular_star_polygon(center: DVec2, sides: u64, radius: f64, inner_radius: f64) -> Self {
|
||||
/// Constructs a star polygon (n-star). See [new_regular_polygon], but with interspersed vertices at an `inner_radius`.
|
||||
pub fn new_star_polygon(center: DVec2, sides: u64, radius: f64, inner_radius: f64) -> Self {
|
||||
let anchor_positions = (0..sides * 2).map(|i| {
|
||||
let angle = (i as f64) * 0.5 * std::f64::consts::TAU / (sides as f64);
|
||||
let center = center + DVec2::ONE * radius;
|
||||
|
@ -254,6 +254,10 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
/// Construct a cubic spline from a list of points.
|
||||
/// Based on <https://mathworld.wolfram.com/CubicSpline.html>.
|
||||
pub fn new_cubic_spline(points: Vec<DVec2>) -> Self {
|
||||
if points.is_empty() {
|
||||
return Self::new(Vec::new(), false);
|
||||
}
|
||||
|
||||
// Number of points = number of points to find handles for
|
||||
let len_points = points.len();
|
||||
|
||||
|
|
|
@ -6,19 +6,94 @@ use bezier_rs::Subpath;
|
|||
|
||||
use glam::DVec2;
|
||||
|
||||
pub struct UnitCircleGenerator;
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CircleGenerator<Radius> {
|
||||
radius: Radius,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(UnitCircleGenerator)]
|
||||
fn unit_circle(_input: ()) -> VectorData {
|
||||
super::VectorData::from_subpath(Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE))
|
||||
#[node_macro::node_fn(CircleGenerator)]
|
||||
fn circle_generator(_input: (), radius: f32) -> VectorData {
|
||||
let radius: f64 = radius.into();
|
||||
super::VectorData::from_subpath(Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UnitSquareGenerator;
|
||||
pub struct EllipseGenerator<RadiusX, RadiusY> {
|
||||
radius_x: RadiusX,
|
||||
radius_y: RadiusY,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(UnitSquareGenerator)]
|
||||
fn unit_square(_input: ()) -> VectorData {
|
||||
super::VectorData::from_subpaths(vec![Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)])
|
||||
#[node_macro::node_fn(EllipseGenerator)]
|
||||
fn ellipse_generator(_input: (), radius_x: f32, radius_y: f32) -> VectorData {
|
||||
let radius = DVec2::new(radius_x as f64, radius_y as f64);
|
||||
let corner1 = -radius;
|
||||
let corner2 = radius;
|
||||
super::VectorData::from_subpath(Subpath::new_ellipse(corner1, corner2))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RectangleGenerator<SizeX, SizeY> {
|
||||
size_x: SizeX,
|
||||
size_y: SizeY,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(RectangleGenerator)]
|
||||
fn square_generator(_input: (), size_x: f32, size_y: f32) -> VectorData {
|
||||
let size = DVec2::new(size_x as f64, size_y as f64);
|
||||
let corner1 = -size / 2.;
|
||||
let corner2 = size / 2.;
|
||||
|
||||
super::VectorData::from_subpaths(vec![Subpath::new_rect(corner1, corner2)])
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegularPolygonGenerator<Points, Radius> {
|
||||
points: Points,
|
||||
radius: Radius,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(RegularPolygonGenerator)]
|
||||
fn regular_polygon_generator(_input: (), points: u32, radius: f32) -> VectorData {
|
||||
let points = points.into();
|
||||
let radius: f64 = (radius * 2.).into();
|
||||
super::VectorData::from_subpath(Subpath::new_regular_polygon(DVec2::splat(-radius), points, radius))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct StarGenerator<Points, Radius, InnerRadius> {
|
||||
points: Points,
|
||||
radius: Radius,
|
||||
inner_radius: InnerRadius,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(StarGenerator)]
|
||||
fn star_generator(_input: (), points: u32, radius: f32, inner_radius: f32) -> VectorData {
|
||||
let points = points.into();
|
||||
let diameter: f64 = (radius * 2.).into();
|
||||
let inner_diameter = (inner_radius * 2.).into();
|
||||
|
||||
super::VectorData::from_subpath(Subpath::new_star_polygon(DVec2::splat(-diameter), points, diameter, inner_diameter))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct LineGenerator<Pos1, Pos2> {
|
||||
pos_1: Pos1,
|
||||
pos_2: Pos2,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(LineGenerator)]
|
||||
fn line_generator(_input: (), pos_1: DVec2, pos_2: DVec2) -> VectorData {
|
||||
super::VectorData::from_subpaths(vec![Subpath::new_line(pos_1, pos_2)])
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SplineGenerator<Positions> {
|
||||
positions: Positions,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(SplineGenerator)]
|
||||
fn spline_generator(_input: (), positions: Vec<DVec2>) -> VectorData {
|
||||
super::VectorData::from_subpaths(vec![Subpath::new_cubic_spline(positions)])
|
||||
}
|
||||
|
||||
// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node
|
||||
|
|
|
@ -42,6 +42,7 @@ pub enum TaggedValue {
|
|||
Fill(graphene_core::vector::style::Fill),
|
||||
Stroke(graphene_core::vector::style::Stroke),
|
||||
VecF32(Vec<f32>),
|
||||
VecDVec2(Vec<DVec2>),
|
||||
RedGreenBlue(graphene_core::raster::RedGreenBlue),
|
||||
NoiseType(graphene_core::raster::NoiseType),
|
||||
RelativeAbsolute(graphene_core::raster::RelativeAbsolute),
|
||||
|
@ -100,6 +101,7 @@ impl Hash for TaggedValue {
|
|||
Self::Fill(fill) => fill.hash(state),
|
||||
Self::Stroke(stroke) => stroke.hash(state),
|
||||
Self::VecF32(vec_f32) => vec_f32.iter().for_each(|val| val.to_bits().hash(state)),
|
||||
Self::VecDVec2(vec_dvec2) => vec_dvec2.iter().for_each(|val| val.to_array().iter().for_each(|x| x.to_bits().hash(state))),
|
||||
Self::RedGreenBlue(red_green_blue) => red_green_blue.hash(state),
|
||||
Self::NoiseType(noise_type) => noise_type.hash(state),
|
||||
Self::RelativeAbsolute(relative_absolute) => relative_absolute.hash(state),
|
||||
|
@ -165,6 +167,7 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::Fill(x) => Box::new(x),
|
||||
TaggedValue::Stroke(x) => Box::new(x),
|
||||
TaggedValue::VecF32(x) => Box::new(x),
|
||||
TaggedValue::VecDVec2(x) => Box::new(x),
|
||||
TaggedValue::RedGreenBlue(x) => Box::new(x),
|
||||
TaggedValue::NoiseType(x) => Box::new(x),
|
||||
TaggedValue::RelativeAbsolute(x) => Box::new(x),
|
||||
|
@ -233,6 +236,7 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::Fill(_) => concrete!(graphene_core::vector::style::Fill),
|
||||
TaggedValue::Stroke(_) => concrete!(graphene_core::vector::style::Stroke),
|
||||
TaggedValue::VecF32(_) => concrete!(Vec<f32>),
|
||||
TaggedValue::VecDVec2(_) => concrete!(Vec<DVec2>),
|
||||
TaggedValue::RedGreenBlue(_) => concrete!(graphene_core::raster::RedGreenBlue),
|
||||
TaggedValue::NoiseType(_) => concrete!(graphene_core::raster::NoiseType),
|
||||
TaggedValue::RelativeAbsolute(_) => concrete!(graphene_core::raster::RelativeAbsolute),
|
||||
|
@ -288,6 +292,7 @@ impl<'a> TaggedValue {
|
|||
x if x == TypeId::of::<graphene_core::vector::style::Fill>() => Ok(TaggedValue::Fill(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::vector::style::Stroke>() => Ok(TaggedValue::Stroke(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Vec<f32>>() => Ok(TaggedValue::VecF32(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<Vec<DVec2>>() => Ok(TaggedValue::VecDVec2(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::raster::RedGreenBlue>() => Ok(TaggedValue::RedGreenBlue(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::raster::NoiseType>() => Ok(TaggedValue::NoiseType(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<graphene_core::raster::RelativeAbsolute>() => Ok(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())),
|
||||
|
|
|
@ -630,7 +630,13 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
register_node!(graphene_core::vector::RepeatNode<_, _>, input: VectorData, params: [DVec2, u32]),
|
||||
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
||||
register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f32, f32, u32]),
|
||||
register_node!(graphene_core::vector::generator_nodes::UnitCircleGenerator, input: (), params: []),
|
||||
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]),
|
||||
register_node!(graphene_core::vector::generator_nodes::RegularPolygonGenerator<_, _>, input: (), params: [u32, f32]),
|
||||
register_node!(graphene_core::vector::generator_nodes::StarGenerator<_, _, _>, input: (), params: [u32, f32, f32]),
|
||||
register_node!(graphene_core::vector::generator_nodes::LineGenerator<_, _>, input: (), params: [DVec2, DVec2]),
|
||||
register_node!(graphene_core::vector::generator_nodes::SplineGenerator<_>, input: (), params: [Vec<DVec2>]),
|
||||
register_node!(
|
||||
graphene_core::vector::generator_nodes::PathGenerator<_>,
|
||||
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue