Skip to content

Commit

Permalink
Merge pull request #5155 from nortikin/curve_discontinuity
Browse files Browse the repository at this point in the history
"Curve discontinuity" node
  • Loading branch information
portnov authored Oct 19, 2024
2 parents 7ff41d4 + c3fa6fd commit 0c81099
Show file tree
Hide file tree
Showing 12 changed files with 436 additions and 6 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions docs/nodes/curve/discontinuity.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
NURBS Curve Discontinuity
=========================

Functionality
-------------

Given a NURBS (or NURBS-like) curve, this node finds it's "fracture", or
"discontinuity", points.

* Discontinuity points of 1st order are points where curve changes is not
smooth, i.e. it changes it's direction suddenly; more precisely, it's points
where curve derivative (tangent) is not continuous.
* Discontinuity points of 2nd order are points where curve's 2nd derivative is
not continuous;
* Discontinuity points of 3rd order are points where curve's 3rd derivative is
not continuous.

This node can find discontinuities of two kinds:

* Geometric discontinuity is a point where curve derivative (tangent vector or
derivative vector of higher order) changes it's direction suddenly; it does not matter
if it changes it's length or not.
* Parametric discontinuity is a point where curve derivative suddenly changes
it's direction or length.

Geometric discontinuity of 1st order is clearly visible: it's a "sharp corner"
on a curve. Parametric discontinuity may not be visible, if tangent direction
is not changed; in such a case, parametric discontinuity matters only for the
parametrization of the curve.

Inputs
------

This node has the following inputs:

* **Curve**. The curve to be analyzed. This input is mandatory.
* **Order**. Discontinuity order. Possible values are 1, 2 and 3. The default
value is 1.
* **AngleTolerance**. Minimum angle, for which curve's derivative (tangent or
derivative vector of higher order) should change it's direction, in order for
such point to be considered as discontinuity. The value is specified in
radians. The default value is 1e-6 (one per million).
* **AmplitudeTolerance**. This input is available only when **Geometric**
parameter is not checked. Minimum value, for which curve's derivative vector
should change it's length, in order for such point to be considered as
discontinuity. The default value is 1e-6 (one per million).

Parameters
----------

This node has the following parameter:

* **Geometric**. If checked, the node searches for geometric discontinuities
only. Otherwise, it searches for parametric discontinuities. Checked by
default.

Outputs
-------

This node has the following outputs:

* **Segments**. Segments, to which the curve is split by discontinuity points.
* **Points**. Discontinuity points on the curve.
* **T**. Curve's T parameter values for discontinuity points.

Examples of usage
-----------------

Discontinuity of 1st curve is visible as a break (crisp) on the curve. Here we
check only for "Geometric" continuity, which means that we only check
directions of curve tangents, but do not check tangent lengths.

.. image:: ../../../docs/assets/nodes/curve/curve_discontinuity_1.png
:target: ../../../docs/assets/nodes/curve/curve_discontinuity_1.png

Here we check for "parametric" continuity instead of "geometric". Now we see
two points of discontinuity. Point 1 is the same; at point 2, with curve's
control polygon displayed we can see that control points are not symmetric
around this point, in this case this means that curve tangent vector at the
right of this point have much more length, than curve tangent to the left of
this point.

.. image:: ../../../docs/assets/nodes/curve/curve_discontinuity_2.png
:target: ../../../docs/assets/nodes/curve/curve_discontinuity_2.png

Since this curve is composed of several Bezier segments, in all points where
these segments are "glued" together there are points of discontinuity of 2nd
order. If you look at curvature comb near these points, you can see that it can
be discontinuous ("broken"). I.e., at these points curve's curvature may change
suddenly.

.. image:: ../../../docs/assets/nodes/curve/curve_discontinuity_3.png
:target: ../../../docs/assets/nodes/curve/curve_discontinuity_3.png

This is a generic NURBS curve. It does not have discontinuity points of 1st or
2nd order. But it has discontinuity points of 3rd order; At these points the
curve itself looks pretty smooth, but if you look closely at curvature comb you
can see that, albeit continuous, curvature comb is not smooth at these points.

.. image:: ../../../docs/assets/nodes/curve/curve_discontinuity_4.png
:target: ../../../docs/assets/nodes/curve/curve_discontinuity_4.png

1 change: 1 addition & 0 deletions index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
- SvExNurbsCurveNode
- SvApproxNurbsCurveMk3Node
- SvExInterpolateNurbsCurveNodeMK2
- SvCurveDiscontinuityNode
- SvFilletCurveNode
- SvDeconstructCurveNode
- SvNurbsCurveNodesNode
Expand Down
1 change: 1 addition & 0 deletions menus/full_by_data_type.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@
- SvIntersectNurbsCurvesNode
- SvPrepareCurvesNetNode
- SvNurbsCurveNodesNode
- SvCurveDiscontinuityNode
- SvDeconstructCurveNode
- SvExMarchingSquaresNode
- SvExMSquaresOnSurfaceNode
Expand Down
1 change: 1 addition & 0 deletions menus/full_nortikin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@
- SvExCurveExtremesNode
- ---
- SvNurbsCurveNodesNode
- SvCurveDiscontinuityNode
- SvDeconstructCurveNode
- ---
- SvExNearestPointOnCurveNode
Expand Down
127 changes: 127 additions & 0 deletions nodes/curve/discontinuity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE

import numpy as np
import bpy
from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level, get_data_nesting_level
from sverchok.utils.curve.core import SvCurve
from sverchok.utils.curve.nurbs import SvNurbsCurve

class SvCurveDiscontinuityNode(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Curve Break Discontinuity
Tooltip: Find points of curve discontinuity, and break curve into segments
"""
bl_idname = 'SvCurveDiscontinuityNode'
bl_label = 'NURBS Curve Discontinuity'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_CURVE_DISCONTINUITY'

def update_sockets(self, context):
self.inputs['AmplitudeTolerance'].hide_safe = self.direction_only
updateNode(self, context)

order : IntProperty(
name = "Order",
description = "Discontinuity order",
default = 1,
min = 1, max = 3,
update = updateNode)

direction_only : BoolProperty(
name = "Geometric",
description = "If checked, check only direction of derivatives, not their lengths",
default = True,
update = update_sockets)

angle_tolerance : FloatProperty(
name = "AngleTolerance",
precision = 8,
default = 1e-6,
min = 0.0,
update = updateNode)

amplitude_tolerance : FloatProperty(
name = "AmplitudeTolerance",
precision = 8,
default = 1e-6,
min = 0.0,
update = updateNode)

def sv_init(self, context):
self.inputs.new('SvCurveSocket', "Curve")
self.inputs.new('SvStringsSocket', "Order").prop_name = 'order'
self.inputs.new('SvStringsSocket', "AngleTolerance").prop_name = 'angle_tolerance'
self.inputs.new('SvStringsSocket', "AmplitudeTolerance").prop_name = 'amplitude_tolerance'
self.outputs.new('SvCurveSocket', "Segments")
self.outputs.new('SvVerticesSocket', "Points")
self.outputs.new('SvStringsSocket', "T")
self.update_sockets(context)

def draw_buttons(self, context, layout):
layout.prop(self, 'direction_only')

def process(self):
if not any(socket.is_linked for socket in self.outputs):
return

curve_s = self.inputs['Curve'].sv_get()
order_s = self.inputs['Order'].sv_get()
angle_tolerance_s = self.inputs['AngleTolerance'].sv_get()
amplitude_tolerance_s = self.inputs['AmplitudeTolerance'].sv_get()

input_level = get_data_nesting_level(curve_s, data_types=(SvCurve,))
flat_output = input_level < 2

curve_s = ensure_nesting_level(curve_s, 2, data_types=(SvCurve,))
order_s = ensure_nesting_level(order_s, 2)
angle_tolerance_s = ensure_nesting_level(angle_tolerance_s, 2)
amplitude_tolerance_s = ensure_nesting_level(amplitude_tolerance_s, 2)

segments_out = []
points_out = []
t_out = []
for params in zip_long_repeat(curve_s, order_s, angle_tolerance_s, amplitude_tolerance_s):
new_segments = []
new_points = []
new_t = []
for curve, order, angle_tolerance, amplitude_tolerance in zip_long_repeat(*params):
curve = SvNurbsCurve.to_nurbs(curve)
if curve is None:
raise Exception("One of curves is not NURBS!")
ts, points, segments = curve.split_at_fracture_points(
order = order,
direction_only = self.direction_only,
or_worse = True,
angle_tolerance = angle_tolerance,
amplitude_tolerance = amplitude_tolerance,
return_details = True)
new_t.append(ts)
new_points.append(points)
new_segments.append(segments)
if flat_output:
segments_out.extend(new_segments)
points_out.extend(new_points)
t_out.extend(new_t)
else:
segments_out.append(new_segments)
points_out.append(new_points)
t_out.append(new_t)

self.outputs['Segments'].sv_set(segments_out)
self.outputs['Points'].sv_set(points_out)
self.outputs['T'].sv_set(t_out)

def register():
bpy.utils.register_class(SvCurveDiscontinuityNode)

def unregister():
bpy.utils.unregister_class(SvCurveDiscontinuityNode)

Binary file added ui/icons/sv_curve_discontinuity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0c81099

Please sign in to comment.