Skip to content

Commit

Permalink
Copy to Points node: add Start/Stop Offset and Adaptive Spacing param…
Browse files Browse the repository at this point in the history
…eters
  • Loading branch information
Keavon committed Jan 3, 2024
1 parent 4c64df0 commit ed82c5f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2656,10 +2656,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Resample Points",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::ResamplePoints<_>"),
implementation: NodeImplementation::proto("graphene_core::vector::ResamplePoints<_, _, _, _>"),
inputs: vec![
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Spacing", TaggedValue::F64(100.), false),
DocumentInputType::value("Start Offset", TaggedValue::F64(0.), false),
DocumentInputType::value("Stop Offset", TaggedValue::F64(0.), false),
DocumentInputType::value("Adaptive Spacing", TaggedValue::Bool(false), false),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
properties: node_properties::resample_points_properties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2044,8 +2044,16 @@ pub fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId,

pub fn resample_points_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let spacing = number_widget(document_node, node_id, 1, "Spacing", NumberInput::default().min(1.), true);
let start_offset = number_widget(document_node, node_id, 2, "Start Offset", NumberInput::default().min(0.), true);
let stop_offset = number_widget(document_node, node_id, 3, "Stop Offset", NumberInput::default().min(0.), true);
let adaptive_spacing = bool_widget(document_node, node_id, 4, "Adaptive Spacing", true);

vec![LayoutGroup::Row { widgets: spacing }]
vec![
LayoutGroup::Row { widgets: spacing }.with_tooltip("Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled)"),
LayoutGroup::Row { widgets: start_offset }.with_tooltip("Exclude some distance from the start of the path before the first instance"),
LayoutGroup::Row { widgets: stop_offset }.with_tooltip("Exclude some distance from the end of the path after the last instance"),
LayoutGroup::Row { widgets: adaptive_spacing }.with_tooltip("Round 'Spacing' to a nearby value that divides into the path length evenly"),
]
}

/// Fill Node Widgets LayoutGroup
Expand Down
36 changes: 29 additions & 7 deletions node-graph/gcore/src/vector/vector_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,24 +204,46 @@ async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + Tr
}

#[derive(Debug, Clone, Copy)]
pub struct ResamplePoints<Spacing> {
pub struct ResamplePoints<Spacing, StartOffset, StopOffset, AdaptiveSpacing> {
spacing: Spacing,
start_offset: StartOffset,
stop_offset: StopOffset,
adaptive_spacing: AdaptiveSpacing,
}

#[node_macro::node_fn(ResamplePoints)]
fn resample_points(mut vector_data: VectorData, spacing: f64) -> VectorData {
fn resample_points(mut vector_data: VectorData, spacing: f64, start_offset: f64, stop_offset: f64, adaptive_spacing: bool) -> VectorData {
for subpath in &mut vector_data.subpaths {
if subpath.is_empty() || spacing.is_zero() || !spacing.is_finite() {
continue;
}

subpath.apply_transform(vector_data.transform);
let length = subpath.length(None);
let rounded_count = (length / spacing).round();

if rounded_count >= 1. {
let new_anchors = (0..=rounded_count as usize).map(|c| subpath.evaluate(SubpathTValue::GlobalEuclidean(c as f64 / rounded_count)));
*subpath = Subpath::from_anchors(new_anchors, subpath.closed() && rounded_count as usize > 1);
let used_length = length - start_offset - stop_offset;
if used_length <= 0. {
continue;
}
let used_length_without_remainder = used_length - used_length % spacing;

let count = if adaptive_spacing {
(used_length / spacing).round()
} else {
(used_length / spacing + f64::EPSILON).floor()
};

if count >= 1. {
let new_anchors = (0..=count as usize).map(|c| {
let ratio = c as f64 / count;

// With adaptive spacing, we widen or narrow the points (that's the rounding performed above) as necessary to ensure the last point is always at the end of the path
// Without adaptive spacing, we just evenly space the points at the exact specified spacing, usually falling short before the end of the path

let used_length_here = if adaptive_spacing { used_length } else { used_length_without_remainder };
let t = ratio * used_length_here + start_offset;
subpath.evaluate(SubpathTValue::GlobalEuclidean(t / length))
});
*subpath = Subpath::from_anchors(new_anchors, subpath.closed() && count as usize > 1);
}

subpath.apply_transform(vector_data.transform.inverse());
Expand Down
2 changes: 1 addition & 1 deletion node-graph/interpreted-executor/src/node_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
register_node!(graphene_std::raster::MandelbrotNode, input: Footprint, params: []),
async_node!(graphene_core::vector::CopyToPoints<_, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, Footprint => VectorData]),
async_node!(graphene_core::vector::CopyToPoints<_, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => VectorData, Footprint => GraphicGroup]),
register_node!(graphene_core::vector::ResamplePoints<_>, input: VectorData, params: [f64]),
register_node!(graphene_core::vector::ResamplePoints<_, _, _, _>, input: VectorData, params: [f64, f64, f64, bool]),
register_node!(graphene_core::vector::SplinesFromPointsNode, input: VectorData, params: []),
register_node!(graphene_core::vector::generator_nodes::CircleGenerator<_>, input: (), params: [f32]),
register_node!(graphene_core::vector::generator_nodes::EllipseGenerator<_, _>, input: (), params: [f32, f32]),
Expand Down

0 comments on commit ed82c5f

Please sign in to comment.