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

"Adaptive Plot Curve" mk2 #5163

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
- SvExCrossCurvePlaneNode
- SvExCrossCurveSurfaceNode
- ---
- SvAdaptivePlotCurveNode
- SvAdaptivePlotCurveMk2Node
- SvExEvalCurveNode

- Surfaces:
Expand Down
2 changes: 1 addition & 1 deletion menus/full_by_data_type.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@
- SvExMSquaresOnSurfaceNode
- ---
- SvAdaptivePlotNurbsCurveNode
- SvAdaptivePlotCurveNode
- SvAdaptivePlotCurveMk2Node
- SvExEvalCurveNode

- Surfaces:
Expand Down
2 changes: 1 addition & 1 deletion menus/full_nortikin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@
- X CURVES:
- icon_name: OUTLINER_OB_CURVE
- SvAdaptivePlotNurbsCurveNode
- SvAdaptivePlotCurveNode
- SvAdaptivePlotCurveMk2Node
- SvExEvalCurveNode
- X SURFACES:
- icon_name: SURFACE_DATA
Expand Down
127 changes: 127 additions & 0 deletions nodes/curve/adaptive_plot_mk2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
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
from sverchok.utils.curve import SvCurve
from sverchok.utils.adaptive_curve import populate_curve

class SvAdaptivePlotCurveMk2Node(SverchCustomTreeNode, bpy.types.Node):
"""
Triggers: Adaptive Plot Curve
Tooltip: Adaptive Plot Curve
"""
bl_idname = 'SvAdaptivePlotCurveMk2Node'
bl_label = 'Adaptive Plot Curve'
bl_icon = 'CURVE_NCURVE'

def update_sockets(self, context):
self.inputs['Seed'].hide_safe = not self.random
self.inputs['Resolution'].hide_safe = not (self.by_length or self.by_curvature)
updateNode(self, context)

count : IntProperty(
name = "Count",
description = "Total number of points",
min = 2, default = 50,
update = updateNode)

resolution : IntProperty(
name = "Resolution",
description = "Length and curvature calculation resolution",
min = 3, default = 100,
update = updateNode)

random : BoolProperty(
name = "Random",
description = "Distribute points randomly",
default = False,
update = update_sockets)

seed : IntProperty(
name = "Seed",
description = "Random Seed value",
default = 0,
update = updateNode)

by_curvature : BoolProperty(
name = "By Curvature",
description = "Use curve curvature value to distribute additional points on the curve: places with greater curvature value will receive more points",
default = True,
update = update_sockets)

by_length : BoolProperty(
name = "By Length",
description = "Use segment lengths to distribute additional points on the curve: segments with greater length will receive more points",
default = False,
update = update_sockets)

curvature_clip : FloatProperty(
name = "Curvature Clip",
min = 0.0,
default = 100.0,
update = updateNode)

def draw_buttons(self, context, layout):
row = layout.row(align=True)
row.prop(self, 'by_curvature', toggle=True)
row.prop(self, 'by_length', toggle=True)
layout.prop(self, 'random')

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

def sv_init(self, context):
self.inputs.new('SvCurveSocket', "Curve")
self.inputs.new('SvStringsSocket', "Count").prop_name = 'count'
self.inputs.new('SvStringsSocket', "Resolution").prop_name = 'resolution'
self.inputs.new('SvStringsSocket', "Seed").prop_name = 'seed'
self.outputs.new('SvVerticesSocket', "Vertices")
self.outputs.new('SvStringsSocket', "Edges")
self.outputs.new('SvStringsSocket', "T")
self.update_sockets(context)

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

curve_s = self.inputs['Curve'].sv_get()
curve_s = ensure_nesting_level(curve_s, 2, data_types = (SvCurve,))
count_s = self.inputs['Count'].sv_get()
resolution_s = self.inputs['Resolution'].sv_get()
seed_s = self.inputs['Seed'].sv_get()

verts_out = []
edges_out = []
ts_out = []
inputs = zip_long_repeat(curve_s, count_s, resolution_s, seed_s)
for curves, count_i, resolution_i, seed_i in inputs:
objects = zip_long_repeat(curves, count_i, resolution_i, seed_i)
for curve, count, resolution, seed in objects:
if not self.random:
seed = None
new_t = populate_curve(curve, count,
resolution = resolution,
by_length = self.by_length,
by_curvature = self.by_curvature,
curvature_clip = self.curvature_clip,
random = self.random,
seed = seed)
n = len(new_t)
ts_out.append(new_t.tolist())
new_verts = curve.evaluate_array(new_t).tolist()
verts_out.append(new_verts)
new_edges = [(i,i+1) for i in range(n-1)]
edges_out.append(new_edges)

