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

On knots precision.

владелец 7129742f
import unittest
from sverchok.utils.logging import error
from sverchok.utils.testing import *
from sverchok.utils.dictionary import SvApproxDict
from sverchok.utils.curve.nurbs_algorithms import KnotvectorDict
class ApproxDictTests(SverchokTestCase):
def test_repr(self):
d = SvApproxDict([(1.0, "A"), (2.0, "B")])
s = repr(d)
self.assertEquals(s, "{1.0: A, 2.0: B}")
def test_dict_1(self):
d = SvApproxDict([(1.0, "A"), (2.0, "B")], precision=1)
d[2.01] = "C"
self.assertEquals(repr(d), "{1.0: A, 2.0: C}")
self.assertEquals(d[1.0], "A")
self.assertEquals(d[1.01], "A")
self.assertEquals(d[0.99], "A")
self.assertEquals(d[2.0], "C")
self.assertEquals(d[2.01], "C")
self.assertEquals(d[1.99], "C")
d[2.5] = "K"
self.assertEquals(repr(d), "{1.0: A, 2.0: C, 2.5: K}")
self.assertEquals(d[2.5], "K")
self.assertEquals(d[2.51], "K")
self.assertEquals(d[2.49], "K")
d[1.5] = "H"
self.assertEquals(repr(d), "{1.0: A, 1.5: H, 2.0: C, 2.5: K}")
self.assertEquals(d[1.5], "H")
self.assertEquals(d[1.51], "H")
self.assertEquals(d[1.49], "H")
class KnotvectorDictTests(SverchokTestCase):
def test_dict_1(self):
d = KnotvectorDict(accuracy=3)
curve1_kv = [0, 0.499, 0.501, 1]
for k in curve1_kv:
d.update(1, k, 1)
curve2_kv = [0, 0.5, 1]
for k in curve2_kv:
d.update(2, k, 1)
expected_items = [(0, 1), (0.499, 1), (0.5, 1), (0.501, 1), (1, 1)]
self.assertEquals(d.items(), expected_items)
def test_dict_2(self):
d = KnotvectorDict(accuracy=3)
curve1_kv = [(0, 4), (0.499, 3), (0.501, 3), (1, 4)]
for k, m in curve1_kv:
d.update(1, k, m)
curve2_kv = [(0, 4), (0.5, 3), (1, 4)]
for k, m in curve2_kv:
d.update(2, k, m)
expected_items = [(0, 4), (0.499, 3), (0.5, 3), (0.501, 3), (1, 4)]
self.assertEquals(d.items(), expected_items)
def test_dict_3(self):
d = KnotvectorDict(accuracy=2)
curve1_kv = [(0,4), (0.499, 3), (0.501, 3), (1, 4)]
for k, m in curve1_kv:
d.update(1, k, m)
expected_items = [(0, 4), (0.499, 3), (0.501, 3), (1, 4)]
self.assertEquals(d.items(), expected_items)
curve2_kv = [(0,4), (0.5, 3), (1, 4)]
for k, m in curve2_kv:
d.update(2, k, m)
expected_items = [(0, 4), (0.499, 3), (0.5, 3), (0.501, 3), (1, 4)]
self.assertEquals(d.items(), expected_items)
......@@ -16,6 +16,7 @@ from sverchok.utils.nurbs_common import SvNurbsBasisFunctions, SvNurbsMaths, fro
from sverchok.utils.curve import knotvector as sv_knotvector
from sverchok.utils.curve.algorithms import unify_curves_degree
from sverchok.utils.decorators import deprecated
from sverchok.utils.dictionary import SvApproxDict
from sverchok.dependencies import scipy
if scipy is not None:
......@@ -32,16 +33,65 @@ def unify_degrees(curves):
curves = [curve.elevate_degree(target=max_degree) for curve in curves]
return curves
def unify_curves(curves, method='UNIFY'):
class KnotvectorDict(object):
def __init__(self, accuracy):
self.multiplicities = []
self.accuracy = accuracy
self.done_knots = set()
self.skip_insertions = defaultdict(list)
def tolerance(self):
return 10**(-self.accuracy)
def update(self, curve_idx, knot, multiplicity):
found_idx = None
found_knot = None
for idx, (c, k, m) in enumerate(self.multiplicities):
if curve_idx != c:
if abs(knot - k) < self.tolerance():
print(f"Found: #{curve_idx}: added {knot} ~= existing {k}")
if (curve_idx, k) not in self.done_knots:
found_idx = idx
found_knot = k
break
if found_idx is not None:
self.multiplicities[found_idx] = (curve_idx, knot, multiplicity)
self.skip_insertions[curve_idx].append(found_knot)
else:
self.multiplicities.append((curve_idx, knot, multiplicity))
self.done_knots.add((curve_idx, knot))
def get(self, knot):
result = 0
for c, k, m in self.multiplicities:
if abs(knot - k) < self.tolerance():
result = max(result, m)
return result
def __repr__(self):
items = [f"c#{c}: {k}: {m}" for c, k, m in self.multiplicities]
s = ", ".join(items)
return "{" + s + "}"
def items(self):
max_per_knot = defaultdict(int)
for c, k, m in self.multiplicities:
max_per_knot[k] = max(max_per_knot[k], m)
keys = sorted(max_per_knot.keys())
return [(key, max_per_knot[key]) for key in keys]
def unify_curves(curves, method='UNIFY', accuracy=6):
tolerance = 10**(-accuracy)
curves = [curve.reparametrize(0.0, 1.0) for curve in curves]
if method == 'UNIFY':
dst_knots = defaultdict(int)
for curve in curves:
m = sv_knotvector.to_multiplicity(curve.get_knotvector())
dst_knots = KnotvectorDict(accuracy)
for i, curve in enumerate(curves):
m = sv_knotvector.to_multiplicity(curve.get_knotvector(), tolerance**2)
print(f"Curve #{i}: degree={curve.get_degree()}, cpts={len(curve.get_control_points())}, {m}")
for u, count in m:
u = round(u, 6)
dst_knots[u] = max(dst_knots[u], count)
dst_knots.update(i, u, count)
result = []
# for i, curve1 in enumerate(curves):
......@@ -50,22 +100,31 @@ def unify_curves(curves, method='UNIFY'):
# curve1 = curve1.to_knotvector(curve2)
# result.append(curve1)
for curve in curves:
for idx, curve in enumerate(curves):
diffs = []
kv = np.round(curve.get_knotvector(), 6)
ms = dict(sv_knotvector.to_multiplicity(kv))
#kv = np.round(curve.get_knotvector(), accuracy)
#curve = curve.copy(knotvector = kv)
print('next curve', curve.get_knotvector())
ms = dict(sv_knotvector.to_multiplicity(curve.get_knotvector(), tolerance**2))
for dst_u, dst_multiplicity in dst_knots.items():
src_multiplicity = ms.get(dst_u, 0)
diff = dst_multiplicity - src_multiplicity
print(f"U = {dst_u}, was = {src_multiplicity}, need = {dst_multiplicity}, diff = {diff}")
diffs.append((dst_u, diff))
#print(f"Src {ms}, dst {dst_knots} => diff {diffs}")
for u, diff in diffs:
if diff > 0:
curve = curve.insert_knot(u, diff)
if u in dst_knots.skip_insertions[idx]:
print(f"C: skip insertion T = {u}")
else:
#kv = curve.get_knotvector()
print(f"C: Insert T = {u} x {diff}")
curve = curve.insert_knot(u, diff)
result.append(curve)
return result
elif method == 'AVERAGE':
kvs = [len(curve.get_control_points()) for curve in curves]
max_kv, min_kv = max(kvs), min(kvs)
......
......@@ -5,6 +5,7 @@
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
import numpy as np
from itertools import zip_longest
class SvDict(dict):
......@@ -135,3 +136,76 @@ class SvDict(dict):
types[i] = 'SvStringsSocket'
return types
class SvApproxDict(object):
def __init__(self, pairs=None, precision=6):
self.precision = precision
self.keys = np.array([])
self.values = np.array([])
if pairs is not None:
for key, value in pairs:
self[key] = value
def tolerance(self):
return 10**(-self.precision)
def __repr__(self):
items = [f"{key}: {value}" for key, value in zip(self.keys, self.values)]
s = ", ".join(items)
return "{" + s + "}"
def __setitem__(self, key, value):
if len(self.keys) == 0:
self.keys = np.array([key])
self.values = np.array([value])
return
i = self.keys.searchsorted(key)
if i > 0:
smaller = self.keys[i-1]
else:
smaller = None
if i < len(self.keys):
greater = self.keys[i]
else:
greater = None
if smaller is not None and (key - smaller) < self.tolerance():
#self.keys[i-1] = 0.5*(key + self.keys[i-1])
self.values[i-1] = value
return
if greater is not None and (greater - key) < self.tolerance():
#self.keys[i] = 0.5*(key + self.keys[i])
self.values[i] = value
return
self.keys = np.insert(self.keys, i, key)
self.values = np.insert(self.values, i, value)
def get(self, key, default=None):
if len(self.keys) == 0:
return default
i = self.keys.searchsorted(key)
if i > 0:
smaller = self.keys[i-1]
if (key - smaller) < self.tolerance():
return self.values[i-1]
if i < len(self.keys):
greater = self.keys[i]
if (greater - key) < self.tolerance():
return self.values[i]
return default
def __getitem__(self, key):
value = self.get(key, None)
if value is None:
raise KeyError("Key not found")
return value
def items(self):
return zip(self.keys, self.values)
......@@ -1175,7 +1175,7 @@ def unify_nurbs_surfaces(surfaces, knots_method = 'UNIFY', knotvector_accuracy=6
for u, diff in diffs_u:
if diff > 0:
#print(f"Insert U = {u} x {diff}")
print(f"S: Insert U = {u} x {diff}")
surface = surface.insert_knot(SvNurbsSurface.U, u, diff)
diffs_v = []
......@@ -1188,7 +1188,7 @@ def unify_nurbs_surfaces(surfaces, knots_method = 'UNIFY', knotvector_accuracy=6
for v, diff in diffs_v:
if diff > 0:
#print(f"Insert V = {v} x {diff}")
print(f"S: Insert V = {v} x {diff}")
surface = surface.insert_knot(SvNurbsSurface.V, v, diff)
result.append(surface)
......
......@@ -53,10 +53,10 @@ def gordon_surface(u_curves, v_curves, intersections, metric='POINTS', u_knots=N
else:
loft_u_kwargs = loft_v_kwargs = interpolate_kwargs = {'metric': metric}
u_curves = unify_curves_degree(u_curves)
u_curves = unify_curves(u_curves)#, method='AVERAGE')
v_curves = unify_curves_degree(v_curves)
v_curves = unify_curves(v_curves)#, method='AVERAGE')
#u_curves = unify_curves_degree(u_curves)
#u_curves = unify_curves(u_curves, accuracy=knotvector_accuracy)#, method='AVERAGE')
#v_curves = unify_curves_degree(v_curves)
#v_curves = unify_curves(v_curves, accuracy=knotvector_accuracy)#, method='AVERAGE')
u_curves_degree = u_curves[0].get_degree()
v_curves_degree = v_curves[0].get_degree()
......@@ -66,8 +66,8 @@ def gordon_surface(u_curves, v_curves, intersections, metric='POINTS', u_knots=N
loft_v_degree = min(len(u_curves)-1, v_curves_degree)
loft_u_degree = min(len(v_curves)-1, u_curves_degree)
_,_,lofted_v = simple_loft(u_curves, degree_v=loft_v_degree, **loft_v_kwargs)
_,_,lofted_u = simple_loft(v_curves, degree_v=loft_u_degree, **loft_u_kwargs)
_,_,lofted_v = simple_loft(u_curves, degree_v=loft_v_degree, knotvector_accuracy = knotvector_accuracy, **loft_v_kwargs)
_,_,lofted_u = simple_loft(v_curves, degree_v=loft_u_degree, knotvector_accuracy = knotvector_accuracy, **loft_u_kwargs)
lofted_u = lofted_u.swap_uv()
int_degree_u = min(m-1, u_curves_degree)
......
......@@ -787,7 +787,7 @@ def build_from_curves(curves, degree_u = None, implementation = SvNurbsSurface.N
return curves, surface
def simple_loft(curves, degree_v = None, knots_u = 'UNIFY', metric='DISTANCE', tknots=None, implementation=SvNurbsSurface.NATIVE):
def simple_loft(curves, degree_v = None, knots_u = 'UNIFY', knotvector_accuracy=6, metric='DISTANCE', tknots=None, implementation=SvNurbsSurface.NATIVE):
"""
Loft between given NURBS curves (a.k.a skinning).
......@@ -809,7 +809,7 @@ def simple_loft(curves, degree_v = None, knots_u = 'UNIFY', metric='DISTANCE', t
curve_class = type(curves[0])
curves = unify_curves_degree(curves)
if knots_u == 'UNIFY':
curves = unify_curves(curves)
curves = unify_curves(curves, accuracy=knotvector_accuracy)
else:
kvs = [len(curve.get_control_points()) for curve in curves]
max_kv, min_kv = max(kvs), min(kvs)
......@@ -824,6 +824,7 @@ def simple_loft(curves, degree_v = None, knots_u = 'UNIFY', metric='DISTANCE', t
raise Exception(f"V degree ({degree_v}) must be not greater than number of curves ({len(curves)}) minus 1")
src_points = [curve.get_homogenous_control_points() for curve in curves]
print("P", [p.shape for p in src_points])
# lens = [len(pts) for pts in src_points]
# max_len, min_len = max(lens), min(lens)
# if max_len != min_len:
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать