Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate knotvector node #4789

Merged
merged 6 commits into from
Dec 4, 2022
Merged
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
77 changes: 77 additions & 0 deletions docs/nodes/curve/generate_knotvector.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
Generate Knotvector
===================

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

This node provides several ways of generating a knotvector for building (or
interpolation) of NURBS curves and surfaces.

Inputs
------

This node has the following inputs:

* **Vertices**. Points to be used to calculate distance between them and
calculate knotvector based on those distances. This input is available and
mandatory only when **Mode** parameter is set to **From Points**.
* **Knots**. Values of T parameter, which will be used to calculate knotvector.
This input is available and mandatory only when **Mode** parameter is set to
**From T values**.
* **Degree**. Degree of the curve (or surface), for which the knotvector is to
be generated. The default value is 3.
* **ControlPointsCount**. Number of curve (or surface) control points. This
input is available either when **Mode** parameter is set to **Uniform**, or
when **Set control points number** is checked. The default value is 4.

Parameters
----------

This node has the following parameters:

* **Mode**. This defines how the knotvector will be generated. The available options are:

* **Uniform**. Generate a uniform knotvector. Such knotvector is defined by
number of control points and degree. The knotvector can be generated as
clamped or non-clamped, depending on **Clamped** parameter.
* **From Points**. Calculate distances between some points, and generate a
non-uniform knotvector based on those distances. This option can be useful
for some custom interpolation algorithms. The generated knotvector will be
always clamped.
* **From T Values**. Generate a knotvector based on T values, which
correspond to some points. This can be useful for some custom interpolation
algorithms, if no standard metric fits you. The generated knotvector will
be always clamped.

* **Metric**. The metric to be used to calculate distances between points. This
parameter is only available when **Mode** parameter is set to **From
Points**. The default option is **Euclidian**.
* **Set control points number**. This parameter is only available when **Mode**
parameter is set to **From Points** or **From T Values**. If checked, the
node will allow to specify arbitrary number of curve's (or surface's) control
points, even though usually it should be derived from the number of points or
T values provided. Unchecked by default.
* **Include endpoints**. This parameter is only available when **Mode**
parameter is set to **From Points** or **From T Values**. If checked, the
algorithm of knotvector generation will be changed in such a way, that first
and last T values (or points positions) will also affect knot values, in the
beginning and in the end of knotvector, respectively. With usual algorithm,
they do not. When this parameter is checked, the number of control points
used to generate the knotvector will be actually increased by 2. This can be
useful for some custom interpolation algorithms, when you need to add another
degree of freedom near curve ends - for example, to be able to specify custom
curve tangents. Unchecked by default.
* **Clamped**. This parameter is available only when **Mode** parameter is set
to **Uniform**. This defines whether the node will generated a clamped
knotvector or non-clamped. Checked by default.
* **Numpy output**. This parameter is available in the N panel only. If
checked, the node will output numbers as NumPy arrays. Unchecked by default.

Outputs
-------

This node has the following outputs:

* **Knotvector**. Generated knotvector.
* **Knots**. Knot values - all unique values from the knotvector.

1 change: 1 addition & 0 deletions index.yaml
Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@
- SvCurveRemoveKnotNode
- SvRefineNurbsCurveNode
- SvCurveRemoveExcessiveKnotsNode
- SvGenerateKnotvectorNode
- ---
- SvCurveElevateDegreeNode
- SvCurveReduceDegreeNode
174 changes: 174 additions & 0 deletions nodes/curve/generate_knotvector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# 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, get_data_nesting_level, ensure_nesting_level
from sverchok.utils.math import supported_metrics, xyz_metrics
from sverchok.utils.curve import knotvector as sv_knotvector
from sverchok.utils.geom import Spline

class SvGenerateKnotvectorNode(bpy.types.Node, SverchCustomTreeNode):
"""
Triggers: Generate Knotvector
Tooltip: Calculate knot values for a spline or NURBS curve
"""
bl_idname = 'SvGenerateKnotvectorNode'
bl_label = 'Generate Knotvector'
bl_icon = 'CURVE_NCURVE'
#sv_icon = 'SV_KNOTVECTOR'
sv_icon = 'SV_ALPHA'

degree : IntProperty(
name = "Degree",
description = "Curve degree",
min = 1,
default = 3,
update = updateNode)

num_cpts : IntProperty(
name = "Control Points",
description = "Number of control points",
min = 2,
default = 4,
update = updateNode)

