Коммит 8737855e создал по автору Ilya Portnov's avatar Ilya Portnov
Просмотр файлов

Curve length: possibility to specify precision.

владелец 9cdf8e70
......@@ -6,6 +6,9 @@ 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.algorithms import SvCurveLengthSolver
from sverchok.utils.curve.nurbs import SvNurbsCurve
from sverchok.utils.curve.nurbs_algorithms import SvNurbsCurveLengthSolver
class SvCurveLengthNode(bpy.types.Node, SverchCustomTreeNode):
"""
......@@ -44,6 +47,17 @@ class SvCurveLengthNode(bpy.types.Node, SverchCustomTreeNode):
items = modes,
update = updateNode)
specify_accuracy : BoolProperty(
name = "Specify accuracy",
default = False,
update = updateNode)
accuracy : IntProperty(
name = "Accuracy",
default = 3,
min = 0,
update = updateNode)
def sv_init(self, context):
self.inputs.new('SvCurveSocket', "Curve")
self.inputs.new('SvStringsSocket', "TMin").prop_name = 't_min'
......@@ -54,6 +68,12 @@ class SvCurveLengthNode(bpy.types.Node, SverchCustomTreeNode):
def draw_buttons(self, context, layout):
layout.label(text='T mode:')
layout.prop(self, 'mode', expand=True)
layout.prop(self, 'specify_accuracy')
if self.specify_accuracy:
layout.prop(self, 'accuracy')
def draw_buttons_ext(self, context, layout):
self.draw_buttons(context, layout)
def process(self):
if not any(socket.is_linked for socket in self.outputs):
......@@ -68,6 +88,11 @@ class SvCurveLengthNode(bpy.types.Node, SverchCustomTreeNode):
t_max_s = ensure_nesting_level(t_max_s, 2)
resolution_s = ensure_nesting_level(resolution_s, 2)
if self.specify_accuracy:
tolerance = 10 ** (-self.accuracy)
else:
tolerance = None
length_out = []
for curve, t_mins, t_maxs, resolutions in zip_long_repeat(curves, t_min_s, t_max_s, resolution_s):
for t_min, t_max, resolution in zip_long_repeat(t_mins, t_maxs, resolutions):
......@@ -86,7 +111,9 @@ class SvCurveLengthNode(bpy.types.Node, SverchCustomTreeNode):
resolution = int(resolution * (t_max - t_min) / (curve_t_max - curve_t_min))
if resolution < 1:
resolution = 1
length = curve.calc_length(t_min, t_max, resolution)
solver = SvCurveLengthSolver(curve)
solver.prepare('SPL', resolution, tolerance=tolerance)
length = solver.calc_length(t_min, t_max)
length_out.append([length])
......
......@@ -49,14 +49,41 @@ class SvCurveLengthSolver(object):
raise Exception("You have to call solver.prepare() first")
return self._length_params[-1]
def prepare(self, mode, resolution=50):
def _calc_tknots_fixed(self, resolution):
t_min, t_max = self.curve.get_u_bounds()
tknots = np.linspace(t_min, t_max, num=resolution)
lengths = self.calc_length_segments(tknots)
self._length_params = np.cumsum(np.insert(lengths, 0, 0))
return tknots
def _prepare_find(self, resolution, tolerance, tknots=None, lengths=None, length_params=None):
if tknots is None:
tknots = self._calc_tknots_fixed(resolution)
if lengths is None:
lengths = self.calc_length_segments(tknots)
if length_params is None:
length_params = np.cumsum(np.insert(lengths, 0, 0))
resolution2 = resolution * 2 - 1
tknots2 = self._calc_tknots_fixed(resolution2)
lengths2 = self.calc_length_segments(tknots2)
length_params2 = np.cumsum(np.insert(lengths2, 0, 0))
dl = abs(length_params2[::2] - length_params)
if (dl < tolerance).all():
return tknots2, length_params2
else:
return self._prepare_find(resolution2, tolerance, tknots2, lengths2, length_params2)
def prepare(self, mode, resolution=50, tolerance=None):
if tolerance is None:
tknots = self._calc_tknots_fixed(resolution)
lengths = self.calc_length_segments(tknots)
self._length_params = np.cumsum(np.insert(lengths, 0, 0))
else:
tknots, self._length_params = self._prepare_find(resolution, tolerance)
self._reverse_spline = self._make_spline(mode, tknots, self._length_params)
self._prime_spline = self._make_spline(mode, self._length_params, tknots)
def _make_spline(self, mode, tknots, values):
zeros = np.zeros(len(tknots))
control_points = np.vstack((values, tknots, zeros)).T
......@@ -72,7 +99,7 @@ class SvCurveLengthSolver(object):
if self._prime_spline is None:
raise Exception("You have to call solver.prepare() first")
lengths = self._prime_spline.eval(np.array([t_min, t_max]))
return lengths[1] - lengths[0]
return lengths[1][1] - lengths[0][1]
def calc_length_params(self, ts):
if self._prime_spline is None:
......
......@@ -467,7 +467,7 @@ class SvNurbsCurve(SvCurve):
curve = curve.reparametrize(0, 1)
return curve
def is_line(self, tolerance=0.001):
def is_line(self, tolerance=0.001, use_length_tolerance=False):
# Check that the provided curve is nearly a straight line segment.
# This implementation depends heavily on the fact that this curve is
# NURBS. It uses so-called "godograph property". In short, this
......@@ -488,9 +488,14 @@ class SvNurbsCurve(SvCurve):
dv = cpt2 - cpt1
dv /= np.linalg.norm(dv)
cos_angle = np.dot(dv, direction)
tan_angle = sqrt(1.0 - cos_angle**2) / cos_angle
if vector_len * tan_angle > tolerance:
return False
if use_length_tolerance:
vector2_len = vector_len / cos_angle
if abs(vector2_len - vector_len) > tolerance/10:
return False
else:
tan_angle = sqrt(1.0 - cos_angle**2) / cos_angle
if vector_len * tan_angle > tolerance:
return False
return True
......
......@@ -16,7 +16,7 @@ from sverchok.utils.math import distribute_int
from sverchok.utils.geom import Spline, linear_approximation, intersect_segment_segment
from sverchok.utils.nurbs_common import SvNurbsBasisFunctions, SvNurbsMaths, from_homogenous, CantInsertKnotException
from sverchok.utils.curve import knotvector as sv_knotvector
from sverchok.utils.curve.algorithms import unify_curves_degree
from sverchok.utils.curve.algorithms import unify_curves_degree, SvCurveLengthSolver
from sverchok.utils.decorators import deprecated
from sverchok.utils.logging import getLogger
from sverchok.dependencies import scipy
......@@ -441,3 +441,51 @@ def refine_curve(curve, samples, algorithm=REFINE_DISTRIBUTE, refine_max=False,
return curve
class SvNurbsCurveLengthSolver(SvCurveLengthSolver):
def __init__(self, curve):
self.curve = curve
self._reverse_spline = None
self._prime_spline = None
def _calc_tknots(self, resolution, tolerance):
def middle(segment):
u1, u2 = segment.get_u_bounds()
u = (u1+u2)*0.5
return u
def split(segment):
u = middle(segment)
return segment.split_at(u)
def calc_tknots(segment):
if segment.is_line(tolerance, use_length_tolerance=True):
u1, u2 = segment.get_u_bounds()
return set([u1, u2])
else:
segment1, segment2 = split(segment)
knots1 = calc_tknots(segment1)
knots2 = calc_tknots(segment2)
knots = knots1.union(knots2)
return knots
t_min, t_max = self.curve.get_u_bounds()
init_knots = np.linspace(t_min, t_max, num=resolution)
segments = [self.curve.cut_segment(u1, u2) for u1, u2 in zip(init_knots, init_knots[1:])]
all_knots = set()
for segment in segments:
knots = calc_tknots(segment)
all_knots = all_knots.union(knots)
return np.array(sorted(all_knots))
def prepare(self, mode, resolution=50, tolerance=1e-3):
if tolerance is None:
tolerance = 1e-3
tknots = self._calc_tknots(resolution, tolerance)
lengths = self.calc_length_segments(tknots)
self._length_params = np.cumsum(np.insert(lengths, 0, 0))
self._reverse_spline = self._make_spline(mode, tknots, self._length_params)
self._prime_spline = self._make_spline(mode, self._length_params, tknots)
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать