Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion editor/src/messages/tool/tool_messages/polygon_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
8 changes: 6 additions & 2 deletions libraries/bezier-rs/src/subpath/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();

Expand Down
91 changes: 83 additions & 8 deletions node-graph/gcore/src/vector/generator_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(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 EllipseGenerator<RadiusX, RadiusY> {
radius_x: RadiusX,
radius_y: RadiusY,
}

#[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)])
}

#[node_macro::node_fn(UnitCircleGenerator)]
fn unit_circle(_input: ()) -> VectorData {
super::VectorData::from_subpath(Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE))
#[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 UnitSquareGenerator;
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(UnitSquareGenerator)]
fn unit_square(_input: ()) -> VectorData {
super::VectorData::from_subpaths(vec![Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)])
#[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
Expand Down
5 changes: 5 additions & 0 deletions node-graph/graph-craft/src/document/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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())),
Expand Down
8 changes: 7 additions & 1 deletion node-graph/interpreted-executor/src/node_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
Expand Down