metric: EnumProperty(
name='Metric',
description = "Knot mode",
default="DISTANCE", items=supported_metrics + xyz_metrics,
update=updateNode)

clamped : BoolProperty(
name = "Clamped",
description = "Generate clamped knotvector",
default = True,
update=updateNode)

modes = [
('UNIFORM', "Uniform", "Generate uniform knotvector based on number of control points and degree", 0),
('POINTS', "From Points", "Generate knotvector from point positions, based on some metric", 1),
('KNOTS', "From T Values", "Generate knotvector from values of T parameter", 2)
]

def update_sockets(self, context):
self.inputs['Vertices'].hide_safe = self.mode != 'POINTS'
self.inputs['ControlPointsCount'].hide_safe = (self.mode != 'UNIFORM') and not self.rescale
self.inputs['Knots'].hide_safe = self.mode != 'KNOTS'
updateNode(self, context)

mode : EnumProperty(
name = "Mode",
description = "How to generate knotvector",
items = modes, default = 'UNIFORM',
update = update_sockets)

rescale : BoolProperty(
name = "Set control points number",
description = "Rescale generated knotvector to fit the specified control points count",
default = False,
update = update_sockets)

include_endpoints : BoolProperty(
name = "Consider end points",
description = "Include first and last values of T parameter in knotvector calculation. This increases the length of knot vector by 2.",
default = False,
update = updateNode)

numpy_out : BoolProperty(
name = "NumPy output",
default = False,
update=updateNode)

def draw_buttons(self, context, layout):
layout.prop(self, 'mode', text='')
if self.mode == 'POINTS':
layout.prop(self, 'metric')
if self.mode in ['POINTS', 'KNOTS']:
layout.prop(self, 'rescale')
layout.prop(self, 'include_endpoints')
if self.mode == 'UNIFORM':
layout.prop(self, 'clamped')

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

def sv_init(self, context):
self.inputs.new('SvVerticesSocket', "Vertices")
self.inputs.new('SvStringsSocket', "Knots")
self.inputs.new('SvStringsSocket', "Degree").prop_name = 'degree'
self.inputs.new('SvStringsSocket', "ControlPointsCount").prop_name = 'num_cpts'
self.outputs.new('SvStringsSocket', "Knotvector")
self.outputs.new('SvStringsSocket', "Knots")
self.update_sockets(context)

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

vertices_s = self.inputs['Vertices'].sv_get(default = [[[[0]]]])
knots_s = self.inputs['Knots'].sv_get(default = [[[0]]])
degree_s = self.inputs['Degree'].sv_get()
num_cpts_s = self.inputs['ControlPointsCount'].sv_get()

input_level = get_data_nesting_level(vertices_s)
nested_output = input_level == 4

vertices_s = ensure_nesting_level(vertices_s, 4)
knots_s = ensure_nesting_level(knots_s, 3)
degree_s = ensure_nesting_level(degree_s, 2)
num_cpts_s = ensure_nesting_level(num_cpts_s, 2)

knotvector_out = []
knots_out = []
for params in zip_long_repeat(vertices_s, knots_s, degree_s, num_cpts_s):
new_knotvectors = []
new_knots = []
for vertices, knots, degree, num_cpts in zip_long_repeat(*params):
if self.mode == 'UNIFORM':
knotvector = sv_knotvector.generate(degree, num_cpts, clamped = self.clamped)
knots = np.unique(knotvector)
elif self.mode == 'POINTS':
if not self.rescale:
num_cpts = None
knots = Spline.create_knots(np.asarray(vertices), metric = self.metric)
knotvector = sv_knotvector.from_tknots(degree, knots,
include_endpoints = self.include_endpoints, n_cpts=num_cpts)
else: # KNOTS
if not self.rescale:
num_cpts = None
knots = np.asarray(knots)
knotvector = sv_knotvector.from_tknots(degree, knots,
include_endpoints = self.include_endpoints, n_cpts=num_cpts)
if not self.numpy_out:
knots = knots.tolist()
knotvector = knotvector.tolist()

if nested_output:
new_knotvectors.append(knotvector)
new_knots.append(knots)
else:
new_knotvectors.extend(knotvector)
new_knots.extend(knots)

knotvector_out.append(new_knotvectors)
knots_out.append(new_knots)

self.outputs['Knotvector'].sv_set(knotvector_out)
self.outputs['Knots'].sv_set(knots_out)

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

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

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