self.outputs['Vertices'].sv_set(verts_out)
self.outputs['Edges'].sv_set(edges_out)
self.outputs['T'].sv_set(ts_out)

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

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

6 changes: 4 additions & 2 deletions nodes/curve/adaptive_plot.py → old_nodes/adaptive_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, zip_long_repeat, ensure_nesting_level
from sverchok.utils.curve import SvCurve
from sverchok.utils.adaptive_curve import populate_curve, MinMaxPerSegment, TotalCount
from sverchok.utils.adaptive_curve import populate_curve_old, MinMaxPerSegment, TotalCount

class SvAdaptivePlotCurveNode(SverchCustomTreeNode, bpy.types.Node):
"""
Expand All @@ -15,6 +15,8 @@ class SvAdaptivePlotCurveNode(SverchCustomTreeNode, bpy.types.Node):
bl_label = 'Adaptive Plot Curve'
bl_icon = 'CURVE_NCURVE'

replacement_nodes = [('SvAdaptivePlotCurveMk2Node', None, None)]

sample_size : IntProperty(
name = "Segments",
description = "Number of initial subdivisions",
Expand Down Expand Up @@ -138,7 +140,7 @@ def process(self):
controller = MinMaxPerSegment(min_ppe, max_ppe)
else:
controller = TotalCount(count)
new_t = populate_curve(curve, samples+1,
new_t = populate_curve_old(curve, samples+1,
by_length = self.by_length,
by_curvature = self.by_curvature,
population_controller = controller,
Expand Down
38 changes: 37 additions & 1 deletion utils/adaptive_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

from sverchok.utils.sv_logging import sv_logger
from sverchok.utils.math import distribute_int
from sverchok.utils.geom import CubicSpline
from sverchok.utils.integrate import TrapezoidIntegral
from sverchok.utils.curve import SvCurveLengthSolver


Expand Down Expand Up @@ -92,7 +94,7 @@ def populate_t_segment(key_ts, target_count):
result.update(ts)
return np.asarray(list(sorted(result)))

def populate_curve(curve, samples_t, by_length = False, by_curvature = True, population_controller = None, curvature_clip = 100, seed = None):
def populate_curve_old(curve, samples_t, by_length = False, by_curvature = True, population_controller = None, curvature_clip = 100, seed = None):
if population_controller is None:
population_controller = MinMaxPerSegment(1, 5)

Expand Down Expand Up @@ -172,3 +174,37 @@ def populate_curve(curve, samples_t, by_length = False, by_curvature = True, pop
new_t = np.sort(new_t)
return new_t

def populate_curve(curve, n_points, resolution=100, by_length = False, by_curvature = True, curvature_clip=100.0, random=False, seed=None):
t_min, t_max = curve.get_u_bounds()
factors = np.zeros((resolution,))
ts = np.linspace(t_min, t_max, num=resolution)
if by_length:
lengths = SvCurveLengthSolver(curve).calc_length_segments(ts)
lengths = np.cumsum(np.insert(lengths, 0, 0))
factors += lengths / lengths[-1]
if by_curvature:
curvatures = curve.curvature_array(ts)
curvatures = np.clip(curvatures, 0.0, curvature_clip)
integral = TrapezoidIntegral(ts, ts, np.sqrt(curvatures))
#integral = TrapezoidIntegral(ts, ts, curvatures)
integral.calc()
factors += integral.summands
if not by_length and not by_curvature:
factors = np.linspace(0.0, 1.0, num=resolution)
factors /= factors[-1]
cpts = np.zeros((resolution, 3))
cpts[:,0] = factors
cpts[:,1] = ts
spline = CubicSpline(cpts, metric='X', is_cyclic=False)
if random:
if seed is None:
seed = 12345
np.random.seed(seed)
factor_values = np.random.uniform(0.0, 1.0, size=n_points)
factor_values = np.append(factor_values, [0.0, 1.0])
factor_values = np.sort(factor_values)
else:
factor_values = np.linspace(0.0, 1.0, num=n_points)
new_ts = spline.eval(factor_values)[:,1]
return new_ts

1 change: 1 addition & 0 deletions utils/curve/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,7 @@ def __init__(self, curve, resolution, rescale_t = False, rescale_curvature = Fal
ts = (ts - t_min) / (t_max - t_min)
if rescale_curvature:
ys = ys / ys[-1]
self.values = ys
zeros = np.zeros(len(ts))
cpts = np.vstack((ts, ys, zeros)).T
self.prime_spline = CubicSpline(cpts, tknots = ts, is_cyclic=False)
Expand Down
Loading