From 3669434e12f5f0e18d8e287522695d55e4d25bb5 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Thu, 15 Jul 2021 00:13:14 +0500 Subject: [PATCH 01/21] remove_knot debug. --- utils/curve/nurbs.py | 121 +++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 51 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index b877ad96f..b9cbd1fd8 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -907,26 +907,28 @@ class SvNativeNurbsCurve(SvNurbsCurve): control_points, weights) return curve - def remove_knot(self, u, count=1, tol=1e-4): + def remove_knot(self, u, count=1, tolerance=1e-4): # Implementation adapted from Geomdl - def knot_removal_alpha_i(u, degree, knotvector, count, idx): - return (u - knotvector[idx]) / (knotvector[idx + degree + 1 + count] - knotvector[idx]) - - def knot_removal_alpha_j(u, degree, knotvector, count, idx): - return (u - knotvector[idx - count]) / (knotvector[idx + degree + 1] - knotvector[idx - count]) - - def point_distance(p1, p2): - return np.linalg.norm(np.array(p1) - np.array(p2)) - degree = self.get_degree() + order = degree+1 knotvector = self.get_knotvector() - ctrlpts = self.get_homogenous_control_points().tolist() + ctrlpts = self.get_homogenous_control_points() N = len(ctrlpts) + def knot_removal_alpha_i(u, knotvector, count, idx): + return (u - knotvector[idx]) / (knotvector[idx + order + count] - knotvector[idx]) + + def knot_removal_alpha_j(u, knotvector, count, idx): + return (u - knotvector[idx - count]) / (knotvector[idx + order] - knotvector[idx - count]) + + def point_distance(p1, p2): + return np.linalg.norm(p1 - p2) + #return np.linalg.norm(np.array(p1) - np.array(p2)) + s = sv_knotvector.find_multiplicity(knotvector, u)#-1 # multiplicity - r = knotvector.searchsorted(u, side='right')- 1 # knot span - #r = sv_knotvector.find_span(self.knotvector, N, u) + #r = knotvector.searchsorted(u, side='right')- 1 # knot span + r = sv_knotvector.find_span(self.knotvector, N, u) # Edge case if count < 1: @@ -940,26 +942,28 @@ class SvNativeNurbsCurve(SvNurbsCurve): ctrlpts_new = deepcopy(ctrlpts) # Initialize temp array for storing new control points - temp = [[] for _ in range((2 * degree) + 1)] + temp = np.zeros((2*degree+1, 4)) + removed_count = 1 # Loop for Eqs 5.28 & 5.29 for t in range(0, count): - #print(f"T: {t} / {count}, first = {first}, last = {last}; N = {len(ctrlpts)}, degree = {degree}, r = {r}, s = {s}") - temp[0] = ctrlpts[first - 1] - temp[last - first + 2] = ctrlpts[last + 1] + print(f"T: {t} / {count}, first = {first}, last = {last}; N = {len(ctrlpts)}, degree = {degree}, r = {r}, s = {s}") + offset = first - 1 # difference in index between `temp` and ctrlpts + temp[0] = ctrlpts[offset] + temp[last + 1 - offset] = ctrlpts[last + 1] i = first j = last ii = 1 - jj = last - first + 1 - remflag = False + jj = last - offset + can_remove = False # Compute control points for one removal step while j - i >= t: - alpha_i = knot_removal_alpha_i(u, degree, knotvector, t, i) - alpha_j = knot_removal_alpha_j(u, degree, knotvector, t, j) + alpha_i = knot_removal_alpha_i(u, knotvector, t, i) + alpha_j = knot_removal_alpha_j(u, knotvector, t, j) - temp[ii] = [(cpt - (1.0 - alpha_i) * ti) / alpha_i for cpt, ti in zip(ctrlpts[i], temp[ii - 1])] - temp[jj] = [(cpt - alpha_j * tj) / (1.0 - alpha_j) for cpt, tj in zip(ctrlpts[j], temp[jj + 1])] + temp[ii] = (ctrlpts[i] - (1.0 - alpha_i)*temp[ii - 1]) / alpha_i + temp[jj] = (ctrlpts[j] - alpha_j*temp[ii + 1]) / (1.0 - alpha_j) i += 1 j -= 1 @@ -968,53 +972,68 @@ class SvNativeNurbsCurve(SvNurbsCurve): # Check if the knot is removable if j - i < t: - if point_distance(temp[ii - 1], temp[jj + 1]) <= tol: - remflag = True + dist = point_distance(temp[ii - 1], temp[jj + 1]) + if dist <= tolerance: + print(f"F.1, t={t}, i={i}, j={j}, ii={ii}, jj={jj}, dist={dist}") + can_remove = True else: - alpha_i = knot_removal_alpha_i(u, degree, knotvector, t, i) - ptn = [(alpha_i * t1) + ((1.0 - alpha_i) * t2) for t1, t2 in zip(temp[ii + t + 1], temp[ii - 1])] - if point_distance(ctrlpts[i], ptn) <= tol: - remflag = True + alpha_i = knot_removal_alpha_i(u, knotvector, t, i) + ptn = alpha_i * temp[ii + t + 1] + (1.0 - alpha_i)*temp[ii - 1] + dist = point_distance(ctrlpts[i], ptn) + if dist <= tolerance: + print(f"F.2, dist={dist}") + can_remove = True # Check if we can remove the knot and update new control points array - if remflag: + if can_remove: + print(f"T={t}: remove") i = first j = last while j - i > t: - ctrlpts_new[i] = temp[i - first + 1] - ctrlpts_new[j] = temp[j - first + 1] + ctrlpts_new[i] = temp[i - offset] + ctrlpts_new[j] = temp[j - offset] i += 1 j -= 1 + # Update indices + first -= 1 + last += 1 + removed_count += 1 + else: + print(f"T={t}: stop") break #raise Exception(f"Knot {u} can not be removed {count} times") - # Update indices - first -= 1 - last += 1 + if removed_count < count: + raise Exception(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") - # Fix indexing - t += 1 + new_kv = np.copy(self.get_knotvector()) - # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) - j = int((2*r - s - degree) / 2) # first control point out - i = j - for k in range(1, t): - if k % 2 == 1: - i += 1 - else: - j -= 1 - for k in range(i+1, len(ctrlpts)): - ctrlpts_new[j] = ctrlpts_new[k] - j += 1 + if t > 0: + m = N + degree + 1 + for k in range(r+1, m): + new_kv[k-t] = new_kv[k] + new_kv = new_kv[:m-t] + #new_kv = np.delete(self.get_knotvector(), np.s_[(r-t+1):(r+1)]) + + # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) + j = int((2*r - s - degree) / 2) # first control point out + i = j + for k in range(1, t): + if k % 2 == 1: + i += 1 + else: + j -= 1 + for k in range(i+1, N): + ctrlpts_new[j] = ctrlpts_new[k] + j += 1 - # Slice to get the new control points - ctrlpts_new = ctrlpts_new[0:-t] + # Slice to get the new control points + ctrlpts_new = ctrlpts_new[0:-t] ctrlpts_new = np.array(ctrlpts_new) control_points, weights = from_homogenous(ctrlpts_new) - new_kv = np.delete(self.get_knotvector(), np.s_[(r-t+1):(r+1)]) #print(f"R: r = {r}, t = {t}, N ctrlpts {len(ctrlpts_new)}") #print(f" {self.get_knotvector()} => {new_kv}") -- GitLab From 37d7096fa27d918d6a945212e691128cdc7daa51 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Thu, 15 Jul 2021 23:05:35 +0500 Subject: [PATCH 02/21] API update. --- utils/curve/nurbs.py | 69 +++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index b9cbd1fd8..7e742b280 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -28,6 +28,9 @@ from sverchok.dependencies import geomdl if geomdl is not None: from geomdl import NURBS, BSpline, operations, fitting +class CantRemoveKnotException(Exception): + pass + ################## # # # Curves # @@ -495,7 +498,10 @@ class SvNurbsCurve(SvCurve): def insert_knot(self, u, count=1): raise Exception("Not implemented!") - def remove_knot(self, u, count=1): + ALL = 'ALL' + ALL_BUT_ONE = 'ALL_BUT_ONE' + + def remove_knot(self, u, count=1, target=None, tolerance=1e-6): raise Exception("Not implemented!") def get_min_continuity(self): @@ -698,11 +704,30 @@ class SvGeomdlCurve(SvNurbsCurve): r.u_bounds = self.u_bounds return r - def remove_knot(self, u, count=1): + def remove_knot(self, u, count=1, target=None): + if (count is None) == (target is None): + raise Exception("Either count or target must be specified") + + knotvector = self.get_knotvector() + orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) + if count is None: + if target == SvNurbsCurve.ALL: + count = orig_multiplicity + elif target == SvNurbsCurve.ALL_BUT_ONE: + count = orig_multiplicity - 1 + else: + count = orig_multiplicity - target + curve = self.copy() curve = operations.remove_knot(curve.curve, [u], [count]) result = SvGeomdlCurve(curve) result.u_bounds = self.u_bounds + + new_kv = result.get_knotvector() + new_multiplicity = sv_knotvector.find_multiplicity(new_kv, u) + if orig_multiplicity - count < new_multiplicity: + raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {orig_multiplicity - count} times") + return result class SvNativeNurbsCurve(SvNurbsCurve): @@ -907,9 +932,12 @@ class SvNativeNurbsCurve(SvNurbsCurve): control_points, weights) return curve - def remove_knot(self, u, count=1, tolerance=1e-4): + def remove_knot(self, u, count=1, target=None, tolerance=1e-6): # Implementation adapted from Geomdl + if (count is None) == (target is None): + raise Exception("Either count or target must be specified") + degree = self.get_degree() order = degree+1 knotvector = self.get_knotvector() @@ -926,17 +954,27 @@ class SvNativeNurbsCurve(SvNurbsCurve): return np.linalg.norm(p1 - p2) #return np.linalg.norm(np.array(p1) - np.array(p2)) - s = sv_knotvector.find_multiplicity(knotvector, u)#-1 # multiplicity - #r = knotvector.searchsorted(u, side='right')- 1 # knot span - r = sv_knotvector.find_span(self.knotvector, N, u) + orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) # multiplicity + knot_span = sv_knotvector.find_span(self.knotvector, N, u) + + if count is None: + if target == SvNurbsCurve.ALL: + count = orig_multiplicity + elif target == SvNurbsCurve.ALL_BUT_ONE: + count = orig_multiplicity - 1 + else: + count = orig_multiplicity - target # Edge case if count < 1: return self + if count > orig_multiplicity: + raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but it's multiplicity is only {orig_multiplicity}") + # Initialize variables - first = r - degree - last = r - s + first = knot_span - degree + last = knot_span - orig_multiplicity # Don't change input variables, prepare new ones for updating ctrlpts_new = deepcopy(ctrlpts) @@ -947,7 +985,6 @@ class SvNativeNurbsCurve(SvNurbsCurve): removed_count = 1 # Loop for Eqs 5.28 & 5.29 for t in range(0, count): - print(f"T: {t} / {count}, first = {first}, last = {last}; N = {len(ctrlpts)}, degree = {degree}, r = {r}, s = {s}") offset = first - 1 # difference in index between `temp` and ctrlpts temp[0] = ctrlpts[offset] temp[last + 1 - offset] = ctrlpts[last + 1] @@ -974,19 +1011,16 @@ class SvNativeNurbsCurve(SvNurbsCurve): if j - i < t: dist = point_distance(temp[ii - 1], temp[jj + 1]) if dist <= tolerance: - print(f"F.1, t={t}, i={i}, j={j}, ii={ii}, jj={jj}, dist={dist}") can_remove = True else: alpha_i = knot_removal_alpha_i(u, knotvector, t, i) ptn = alpha_i * temp[ii + t + 1] + (1.0 - alpha_i)*temp[ii - 1] dist = point_distance(ctrlpts[i], ptn) if dist <= tolerance: - print(f"F.2, dist={dist}") can_remove = True # Check if we can remove the knot and update new control points array if can_remove: - print(f"T={t}: remove") i = first j = last while j - i > t: @@ -1000,24 +1034,22 @@ class SvNativeNurbsCurve(SvNurbsCurve): removed_count += 1 else: - print(f"T={t}: stop") break - #raise Exception(f"Knot {u} can not be removed {count} times") if removed_count < count: - raise Exception(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") + raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") new_kv = np.copy(self.get_knotvector()) if t > 0: m = N + degree + 1 - for k in range(r+1, m): + for k in range(knot_span+1, m): new_kv[k-t] = new_kv[k] new_kv = new_kv[:m-t] #new_kv = np.delete(self.get_knotvector(), np.s_[(r-t+1):(r+1)]) # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) - j = int((2*r - s - degree) / 2) # first control point out + j = int((2*knot_span - orig_multiplicity - degree) / 2) # first control point out i = j for k in range(1, t): if k % 2 == 1: @@ -1034,9 +1066,6 @@ class SvNativeNurbsCurve(SvNurbsCurve): ctrlpts_new = np.array(ctrlpts_new) control_points, weights = from_homogenous(ctrlpts_new) - #print(f"R: r = {r}, t = {t}, N ctrlpts {len(ctrlpts_new)}") - #print(f" {self.get_knotvector()} => {new_kv}") - return self.copy(knotvector = new_kv, control_points = control_points, weights = weights) -- GitLab From d4ed5431082c8ea13f25f8fad6db931dce47a494 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Thu, 15 Jul 2021 23:51:51 +0500 Subject: [PATCH 03/21] update API. --- utils/curve/nurbs.py | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 7e742b280..d900f9941 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -44,6 +44,9 @@ class SvNurbsCurve(SvCurve): NATIVE = SvNurbsMaths.NATIVE GEOMDL = SvNurbsMaths.GEOMDL + ALL = 'ALL' + ALL_BUT_ONE = 'ALL_BUT_ONE' + @classmethod def build(cls, implementation, degree, knotvector, control_points, weights=None, normalize_knots=False): return SvNurbsMaths.build_curve(implementation, degree, knotvector, control_points, weights, normalize_knots) @@ -186,7 +189,7 @@ class SvNurbsCurve(SvCurve): if remove_knots == True: remove_knots = p-1 join_point = kv1[-1] - result = result.remove_knot(join_point, remove_knots) + result = result.remove_knot(join_point, count=remove_knots, if_possible=True) return result def lerp_to(self, curve2, coefficient): @@ -498,9 +501,6 @@ class SvNurbsCurve(SvCurve): def insert_knot(self, u, count=1): raise Exception("Not implemented!") - ALL = 'ALL' - ALL_BUT_ONE = 'ALL_BUT_ONE' - def remove_knot(self, u, count=1, target=None, tolerance=1e-6): raise Exception("Not implemented!") @@ -704,19 +704,18 @@ class SvGeomdlCurve(SvNurbsCurve): r.u_bounds = self.u_bounds return r - def remove_knot(self, u, count=1, target=None): + def remove_knot(self, u, count=1, target=None, if_possible=False): if (count is None) == (target is None): raise Exception("Either count or target must be specified") knotvector = self.get_knotvector() orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) - if count is None: - if target == SvNurbsCurve.ALL: - count = orig_multiplicity - elif target == SvNurbsCurve.ALL_BUT_ONE: - count = orig_multiplicity - 1 - else: - count = orig_multiplicity - target + if count == SvNurbsCurve.ALL: + count = orig_multiplicity + elif count == SvNurbsCurve.ALL_BUT_ONE: + count = orig_multiplicity - 1 + elif count is None: + count = orig_multiplicity - target curve = self.copy() curve = operations.remove_knot(curve.curve, [u], [count]) @@ -725,7 +724,7 @@ class SvGeomdlCurve(SvNurbsCurve): new_kv = result.get_knotvector() new_multiplicity = sv_knotvector.find_multiplicity(new_kv, u) - if orig_multiplicity - count < new_multiplicity: + if not if_possible and (orig_multiplicity - count < new_multiplicity): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {orig_multiplicity - count} times") return result @@ -932,7 +931,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): control_points, weights) return curve - def remove_knot(self, u, count=1, target=None, tolerance=1e-6): + def remove_knot(self, u, count=1, target=None, tolerance=1e-6, if_possible=False): # Implementation adapted from Geomdl if (count is None) == (target is None): @@ -957,19 +956,18 @@ class SvNativeNurbsCurve(SvNurbsCurve): orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) # multiplicity knot_span = sv_knotvector.find_span(self.knotvector, N, u) - if count is None: - if target == SvNurbsCurve.ALL: - count = orig_multiplicity - elif target == SvNurbsCurve.ALL_BUT_ONE: - count = orig_multiplicity - 1 - else: - count = orig_multiplicity - target + if count == SvNurbsCurve.ALL: + count = orig_multiplicity + elif count == SvNurbsCurve.ALL_BUT_ONE: + count = orig_multiplicity - 1 + elif count is None: + count = orig_multiplicity - target # Edge case if count < 1: return self - if count > orig_multiplicity: + if not if_possible and (count > orig_multiplicity): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but it's multiplicity is only {orig_multiplicity}") # Initialize variables @@ -1036,7 +1034,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): else: break - if removed_count < count: + if not if_possible and (removed_count < count): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") new_kv = np.copy(self.get_knotvector()) -- GitLab From 80635a2cec6a7417a5601387e6b43aff908e0c8a Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 12:42:17 +0500 Subject: [PATCH 04/21] remove_knot fixes. --- utils/curve/nurbs.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index d900f9941..011fce9fd 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -978,14 +978,15 @@ class SvNativeNurbsCurve(SvNurbsCurve): ctrlpts_new = deepcopy(ctrlpts) # Initialize temp array for storing new control points - temp = np.zeros((2*degree+1, 4)) + temp_i = np.zeros((2*degree+1, 4)) + temp_j = np.zeros((2*degree+1, 4)) - removed_count = 1 + removed_count = 0 # Loop for Eqs 5.28 & 5.29 for t in range(0, count): offset = first - 1 # difference in index between `temp` and ctrlpts - temp[0] = ctrlpts[offset] - temp[last + 1 - offset] = ctrlpts[last + 1] + temp_i[0] = ctrlpts[offset] + temp_j[last + 1 - offset] = ctrlpts[last + 1] i = first j = last ii = 1 @@ -993,12 +994,16 @@ class SvNativeNurbsCurve(SvNurbsCurve): can_remove = False # Compute control points for one removal step - while j - i >= t: + while j - i > t: alpha_i = knot_removal_alpha_i(u, knotvector, t, i) alpha_j = knot_removal_alpha_j(u, knotvector, t, j) + print(f"A[i] = {alpha_i}, A[j] = {alpha_j}") + print(f"C[i] = {ctrlpts[i]}, C[j] = {ctrlpts[j]}, temp[ii-1] = {temp_i[ii-1]}, temp[jj+1] = {temp_j[jj+1]}") - temp[ii] = (ctrlpts[i] - (1.0 - alpha_i)*temp[ii - 1]) / alpha_i - temp[jj] = (ctrlpts[j] - alpha_j*temp[ii + 1]) / (1.0 - alpha_j) + temp_i[ii] = (ctrlpts[i] - (1.0 - alpha_i)*temp_i[ii - 1]) / alpha_i + print(f"temp[ii={ii}] := {temp_i[ii]}") + temp_j[jj] = (ctrlpts[j] - alpha_j*temp_j[jj + 1]) / (1.0 - alpha_j) + print(f"temp[jj={jj}] := {temp_j[jj]}") i += 1 j -= 1 @@ -1007,23 +1012,26 @@ class SvNativeNurbsCurve(SvNurbsCurve): # Check if the knot is removable if j - i < t: - dist = point_distance(temp[ii - 1], temp[jj + 1]) + dist = point_distance(temp_i[ii - 1], temp_j[jj + 1]) + print(f"F.1: first={first}, last={last}, i={i}, j={j}, ii-1={ii-1}, jj+1={jj+1}, dist={dist}") if dist <= tolerance: can_remove = True else: alpha_i = knot_removal_alpha_i(u, knotvector, t, i) - ptn = alpha_i * temp[ii + t + 1] + (1.0 - alpha_i)*temp[ii - 1] + ptn = alpha_i * temp_j[ii + t + 1] + (1.0 - alpha_i)*temp_i[ii - 1] dist = point_distance(ctrlpts[i], ptn) + print(f"F.2: first={first}, last={last}, i={i}, j={j}, ii-1={ii-1}, temp[ii+t+1]={temp_j[ii+t+1]}, temp[ii-1]={temp_i[ii-1]}, ctrlpts[i]={ctrlpts[i]}, ptn={ptn}, dist={dist}") if dist <= tolerance: can_remove = True # Check if we can remove the knot and update new control points array if can_remove: + print(f"T={t}, remove") i = first j = last while j - i > t: - ctrlpts_new[i] = temp[i - offset] - ctrlpts_new[j] = temp[j - offset] + ctrlpts_new[i] = temp_i[i - offset] + ctrlpts_new[j] = temp_j[j - offset] i += 1 j -= 1 # Update indices @@ -1039,17 +1047,17 @@ class SvNativeNurbsCurve(SvNurbsCurve): new_kv = np.copy(self.get_knotvector()) - if t > 0: + if removed_count > 0: m = N + degree + 1 for k in range(knot_span+1, m): - new_kv[k-t] = new_kv[k] - new_kv = new_kv[:m-t] + new_kv[k-removed_count] = new_kv[k] + new_kv = new_kv[:m-removed_count] #new_kv = np.delete(self.get_knotvector(), np.s_[(r-t+1):(r+1)]) # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) j = int((2*knot_span - orig_multiplicity - degree) / 2) # first control point out i = j - for k in range(1, t): + for k in range(1, removed_count): if k % 2 == 1: i += 1 else: @@ -1059,7 +1067,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): j += 1 # Slice to get the new control points - ctrlpts_new = ctrlpts_new[0:-t] + ctrlpts_new = ctrlpts_new[0:-removed_count] ctrlpts_new = np.array(ctrlpts_new) control_points, weights = from_homogenous(ctrlpts_new) -- GitLab From 0102ac1f88a34a59d8fecbf19831bc5deb06081b Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 13:20:19 +0500 Subject: [PATCH 05/21] Remove debug prints. --- utils/curve/nurbs.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 011fce9fd..8a96ae98c 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -997,13 +997,9 @@ class SvNativeNurbsCurve(SvNurbsCurve): while j - i > t: alpha_i = knot_removal_alpha_i(u, knotvector, t, i) alpha_j = knot_removal_alpha_j(u, knotvector, t, j) - print(f"A[i] = {alpha_i}, A[j] = {alpha_j}") - print(f"C[i] = {ctrlpts[i]}, C[j] = {ctrlpts[j]}, temp[ii-1] = {temp_i[ii-1]}, temp[jj+1] = {temp_j[jj+1]}") temp_i[ii] = (ctrlpts[i] - (1.0 - alpha_i)*temp_i[ii - 1]) / alpha_i - print(f"temp[ii={ii}] := {temp_i[ii]}") temp_j[jj] = (ctrlpts[j] - alpha_j*temp_j[jj + 1]) / (1.0 - alpha_j) - print(f"temp[jj={jj}] := {temp_j[jj]}") i += 1 j -= 1 @@ -1013,20 +1009,17 @@ class SvNativeNurbsCurve(SvNurbsCurve): # Check if the knot is removable if j - i < t: dist = point_distance(temp_i[ii - 1], temp_j[jj + 1]) - print(f"F.1: first={first}, last={last}, i={i}, j={j}, ii-1={ii-1}, jj+1={jj+1}, dist={dist}") if dist <= tolerance: can_remove = True else: alpha_i = knot_removal_alpha_i(u, knotvector, t, i) ptn = alpha_i * temp_j[ii + t + 1] + (1.0 - alpha_i)*temp_i[ii - 1] dist = point_distance(ctrlpts[i], ptn) - print(f"F.2: first={first}, last={last}, i={i}, j={j}, ii-1={ii-1}, temp[ii+t+1]={temp_j[ii+t+1]}, temp[ii-1]={temp_i[ii-1]}, ctrlpts[i]={ctrlpts[i]}, ptn={ptn}, dist={dist}") if dist <= tolerance: can_remove = True # Check if we can remove the knot and update new control points array if can_remove: - print(f"T={t}, remove") i = first j = last while j - i > t: -- GitLab From 676e7eeed869575853df169d45295b4462e962a1 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 14:11:44 +0500 Subject: [PATCH 06/21] Insert/remove knot nodes. --- index.md | 3 ++ nodes/curve/insert_knot.py | 75 ++++++++++++++++++++++++++++++++ nodes/curve/remove_knot.py | 87 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 nodes/curve/insert_knot.py create mode 100644 nodes/curve/remove_knot.py diff --git a/index.md b/index.md index 14d05ea4e..0b2a0f952 100644 --- a/index.md +++ b/index.md @@ -68,6 +68,9 @@ SvApproxNurbsCurveMk2Node SvExInterpolateNurbsCurveNode SvDeconstructCurveNode + --- + SvCurveInsertKnotNode + SvCurveRemoveKnotNode ## Curves @ Bezier SvBezierSplineNode diff --git a/nodes/curve/insert_knot.py b/nodes/curve/insert_knot.py new file mode 100644 index 000000000..e58377fb1 --- /dev/null +++ b/nodes/curve/insert_knot.py @@ -0,0 +1,75 @@ + +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, repeat_last_for_length +from sverchok.utils.curve import SvCurve +from sverchok.utils.curve.nurbs import SvNurbsCurve + +class SvCurveInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Insert Knot + Tooltip: Inset knot in a NURBS curve + """ + bl_idname = 'SvCurveInsertKnotNode' + bl_label = 'NURBS Curve - Insert Knot' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + knot : FloatProperty( + name = "Knot", + description = "New knot value", + default = 0.5, + update = updateNode) + + count : IntProperty( + name = "Count", + description = "Number of times to insert the knot", + default = 1, + min = 0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.inputs.new('SvStringsSocket', "Knot").prop_name = 'knot' + self.inputs.new('SvStringsSocket', "Count").prop_name = 'count' + self.outputs.new('SvCurveSocket', "Curve") + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + curve_s = self.inputs['Curve'].sv_get() + knot_s = self.inputs['Knot'].sv_get() + count_s = self.inputs['Count'].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,)) + knot_s = ensure_nesting_level(knot_s, 3) + count_s = ensure_nesting_level(count_s, 3) + + curves_out = [] + for curves, knots_i, counts_i in zip_long_repeat(curve_s, knot_s, count_s): + new_curves = [] + for curve, knots, counts in zip_long_repeat(curves, knots_i, counts_i): + curve = SvNurbsCurve.to_nurbs(curve) + if curve is None: + raise Exception("One of curves is not NURBS") + for knot, count in zip_long_repeat(knots, counts): + curve = curve.insert_knot(knot, count) + new_curves.append(curve) + if flat_output: + curves_out.extend(new_curves) + else: + curves_out.append(new_curves) + + self.outputs['Curve'].sv_set(curves_out) + +def register(): + bpy.utils.register_class(SvCurveInsertKnotNode) + +def unregister(): + bpy.utils.unregister_class(SvCurveInsertKnotNode) + diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py new file mode 100644 index 000000000..4778d1820 --- /dev/null +++ b/nodes/curve/remove_knot.py @@ -0,0 +1,87 @@ + +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, repeat_last_for_length +from sverchok.utils.curve import SvCurve +from sverchok.utils.curve.nurbs import SvNurbsCurve + +class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Remove Knot + Tooltip: Remove a knot from a NURBS curve + """ + bl_idname = 'SvCurveRemoveKnotNode' + bl_label = 'NURBS Curve - Remove Knot' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + knot : FloatProperty( + name = "Knot", + description = "Knot value", + default = 0.5, + update = updateNode) + + count : IntProperty( + name = "Count", + description = "Number of times to remove the knot", + default = 1, + min = 0, + update = updateNode) + + accuracy : IntProperty( + name = "Accuracy", + default = 6, + min=1, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.inputs.new('SvStringsSocket', "Knot").prop_name = 'knot' + self.inputs.new('SvStringsSocket', "Count").prop_name = 'count' + self.outputs.new('SvCurveSocket', "Curve") + + def draw_buttons_ext(self, context, layout): + layout.prop(self, 'accuracy') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + curve_s = self.inputs['Curve'].sv_get() + knot_s = self.inputs['Knot'].sv_get() + count_s = self.inputs['Count'].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,)) + knot_s = ensure_nesting_level(knot_s, 3) + count_s = ensure_nesting_level(count_s, 3) + + tolerance = 10**(-self.accuracy) + + curves_out = [] + for curves, knots_i, counts_i in zip_long_repeat(curve_s, knot_s, count_s): + new_curves = [] + for curve, knots, counts in zip_long_repeat(curves, knots_i, counts_i): + curve = SvNurbsCurve.to_nurbs(curve) + if curve is None: + raise Exception("One of curves is not NURBS") + for knot, count in zip_long_repeat(knots, counts): + curve = curve.remove_knot(knot, count=count, tolerance=tolerance) + new_curves.append(curve) + if flat_output: + curves_out.extend(new_curves) + else: + curves_out.append(new_curves) + + self.outputs['Curve'].sv_set(curves_out) + +def register(): + bpy.utils.register_class(SvCurveRemoveKnotNode) + +def unregister(): + bpy.utils.unregister_class(SvCurveRemoveKnotNode) + + -- GitLab From 7ea82cec0066cf524a6b49f1041df2c1e786b2b2 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 14:27:31 +0500 Subject: [PATCH 07/21] Rewrite knot removal algorithm to one-by-one. --- utils/curve/nurbs.py | 143 ++++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 63 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 8a96ae98c..901a57f82 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -937,24 +937,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): if (count is None) == (target is None): raise Exception("Either count or target must be specified") - degree = self.get_degree() - order = degree+1 - knotvector = self.get_knotvector() - ctrlpts = self.get_homogenous_control_points() - N = len(ctrlpts) - - def knot_removal_alpha_i(u, knotvector, count, idx): - return (u - knotvector[idx]) / (knotvector[idx + order + count] - knotvector[idx]) - - def knot_removal_alpha_j(u, knotvector, count, idx): - return (u - knotvector[idx - count]) / (knotvector[idx + order] - knotvector[idx - count]) - - def point_distance(p1, p2): - return np.linalg.norm(p1 - p2) - #return np.linalg.norm(np.array(p1) - np.array(p2)) - - orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) # multiplicity - knot_span = sv_knotvector.find_span(self.knotvector, N, u) + orig_multiplicity = sv_knotvector.find_multiplicity(self.get_knotvector(), u) if count == SvNurbsCurve.ALL: count = orig_multiplicity @@ -963,27 +946,47 @@ class SvNativeNurbsCurve(SvNurbsCurve): elif count is None: count = orig_multiplicity - target + degree = self.get_degree() + order = degree+1 + + if not if_possible and (count > orig_multiplicity): + raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but it's multiplicity is only {orig_multiplicity}") + # Edge case if count < 1: return self - if not if_possible and (count > orig_multiplicity): - raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but it's multiplicity is only {orig_multiplicity}") + def knot_removal_alpha_i(u, knotvector, idx): + return (u - knotvector[idx]) / (knotvector[idx + order] - knotvector[idx]) + + def knot_removal_alpha_j(u, knotvector, idx): + return (u - knotvector[idx]) / (knotvector[idx + order] - knotvector[idx]) + + def point_distance(p1, p2): + return np.linalg.norm(p1 - p2) + #return np.linalg.norm(np.array(p1) - np.array(p2)) - # Initialize variables - first = knot_span - degree - last = knot_span - orig_multiplicity + def remove_one_knot(curve): + ctrlpts = curve.get_homogenous_control_points() + N = len(ctrlpts) + knotvector = curve.get_knotvector() + orig_multiplicity = sv_knotvector.find_multiplicity(knotvector, u) + knot_span = sv_knotvector.find_span(knotvector, N, u) - # Don't change input variables, prepare new ones for updating - ctrlpts_new = deepcopy(ctrlpts) + # Initialize variables + first = knot_span - degree + last = knot_span - orig_multiplicity - # Initialize temp array for storing new control points - temp_i = np.zeros((2*degree+1, 4)) - temp_j = np.zeros((2*degree+1, 4)) + # Don't change input variables, prepare new ones for updating + ctrlpts_new = deepcopy(ctrlpts) - removed_count = 0 - # Loop for Eqs 5.28 & 5.29 - for t in range(0, count): + # Initialize temp array for storing new control points + temp_i = np.zeros((2*degree+1, 4)) + temp_j = np.zeros((2*degree+1, 4)) + + removed_count = 0 + # Loop for Eqs 5.28 & 5.29 + t = 0 offset = first - 1 # difference in index between `temp` and ctrlpts temp_i[0] = ctrlpts[offset] temp_j[last + 1 - offset] = ctrlpts[last + 1] @@ -995,8 +998,8 @@ class SvNativeNurbsCurve(SvNurbsCurve): # Compute control points for one removal step while j - i > t: - alpha_i = knot_removal_alpha_i(u, knotvector, t, i) - alpha_j = knot_removal_alpha_j(u, knotvector, t, j) + alpha_i = knot_removal_alpha_i(u, knotvector, i) + alpha_j = knot_removal_alpha_j(u, knotvector, j) temp_i[ii] = (ctrlpts[i] - (1.0 - alpha_i)*temp_i[ii - 1]) / alpha_i temp_j[jj] = (ctrlpts[j] - alpha_j*temp_j[jj + 1]) / (1.0 - alpha_j) @@ -1011,12 +1014,16 @@ class SvNativeNurbsCurve(SvNurbsCurve): dist = point_distance(temp_i[ii - 1], temp_j[jj + 1]) if dist <= tolerance: can_remove = True + else: + print(f"F.1 Dist={dist}") else: - alpha_i = knot_removal_alpha_i(u, knotvector, t, i) + alpha_i = knot_removal_alpha_i(u, knotvector, i) ptn = alpha_i * temp_j[ii + t + 1] + (1.0 - alpha_i)*temp_i[ii - 1] dist = point_distance(ctrlpts[i], ptn) if dist <= tolerance: can_remove = True + else: + print(f"F.2 T={t} i={i} j={j}, ii={ii}, jj={jj}, A={alpha_i}, temp[ii+t+1]={temp_j[ii+t+1]}, temp[ii-1]={temp_i[ii-1]}, ptn={ptn}, ctrlpts[i]={ctrlpts[i]} Dist={dist}") # Check if we can remove the knot and update new control points array if can_remove: @@ -1033,39 +1040,49 @@ class SvNativeNurbsCurve(SvNurbsCurve): removed_count += 1 else: + raise CantRemoveKnotException() + + new_kv = np.copy(curve.get_knotvector()) + + if removed_count > 0: + m = N + degree + 1 + for k in range(knot_span+1, m): + new_kv[k-removed_count] = new_kv[k] + new_kv = new_kv[:m-removed_count] + #new_kv = np.delete(curve.get_knotvector(), np.s_[(r-t+1):(r+1)]) + + # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) + j = int((2*knot_span - orig_multiplicity - degree) / 2) # first control point out + i = j + for k in range(1, removed_count): + if k % 2 == 1: + i += 1 + else: + j -= 1 + for k in range(i+1, N): + ctrlpts_new[j] = ctrlpts_new[k] + j += 1 + + # Slice to get the new control points + ctrlpts_new = ctrlpts_new[0:-removed_count] + + ctrlpts_new = np.array(ctrlpts_new) + control_points, weights = from_homogenous(ctrlpts_new) + + return curve.copy(knotvector = new_kv, control_points = control_points, weights = weights) + + curve = self + removed_count = 0 + for i in range(count): + try: + curve = remove_one_knot(curve) + removed_count += 1 + except CantRemoveKnotException as e: break if not if_possible and (removed_count < count): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") - - new_kv = np.copy(self.get_knotvector()) - - if removed_count > 0: - m = N + degree + 1 - for k in range(knot_span+1, m): - new_kv[k-removed_count] = new_kv[k] - new_kv = new_kv[:m-removed_count] - #new_kv = np.delete(self.get_knotvector(), np.s_[(r-t+1):(r+1)]) - - # Shift control points (refer to p.183 of The NURBS Book, 2nd Edition) - j = int((2*knot_span - orig_multiplicity - degree) / 2) # first control point out - i = j - for k in range(1, removed_count): - if k % 2 == 1: - i += 1 - else: - j -= 1 - for k in range(i+1, N): - ctrlpts_new[j] = ctrlpts_new[k] - j += 1 - - # Slice to get the new control points - ctrlpts_new = ctrlpts_new[0:-removed_count] - - ctrlpts_new = np.array(ctrlpts_new) - control_points, weights = from_homogenous(ctrlpts_new) - - return self.copy(knotvector = new_kv, control_points = control_points, weights = weights) + return curve SvNurbsMaths.curve_classes[SvNurbsMaths.NATIVE] = SvNativeNurbsCurve -- GitLab From 934d878c8d9aed14accd28a29496b18ed54d22b3 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 19:22:14 +0500 Subject: [PATCH 08/21] "remove excessive knots" node. --- index.md | 1 + nodes/curve/remove_excessive_knots.py | 67 +++++++++++++++++++++++++++ nodes/curve/remove_knot.py | 1 - utils/curve/nurbs_algorithms.py | 6 +++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 nodes/curve/remove_excessive_knots.py diff --git a/index.md b/index.md index 0b2a0f952..0c8ff3381 100644 --- a/index.md +++ b/index.md @@ -71,6 +71,7 @@ --- SvCurveInsertKnotNode SvCurveRemoveKnotNode + SvCurveRemoveExcessiveKnotsNode ## Curves @ Bezier SvBezierSplineNode diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py new file mode 100644 index 000000000..ab75a9973 --- /dev/null +++ b/nodes/curve/remove_excessive_knots.py @@ -0,0 +1,67 @@ + +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, repeat_last_for_length +from sverchok.utils.curve import SvCurve +from sverchok.utils.curve.nurbs import SvNurbsCurve +from sverchok.utils.curve.nurbs_algorithms import remove_excessive_knots + +class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Remove Excessive Knots + Tooltip: Remove all excessive knots from a NURBS curve + """ + bl_idname = 'SvCurveRemoveExcessiveKnotsNode' + bl_label = 'NURBS Curve - Remove Excessive Knots' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + accuracy : IntProperty( + name = "Accuracy", + default = 6, + min=1, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvCurveSocket', "Curve") + self.outputs.new('SvCurveSocket', "Curve") + + def draw_buttons_ext(self, context, layout): + layout.prop(self, 'accuracy') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + curve_s = self.inputs['Curve'].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,)) + + tolerance = 10**(-self.accuracy) + + curves_out = [] + for curves in curve_s: + new_curves = [] + for curve in curves: + curve = SvNurbsCurve.to_nurbs(curve) + if curve is None: + raise Exception("One of curves is not NURBS") + curve = remove_excessive_knots(curve, tolerance=tolerance) + new_curves.append(curve) + if flat_output: + curves_out.extend(new_curves) + else: + curves_out.append(new_curves) + + self.outputs['Curve'].sv_set(curves_out) + +def register(): + bpy.utils.register_class(SvCurveRemoveExcessiveKnotsNode) + +def unregister(): + bpy.utils.unregister_class(SvCurveRemoveExcessiveKnotsNode) + diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 4778d1820..71c609bf3 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -84,4 +84,3 @@ def register(): def unregister(): bpy.utils.unregister_class(SvCurveRemoveKnotNode) - diff --git a/utils/curve/nurbs_algorithms.py b/utils/curve/nurbs_algorithms.py index 1d73d5b58..40985c54f 100644 --- a/utils/curve/nurbs_algorithms.py +++ b/utils/curve/nurbs_algorithms.py @@ -396,3 +396,9 @@ def intersect_nurbs_curves(curve1, curve2, method='SLSQP', numeric_precision=0.0 return _intersect(curve1, curve2, curve1.get_u_bounds(), curve2.get_u_bounds()) +def remove_excessive_knots(curve, tolerance=1e-6): + kv = curve.get_knotvector() + for u in sv_knotvector.get_internal_knots(kv): + curve = curve.remove_knot(u, count='ALL', if_possible=True, tolerance=tolerance) + return curve + -- GitLab From e640d4ab5acf5f7d56879eab5a7750bc9663ba3b Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sat, 17 Jul 2021 20:46:16 +0500 Subject: [PATCH 09/21] "if_possible" flag. --- nodes/curve/remove_knot.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 71c609bf3..3b82de936 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -30,6 +30,12 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): min = 0, update = updateNode) + if_possible: BoolProperty( + name = "Only if possible", + description = "Don't fail when trying to remove the knot too many times, just remove it as many times as possible", + default = False, + update = updateNode) + accuracy : IntProperty( name = "Accuracy", default = 6, @@ -42,7 +48,11 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): self.inputs.new('SvStringsSocket', "Count").prop_name = 'count' self.outputs.new('SvCurveSocket', "Curve") + def draw_buttons(self, context, layout): + layout.prop(self, 'if_possible') + def draw_buttons_ext(self, context, layout): + self.draw_buttons(context, layout) layout.prop(self, 'accuracy') def process(self): @@ -69,7 +79,7 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): if curve is None: raise Exception("One of curves is not NURBS") for knot, count in zip_long_repeat(knots, counts): - curve = curve.remove_knot(knot, count=count, tolerance=tolerance) + curve = curve.remove_knot(knot, count=count, tolerance=tolerance, if_possible=self.if_possible) new_curves.append(curve) if flat_output: curves_out.extend(new_curves) -- GitLab From 51a214adff6adab9da12f70f441f3ac2292a7608 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 09:56:22 +0500 Subject: [PATCH 10/21] Change tolerance parameter. --- nodes/curve/remove_excessive_knots.py | 13 +++++++------ nodes/curve/remove_knot.py | 13 +++++++------ utils/curve/nurbs.py | 6 ++---- utils/nurbs_common.py | 3 +++ utils/surface/freecad.py | 2 +- utils/surface/nurbs.py | 26 +++++++++++++++++++++----- 6 files changed, 41 insertions(+), 22 deletions(-) diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py index ab75a9973..43a75d453 100644 --- a/nodes/curve/remove_excessive_knots.py +++ b/nodes/curve/remove_excessive_knots.py @@ -18,10 +18,11 @@ class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_FLIP_CURVE' - accuracy : IntProperty( - name = "Accuracy", - default = 6, - min=1, + tolerance : FloatProperty( + name = "Tolerance", + default = 1e-6, + precision = 8, + min = 0, update = updateNode) def sv_init(self, context): @@ -29,7 +30,7 @@ class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): self.outputs.new('SvCurveSocket', "Curve") def draw_buttons_ext(self, context, layout): - layout.prop(self, 'accuracy') + layout.prop(self, 'tolerance') def process(self): if not any(socket.is_linked for socket in self.outputs): @@ -41,7 +42,7 @@ class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): flat_output = input_level < 2 curve_s = ensure_nesting_level(curve_s, 2, data_types=(SvCurve,)) - tolerance = 10**(-self.accuracy) + tolerance = self.tolerance curves_out = [] for curves in curve_s: diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 3b82de936..7e77225ee 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -36,10 +36,11 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): default = False, update = updateNode) - accuracy : IntProperty( - name = "Accuracy", - default = 6, - min=1, + tolerance : FloatProperty( + name = "Tolerance", + default = 1e-6, + precision = 8, + min = 0, update = updateNode) def sv_init(self, context): @@ -53,7 +54,7 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): def draw_buttons_ext(self, context, layout): self.draw_buttons(context, layout) - layout.prop(self, 'accuracy') + layout.prop(self, 'tolerance') def process(self): if not any(socket.is_linked for socket in self.outputs): @@ -69,7 +70,7 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): knot_s = ensure_nesting_level(knot_s, 3) count_s = ensure_nesting_level(count_s, 3) - tolerance = 10**(-self.accuracy) + tolerance = self.tolerance curves_out = [] for curves, knots_i, counts_i in zip_long_repeat(curve_s, knot_s, count_s): diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 901a57f82..3a9a4653e 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -18,7 +18,8 @@ from sverchok.utils.curve.algorithms import unify_curves_degree from sverchok.utils.curve.nurbs_algorithms import interpolate_nurbs_curve, unify_two_curves, unify_curves from sverchok.utils.nurbs_common import ( SvNurbsMaths,SvNurbsBasisFunctions, - nurbs_divide, elevate_bezier_degree, from_homogenous + nurbs_divide, elevate_bezier_degree, from_homogenous, + CantRemoveKnotException ) from sverchok.utils.surface.nurbs import SvNativeNurbsSurface, SvGeomdlSurface from sverchok.utils.surface.algorithms import nurbs_revolution_surface @@ -28,9 +29,6 @@ from sverchok.dependencies import geomdl if geomdl is not None: from geomdl import NURBS, BSpline, operations, fitting -class CantRemoveKnotException(Exception): - pass - ################## # # # Curves # diff --git a/utils/nurbs_common.py b/utils/nurbs_common.py index d51fa7e57..53d2bd3b3 100644 --- a/utils/nurbs_common.py +++ b/utils/nurbs_common.py @@ -216,3 +216,6 @@ class SvNurbsBasisFunctions(object): return calc +class CantRemoveKnotException(Exception): + pass + diff --git a/utils/surface/freecad.py b/utils/surface/freecad.py index 1f7564562..eacdc4a6a 100644 --- a/utils/surface/freecad.py +++ b/utils/surface/freecad.py @@ -311,7 +311,7 @@ class SvFreeCadNurbsSurface(SvNurbsSurface): surface.surface.insertVKnot(parameter, count, tolerance) return surface - def remove_knot(self, direction, parameter, count=1, tolerance=1e-4): + def remove_knot(self, direction, parameter, count=1, if_possible=False, tolerance=1e-6): surface = SvFreeCadNurbsSurface(self.surface.copy()) if direction == 'U': ms = sv_knotvector.to_multiplicity(self.get_knotvector_u()) diff --git a/utils/surface/nurbs.py b/utils/surface/nurbs.py index a348ee166..9aaa12f82 100644 --- a/utils/surface/nurbs.py +++ b/utils/surface/nurbs.py @@ -5,7 +5,8 @@ from collections import defaultdict from sverchok.utils.geom import Spline from sverchok.utils.nurbs_common import ( SvNurbsMaths, SvNurbsBasisFunctions, - nurbs_divide, from_homogenous + nurbs_divide, from_homogenous, + CantRemoveKnotException ) from sverchok.utils.curve import knotvector as sv_knotvector from sverchok.utils.curve.nurbs_algorithms import interpolate_nurbs_curve, unify_curves, nurbs_curve_to_xoy, nurbs_curve_matrix @@ -269,14 +270,29 @@ class SvGeomdlSurface(SvNurbsSurface): surface = operations.insert_knot(self.surface, uv, counts) return SvGeomdlSurface(surface) - def remove_knot(self, direction, parameter, count=1): + def remove_knot(self, direction, parameter, count=1, if_possible=False, tolerance=None): if direction == SvNurbsSurface.U: + orig_kv = self.get_knotvector_u() uv = [parameter, None] counts = [count, 0] elif direction == SvNurbsSurface.V: + orig_kv = self.get_knotvector_v() uv = [None, parameter] counts = [0, count] + orig_multiplicity = sv_knotvector.find_multiplicity(orig_kv, parameter) + surface = operations.remove_knot(self.surface, uv, counts) + + if direction == SvNurbsSurface.U: + new_kv = self.get_knotvector_u() + elif direction == SvNurbsSurface.V: + new_kv = self.get_knotvector_v() + + new_multiplicity = sv_knotvector.find_multiplicity(new_kv, parameter) + + if not if_possible and (orig_multiplicity - new_multiplicity < count): + raise CantRemoveKnotException(f"Asked to remove knot {direction}={parameter} {count} times, but could remove it only {orig_multiplicity-new_multiplicity} times") + return SvGeomdlSurface(surface) def get_degree_u(self): @@ -566,7 +582,7 @@ class SvNativeNurbsSurface(SvNurbsSurface): else: raise Exception("Unsupported direction") - def remove_knot(self, direction, parameter, count=1): + def remove_knot(self, direction, parameter, count=1, if_possible=False, tolerance=1e-6): if direction == SvNurbsSurface.U: new_points = [] new_weights = [] @@ -577,7 +593,7 @@ class SvNativeNurbsSurface(SvNurbsSurface): fixed_v_curve = SvNurbsMaths.build_curve(SvNurbsMaths.NATIVE, self.degree_u, self.knotvector_u, fixed_v_points, fixed_v_weights) - fixed_v_curve = fixed_v_curve.remove_knot(parameter, count) + fixed_v_curve = fixed_v_curve.remove_knot(parameter, count, if_possible=if_possible, tolerance=tolerance) fixed_v_knotvector = fixed_v_curve.get_knotvector() new_u_degree = fixed_v_curve.get_degree() fixed_v_points = fixed_v_curve.get_control_points() @@ -602,7 +618,7 @@ class SvNativeNurbsSurface(SvNurbsSurface): fixed_u_curve = SvNurbsMaths.build_curve(SvNurbsMaths.NATIVE, self.degree_v, self.knotvector_v, fixed_u_points, fixed_u_weights) - fixed_u_curve = fixed_u_curve.remove_knot(parameter, count) + fixed_u_curve = fixed_u_curve.remove_knot(parameter, count, if_possible=if_possible, tolerance=tolerance) fixed_u_knotvector = fixed_u_curve.get_knotvector() new_v_degree = fixed_u_curve.get_degree() fixed_u_points = fixed_u_curve.get_control_points() -- GitLab From ce84e6e50567b0843d4e65f4638fd2c1dfc715ad Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 10:52:39 +0500 Subject: [PATCH 11/21] Insert/remove knot nodes for surfaces. --- index.md | 3 + nodes/surface/insert_knot.py | 90 ++++++++++++++++++++++++++++ nodes/surface/remove_knot.py | 111 +++++++++++++++++++++++++++++++++++ utils/surface/nurbs.py | 2 +- 4 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 nodes/surface/insert_knot.py create mode 100644 nodes/surface/remove_knot.py diff --git a/index.md b/index.md index 0c8ff3381..3210f04f1 100644 --- a/index.md +++ b/index.md @@ -141,6 +141,9 @@ SvDeconstructSurfaceNode --- SvExQuadsToNurbsNode + --- + SvSurfaceInsertKnotNode + SvSurfaceRemoveKnotNode ## Surfaces SvExPlaneSurfaceNode diff --git a/nodes/surface/insert_knot.py b/nodes/surface/insert_knot.py new file mode 100644 index 000000000..8b33d6344 --- /dev/null +++ b/nodes/surface/insert_knot.py @@ -0,0 +1,90 @@ + +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, repeat_last_for_length +from sverchok.utils.surface import SvSurface +from sverchok.utils.surface.nurbs import SvNurbsSurface + +class SvSurfaceInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Insert Knot + Tooltip: Insert a knot in a NURBS surface + """ + bl_idname = 'SvSurfaceInsertKnotNode' + bl_label = 'NURBS Surface - Insert Knot' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + directions = [ + ('U', "U", "U direction", 0), + ('V', "V", "V direction", 1) + ] + + direction : EnumProperty( + name = "Parameter", + description = "From which parameter direction to remove the knot", + items = directions, + default = 'U', + update = updateNode) + + knot : FloatProperty( + name = "Knot", + description = "New knot value", + default = 0.5, + update = updateNode) + + count : IntProperty( + name = "Count", + description = "Number of times to insert the knot", + default = 1, + min = 0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.inputs.new('SvStringsSocket', "Knot").prop_name = 'knot' + self.inputs.new('SvStringsSocket', "Count").prop_name = 'count' + self.outputs.new('SvSurfaceSocket', "Surface") + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + knot_s = self.inputs['Knot'].sv_get() + count_s = self.inputs['Count'].sv_get() + + input_level = get_data_nesting_level(surface_s, data_types=(SvSurface,)) + flat_output = input_level < 2 + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + knot_s = ensure_nesting_level(knot_s, 3) + count_s = ensure_nesting_level(count_s, 3) + + surfaces_out = [] + for surfaces, knots_i, counts_i in zip_long_repeat(surface_s, knot_s, count_s): + new_surfaces = [] + for surface, knots, counts in zip_long_repeat(surfaces, knots_i, counts_i): + surface = SvNurbsSurface.get(surface) + if surface is None: + raise Exception("One of surfaces is not NURBS") + for knot, count in zip_long_repeat(knots, counts): + surface = surface.insert_knot(self.direction, knot, count) + new_surfaces.append(surface) + if flat_output: + surfaces_out.extend(new_surfaces) + else: + surfaces_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surfaces_out) + +def register(): + bpy.utils.register_class(SvSurfaceInsertKnotNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceInsertKnotNode) + diff --git a/nodes/surface/remove_knot.py b/nodes/surface/remove_knot.py new file mode 100644 index 000000000..9bff875a9 --- /dev/null +++ b/nodes/surface/remove_knot.py @@ -0,0 +1,111 @@ + +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, repeat_last_for_length +from sverchok.utils.surface import SvSurface +from sverchok.utils.surface.nurbs import SvNurbsSurface + +class SvSurfaceRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Remove Knot + Tooltip: Remove a knot from a NURBS surface + """ + bl_idname = 'SvSurfaceRemoveKnotNode' + bl_label = 'NURBS Surface - Remove Knot' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + directions = [ + ('U', "U", "U direction", 0), + ('V', "V", "V direction", 1) + ] + + direction : EnumProperty( + name = "Parameter", + description = "From which parameter direction to remove the knot", + items = directions, + default = 'U', + update = updateNode) + + knot : FloatProperty( + name = "Knot", + description = "Knot value", + default = 0.5, + update = updateNode) + + count : IntProperty( + name = "Count", + description = "Number of times to remove the knot", + default = 1, + min = 0, + update = updateNode) + + if_possible: BoolProperty( + name = "Only if possible", + description = "Don't fail when trying to remove the knot too many times, just remove it as many times as possible", + default = False, + update = updateNode) + + tolerance : FloatProperty( + name = "Tolerance", + default = 1e-6, + precision = 8, + min = 0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.inputs.new('SvStringsSocket', "Knot").prop_name = 'knot' + self.inputs.new('SvStringsSocket', "Count").prop_name = 'count' + self.outputs.new('SvSurfaceSocket', "Surface") + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + layout.prop(self, 'if_possible') + + def draw_buttons_ext(self, context, layout): + self.draw_buttons(context, layout) + layout.prop(self, 'tolerance') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + knot_s = self.inputs['Knot'].sv_get() + count_s = self.inputs['Count'].sv_get() + + input_level = get_data_nesting_level(surface_s, data_types=(SvSurface,)) + flat_output = input_level < 2 + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + knot_s = ensure_nesting_level(knot_s, 3) + count_s = ensure_nesting_level(count_s, 3) + + tolerance = self.tolerance + + surfaces_out = [] + for surfaces, knots_i, counts_i in zip_long_repeat(surface_s, knot_s, count_s): + new_surfaces = [] + for surface, knots, counts in zip_long_repeat(surfaces, knots_i, counts_i): + surface = SvNurbsSurface.get(surface) + if surface is None: + raise Exception("One of surfaces is not NURBS") + for knot, count in zip_long_repeat(knots, counts): + surface = surface.remove_knot(self.direction, knot, count=count, tolerance=tolerance, if_possible=self.if_possible) + new_surfaces.append(surface) + if flat_output: + surfaces_out.extend(new_surfaces) + else: + surfaces_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surfaces_out) + +def register(): + bpy.utils.register_class(SvSurfaceRemoveKnotNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceRemoveKnotNode) + + diff --git a/utils/surface/nurbs.py b/utils/surface/nurbs.py index 9aaa12f82..a645d24e0 100644 --- a/utils/surface/nurbs.py +++ b/utils/surface/nurbs.py @@ -84,7 +84,7 @@ class SvNurbsSurface(SvSurface): def insert_knot(self, direction, parameter, count=1): raise Exception("Not implemented!") - def remove_knot(self, direction, parameter, count=1): + def remove_knot(self, direction, parameter, count=1, tolerance=None, if_possible=False): raise Exception("Not implemented!") def swap_uv(self): -- GitLab From 4a83c00acdfea0674f1274057526922dd6b1143b Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 12:11:53 +0500 Subject: [PATCH 12/21] "remove excessive knots" node for surfaces. --- index.md | 1 + nodes/surface/remove_excessive_knots.py | 87 +++++++++++++++++++++++++ utils/curve/nurbs.py | 1 + utils/surface/algorithms.py | 16 +++++ utils/surface/nurbs.py | 35 +++++++++- 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 nodes/surface/remove_excessive_knots.py diff --git a/index.md b/index.md index 3210f04f1..5575e4c47 100644 --- a/index.md +++ b/index.md @@ -144,6 +144,7 @@ --- SvSurfaceInsertKnotNode SvSurfaceRemoveKnotNode + SvSurfaceRemoveExcessiveKnotsNode ## Surfaces SvExPlaneSurfaceNode diff --git a/nodes/surface/remove_excessive_knots.py b/nodes/surface/remove_excessive_knots.py new file mode 100644 index 000000000..1394f6bf2 --- /dev/null +++ b/nodes/surface/remove_excessive_knots.py @@ -0,0 +1,87 @@ + +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, repeat_last_for_length +from sverchok.utils.surface import SvSurface +from sverchok.utils.surface.nurbs import SvNurbsSurface +from sverchok.utils.surface.algorithms import remove_excessive_knots + +class SvSurfaceRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): + """ + Triggers: Remove Excessive Knots + Tooltip: Remove a knot from a NURBS surface + """ + bl_idname = 'SvSurfaceRemoveExcessiveKnotsNode' + bl_label = 'NURBS Surface - Remove Excessive Knots' + bl_icon = 'OUTLINER_OB_EMPTY' + sv_icon = 'SV_FLIP_CURVE' + + directions = [ + ('UV', "U+V", "Both U and V directions", 0), + ('U', "U", "U direction", 1), + ('V', "V", "V direction", 2) + ] + + direction : EnumProperty( + name = "Parameter", + description = "From which parameter direction to remove knots", + items = directions, + default = 'UV', + update = updateNode) + + tolerance : FloatProperty( + name = "Tolerance", + default = 1e-6, + precision = 8, + min = 0, + update = updateNode) + + def sv_init(self, context): + self.inputs.new('SvSurfaceSocket', "Surface") + self.outputs.new('SvSurfaceSocket', "Surface") + + def draw_buttons(self, context, layout): + layout.prop(self, 'direction', expand=True) + + def draw_buttons_ext(self, context, layout): + self.draw_buttons(context, layout) + layout.prop(self, 'tolerance') + + def process(self): + if not any(socket.is_linked for socket in self.outputs): + return + + surface_s = self.inputs['Surface'].sv_get() + + input_level = get_data_nesting_level(surface_s, data_types=(SvSurface,)) + flat_output = input_level < 2 + surface_s = ensure_nesting_level(surface_s, 2, data_types=(SvSurface,)) + + tolerance = self.tolerance + + surfaces_out = [] + for surfaces in surface_s: + new_surfaces = [] + for surface in surfaces: + surface = SvNurbsSurface.get(surface) + if surface is None: + raise Exception("One of surfaces is not NURBS") + surface = remove_excessive_knots(surface, self.direction, tolerance=tolerance) + new_surfaces.append(surface) + if flat_output: + surfaces_out.extend(new_surfaces) + else: + surfaces_out.append(new_surfaces) + + self.outputs['Surface'].sv_set(surfaces_out) + +def register(): + bpy.utils.register_class(SvSurfaceRemoveExcessiveKnotsNode) + +def unregister(): + bpy.utils.unregister_class(SvSurfaceRemoveExcessiveKnotsNode) + + + diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 3a9a4653e..691691ac3 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -1080,6 +1080,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): if not if_possible and (removed_count < count): raise CantRemoveKnotException(f"Asked to remove knot t={u} for {count} times, but could remove it only {removed_count} times") + #print(f"Removed knot t={u} for {removed_count} times") return curve diff --git a/utils/surface/algorithms.py b/utils/surface/algorithms.py index 6162163e1..419827706 100644 --- a/utils/surface/algorithms.py +++ b/utils/surface/algorithms.py @@ -1225,3 +1225,19 @@ def unify_nurbs_surfaces(surfaces, knots_method = 'UNIFY', knotvector_accuracy=6 else: raise Exception('Unsupported knotvector unification method') +def remove_excessive_knots(surface, direction, tolerance=1e-6): + if direction not in {'U', 'V', 'UV'}: + raise Exception("Unsupported direction") + + if direction in {'U', 'UV'}: + kv = surface.get_knotvector_u() + for u in sv_knotvector.get_internal_knots(kv): + surface = surface.remove_knot('U', u, count='ALL', tolerance=tolerance, if_possible=True) + + if direction in {'V', 'UV'}: + kv = surface.get_knotvector_v() + for v in sv_knotvector.get_internal_knots(kv): + surface = surface.remove_knot('V', v, count='ALL', tolerance=tolerance, if_possible=True) + + return surface + diff --git a/utils/surface/nurbs.py b/utils/surface/nurbs.py index a645d24e0..2935fbc1d 100644 --- a/utils/surface/nurbs.py +++ b/utils/surface/nurbs.py @@ -583,17 +583,42 @@ class SvNativeNurbsSurface(SvNurbsSurface): raise Exception("Unsupported direction") def remove_knot(self, direction, parameter, count=1, if_possible=False, tolerance=1e-6): + def get_common_count(curves): + if not if_possible: + # in this case the first curve.remove_knot() call which can't remove the knot + # requested number of times will raise an exception, so we do not have to bother + return count + else: + # curve.remove_knot() calls will not raise exceptions, so we have to + # select the minimum number of possible knot removals among all curves + min_count = curves[0].get_degree()+1 + for curve in curves: + orig_kv = curve.get_knotvector() + orig_multiplicity = sv_knotvector.find_multiplicity(orig_kv, parameter) + tmp = curve.remove_knot(parameter, count, if_possible=True, tolerance=tolerance) + new_kv = tmp.get_knotvector() + new_multiplicity = sv_knotvector.find_multiplicity(new_kv, parameter) + delta = orig_multiplicity - new_multiplicity + min_count = min(min_count, delta) + return min_count + if direction == SvNurbsSurface.U: new_points = [] new_weights = [] new_u_degree = None + fixed_v_curves = [] for i in range(self.get_control_points().shape[1]): fixed_v_points = self.get_control_points()[:,i] fixed_v_weights = self.get_weights()[:,i] fixed_v_curve = SvNurbsMaths.build_curve(SvNurbsMaths.NATIVE, self.degree_u, self.knotvector_u, fixed_v_points, fixed_v_weights) - fixed_v_curve = fixed_v_curve.remove_knot(parameter, count, if_possible=if_possible, tolerance=tolerance) + fixed_v_curves.append(fixed_v_curve) + + common_count = get_common_count(fixed_v_curves) + + for fixed_v_curve in fixed_v_curves: + fixed_v_curve = fixed_v_curve.remove_knot(parameter, common_count, if_possible=if_possible, tolerance=tolerance) fixed_v_knotvector = fixed_v_curve.get_knotvector() new_u_degree = fixed_v_curve.get_degree() fixed_v_points = fixed_v_curve.get_control_points() @@ -612,13 +637,19 @@ class SvNativeNurbsSurface(SvNurbsSurface): new_points = [] new_weights = [] new_v_degree = None + fixed_u_curves = [] for i in range(self.get_control_points().shape[0]): fixed_u_points = self.get_control_points()[i,:] fixed_u_weights = self.get_weights()[i,:] fixed_u_curve = SvNurbsMaths.build_curve(SvNurbsMaths.NATIVE, self.degree_v, self.knotvector_v, fixed_u_points, fixed_u_weights) - fixed_u_curve = fixed_u_curve.remove_knot(parameter, count, if_possible=if_possible, tolerance=tolerance) + fixed_u_curves.append(fixed_u_curve) + + common_count = get_common_count(fixed_u_curves) + + for fixed_u_curve in fixed_u_curves: + fixed_u_curve = fixed_u_curve.remove_knot(parameter, common_count, if_possible=if_possible, tolerance=tolerance) fixed_u_knotvector = fixed_u_curve.get_knotvector() new_v_degree = fixed_u_curve.get_degree() fixed_u_points = fixed_u_curve.get_control_points() -- GitLab From 55978693fc4793e9c64eade01227e72117eaee9c Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 14:32:30 +0500 Subject: [PATCH 13/21] curve intersection bugfix. --- utils/curve/nurbs.py | 26 +++++++++++++++++--------- utils/curve/nurbs_algorithms.py | 3 +++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 691691ac3..2c8bbe2f3 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -420,15 +420,23 @@ class SvNurbsCurve(SvCurve): degree = self.get_degree() implementation = self.get_nurbs_implementation() - knotvector1, control_points_1, weights_1 = c1 - knotvector2, control_points_2, weights_2 = c2 - - curve1 = SvNurbsCurve.build(implementation, - degree, knotvector1, - control_points_1, weights_1) - curve2 = SvNurbsCurve.build(implementation, - degree, knotvector2, - control_points_2, weights_2) + if c1 is not None: + knotvector1, control_points_1, weights_1 = c1 + curve1 = SvNurbsCurve.build(implementation, + degree, knotvector1, + control_points_1, weights_1) + else: + curve1 = None + + if c2 is not None: + knotvector2, control_points_2, weights_2 = c2 + + curve2 = SvNurbsCurve.build(implementation, + degree, knotvector2, + control_points_2, weights_2) + else: + curve2 = None + return curve1, curve2 def cut_segment(self, new_t_min, new_t_max, rescale=False): diff --git a/utils/curve/nurbs_algorithms.py b/utils/curve/nurbs_algorithms.py index 40985c54f..b9aff1fb9 100644 --- a/utils/curve/nurbs_algorithms.py +++ b/utils/curve/nurbs_algorithms.py @@ -365,6 +365,9 @@ def intersect_nurbs_curves(curve1, curve2, method='SLSQP', numeric_precision=0.0 # control points. def _intersect(curve1, curve2, c1_bounds, c2_bounds): + if curve1 is None or curve2 is None: + return [] + t1_min, t1_max = c1_bounds t2_min, t2_max = c2_bounds -- GitLab From df887ae145398860319fc2a5597049d949e46c8f Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 14:34:10 +0500 Subject: [PATCH 14/21] Add license blocks. --- nodes/curve/insert_knot.py | 6 ++++++ nodes/curve/remove_excessive_knots.py | 6 ++++++ nodes/curve/remove_knot.py | 6 ++++++ nodes/surface/insert_knot.py | 6 ++++++ nodes/surface/remove_excessive_knots.py | 6 ++++++ nodes/surface/remove_knot.py | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/nodes/curve/insert_knot.py b/nodes/curve/insert_knot.py index e58377fb1..f995710a9 100644 --- a/nodes/curve/insert_knot.py +++ b/nodes/curve/insert_knot.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py index 43a75d453..d8e6306f6 100644 --- a/nodes/curve/remove_excessive_knots.py +++ b/nodes/curve/remove_excessive_knots.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 7e77225ee..43cbcecbd 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty diff --git a/nodes/surface/insert_knot.py b/nodes/surface/insert_knot.py index 8b33d6344..94c649b4d 100644 --- a/nodes/surface/insert_knot.py +++ b/nodes/surface/insert_knot.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty diff --git a/nodes/surface/remove_excessive_knots.py b/nodes/surface/remove_excessive_knots.py index 1394f6bf2..cdfb74a43 100644 --- a/nodes/surface/remove_excessive_knots.py +++ b/nodes/surface/remove_excessive_knots.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty diff --git a/nodes/surface/remove_knot.py b/nodes/surface/remove_knot.py index 9bff875a9..dc7bb0203 100644 --- a/nodes/surface/remove_knot.py +++ b/nodes/surface/remove_knot.py @@ -1,3 +1,9 @@ +# 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 bpy from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty -- GitLab From 8b2d9b96ebfb277b18a0b5d409008595af414bf7 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 14:41:27 +0500 Subject: [PATCH 15/21] Icons. --- nodes/surface/insert_knot.py | 2 +- nodes/surface/remove_excessive_knots.py | 2 +- nodes/surface/remove_knot.py | 2 +- ui/icons/sv_surface_clean_knots.png | Bin 0 -> 2825 bytes ui/icons/sv_surface_insert_knot.png | Bin 0 -> 2786 bytes ui/icons/sv_surface_remove_knot.png | Bin 0 -> 2438 bytes ui/icons/svg/sv_surface_clean_knots.svg | 216 +++++++++++++++++++++++ ui/icons/svg/sv_surface_insert_knot.svg | 223 ++++++++++++++++++++++++ ui/icons/svg/sv_surface_remove_knot.svg | 215 +++++++++++++++++++++++ 9 files changed, 657 insertions(+), 3 deletions(-) create mode 100644 ui/icons/sv_surface_clean_knots.png create mode 100644 ui/icons/sv_surface_insert_knot.png create mode 100644 ui/icons/sv_surface_remove_knot.png create mode 100644 ui/icons/svg/sv_surface_clean_knots.svg create mode 100644 ui/icons/svg/sv_surface_insert_knot.svg create mode 100644 ui/icons/svg/sv_surface_remove_knot.svg diff --git a/nodes/surface/insert_knot.py b/nodes/surface/insert_knot.py index 94c649b4d..a41a78c84 100644 --- a/nodes/surface/insert_knot.py +++ b/nodes/surface/insert_knot.py @@ -21,7 +21,7 @@ class SvSurfaceInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvSurfaceInsertKnotNode' bl_label = 'NURBS Surface - Insert Knot' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_SURFACE_INSERT_KNOT' directions = [ ('U', "U", "U direction", 0), diff --git a/nodes/surface/remove_excessive_knots.py b/nodes/surface/remove_excessive_knots.py index cdfb74a43..8f15e1816 100644 --- a/nodes/surface/remove_excessive_knots.py +++ b/nodes/surface/remove_excessive_knots.py @@ -22,7 +22,7 @@ class SvSurfaceRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvSurfaceRemoveExcessiveKnotsNode' bl_label = 'NURBS Surface - Remove Excessive Knots' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_SURFACE_CLEAN_KNOTS' directions = [ ('UV', "U+V", "Both U and V directions", 0), diff --git a/nodes/surface/remove_knot.py b/nodes/surface/remove_knot.py index dc7bb0203..b2f35c15a 100644 --- a/nodes/surface/remove_knot.py +++ b/nodes/surface/remove_knot.py @@ -21,7 +21,7 @@ class SvSurfaceRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvSurfaceRemoveKnotNode' bl_label = 'NURBS Surface - Remove Knot' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_SURFACE_REMOVE_KNOT' directions = [ ('U', "U", "U direction", 0), diff --git a/ui/icons/sv_surface_clean_knots.png b/ui/icons/sv_surface_clean_knots.png new file mode 100644 index 0000000000000000000000000000000000000000..448ee870bfa30ac8e8955941a3e24f7df667f491 GIT binary patch literal 2825 zcmV+k3-pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H13YJMk zK~#90?VEdWQ&k?vKS|oAr7e%9vCtP#9+pRoZlTtehd5f6S8>omv^ZN|=n8e55gc?K zaB$Tf!Bv+cv#{D3L|l;~AkK)mNFmf|?ONBCSON*`&HCO!Md?IZUl4M}Jl zcir#Inan-+{C@ZM``&ZUIltdIfnPXOZt7-rmr+h73WxyIz=-a4BfEc_oMbJ4*{S>l zRCgC6&@!;*4-Vb~#2}Rmzz4w7KqjCW8kc;4Mx!Za@CZ zwrvx+xw#@bI@+6FaT1t4^zQfJX54%dn>KConE)m1cDp!r>XcZwZk>pUiSa~27qAzI z4+H@KI^b((qqJJBICt*cb(7Eoo6RPQii*VTx8LrGh*n@7(o{U;7!A}o8~@); zCOjN%R1{jBj?kDGBF2v=c*F>$ez!20OsrnLn!|?=ySr@wmIDnw`U3b)Krm1n3i>C2 z8C>65R;$-DeDY*;GiRbpPe-4bN%**N{kAVGEF?capSHF(<*k1L*+}D*Pwo+F8lZIS2nK%<^av!==O(zIGh-cjg6(Yw$^7QqN@u_O$}GhoT0I_l!lTL8cIs& z@(?d*!orxkem&DRY(NthhQs0D(MKPpu&_|s^cb)Za9qm{e+UQ#+IudvS}h$N9e${B zMQ3v}^+%8L@BRC^c<4~SJtQJ2iCF~cP&S63G+R;-|?s7TrDcfiX7VwfAW<1`;z zES3RZ19UVrh_hR_iUaZSVh^|$l}db-pD*m~?ZRTQh|J7PkjGs^58sf4ul2?e+C8_eZ9tGVi00utY?VnwpBqWOBa!4e%?ZfXXfW zAwcD50fW7*jk8;}@ZBq~xQr88osPLhMU*x-larI96ulSN*PmX0A}d+y zN~_iSp~80rg@=>+!V6>@3`9C>8qv4hLUcw3`q{Gy zz2OG$goWVSmtNwVjT=FT-fERb!#7!3yn6DabL+Q(N8G9LhkysS!8`uFJI_$5)#%dG ziCemqu`5=%bai*Q@ZNiraRw>X3=Bg`u%1`S z`_luym;$%`25GA(Dk+K4$;pJp#1Ik{g(ftV;K)dsK$pXT&1}YIHq&wWGOg9sv>J^# zyj(zqj~mDIXP+f0FAsG;)9r=#-=}2Nsy-5s;^XIm?$~iVuvRJOD*+m$?$om_hS6vw zDJjWEHGmMb{rDr7%gSghEyYw^jQQ)YW#6+B5#z^`_Qo5;yH0fgoZYg8if5jY)tv!e z1B8^^ovf7cm4Gy$yyrr%*JCo7P^tV~Ps!0HF^E;=zNm`lz%t?mTgVt5z$~(b3Mg8Ua~_H_#z% zMfGM|R;^kk9(w2@q1WpNcwt@WuVIBYgiynMM!PdL7Cp=fJv zRzAz(tMdSV90(`9Eo!w|WMyTEUAuO9ooaK~Y~qW>i(N=CtXZSf@AK}_G^F&`kP+Zq zbUK~buwjE&y`r;gUB@m+SOt(*ⅆ#!6_A#Iwz2CM=ykhZTQ6}nVNA!!D% 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 Ef>mpIJOBUy literal 0 HcmV?d00001 diff --git a/ui/icons/svg/sv_surface_clean_knots.svg b/ui/icons/svg/sv_surface_clean_knots.svg new file mode 100644 index 000000000..8be2bf90f --- /dev/null +++ b/ui/icons/svg/sv_surface_clean_knots.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_surface_insert_knot.svg b/ui/icons/svg/sv_surface_insert_knot.svg new file mode 100644 index 000000000..c911a2c5e --- /dev/null +++ b/ui/icons/svg/sv_surface_insert_knot.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_surface_remove_knot.svg b/ui/icons/svg/sv_surface_remove_knot.svg new file mode 100644 index 000000000..f061514a4 --- /dev/null +++ b/ui/icons/svg/sv_surface_remove_knot.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + -- GitLab From 2e9929e121962ebf740db5dd129503949a743ebb Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 14:42:37 +0500 Subject: [PATCH 16/21] Rename. --- nodes/curve/insert_knot.py | 2 +- nodes/curve/remove_excessive_knots.py | 2 +- nodes/curve/remove_knot.py | 2 +- nodes/surface/insert_knot.py | 2 +- nodes/surface/remove_excessive_knots.py | 2 +- nodes/surface/remove_knot.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nodes/curve/insert_knot.py b/nodes/curve/insert_knot.py index f995710a9..d9e803d06 100644 --- a/nodes/curve/insert_knot.py +++ b/nodes/curve/insert_knot.py @@ -19,7 +19,7 @@ class SvCurveInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Inset knot in a NURBS curve """ bl_idname = 'SvCurveInsertKnotNode' - bl_label = 'NURBS Curve - Insert Knot' + bl_label = 'Insert Knot (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_FLIP_CURVE' diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py index d8e6306f6..b7d2e36e7 100644 --- a/nodes/curve/remove_excessive_knots.py +++ b/nodes/curve/remove_excessive_knots.py @@ -20,7 +20,7 @@ class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Remove all excessive knots from a NURBS curve """ bl_idname = 'SvCurveRemoveExcessiveKnotsNode' - bl_label = 'NURBS Curve - Remove Excessive Knots' + bl_label = 'Remove Excessive Knots (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_FLIP_CURVE' diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 43cbcecbd..86d7b8cbc 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -19,7 +19,7 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Remove a knot from a NURBS curve """ bl_idname = 'SvCurveRemoveKnotNode' - bl_label = 'NURBS Curve - Remove Knot' + bl_label = 'Remove Knot (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_FLIP_CURVE' diff --git a/nodes/surface/insert_knot.py b/nodes/surface/insert_knot.py index a41a78c84..cf86086b0 100644 --- a/nodes/surface/insert_knot.py +++ b/nodes/surface/insert_knot.py @@ -19,7 +19,7 @@ class SvSurfaceInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Insert a knot in a NURBS surface """ bl_idname = 'SvSurfaceInsertKnotNode' - bl_label = 'NURBS Surface - Insert Knot' + bl_label = 'Insert Knot (NURBS Surface)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_SURFACE_INSERT_KNOT' diff --git a/nodes/surface/remove_excessive_knots.py b/nodes/surface/remove_excessive_knots.py index 8f15e1816..f0281bd15 100644 --- a/nodes/surface/remove_excessive_knots.py +++ b/nodes/surface/remove_excessive_knots.py @@ -20,7 +20,7 @@ class SvSurfaceRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Remove a knot from a NURBS surface """ bl_idname = 'SvSurfaceRemoveExcessiveKnotsNode' - bl_label = 'NURBS Surface - Remove Excessive Knots' + bl_label = 'Remove Excessive Knots (NURBS Surface)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_SURFACE_CLEAN_KNOTS' diff --git a/nodes/surface/remove_knot.py b/nodes/surface/remove_knot.py index b2f35c15a..6ac39d16f 100644 --- a/nodes/surface/remove_knot.py +++ b/nodes/surface/remove_knot.py @@ -19,7 +19,7 @@ class SvSurfaceRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): Tooltip: Remove a knot from a NURBS surface """ bl_idname = 'SvSurfaceRemoveKnotNode' - bl_label = 'NURBS Surface - Remove Knot' + bl_label = 'Remove Knot (NURBS Surface)' bl_icon = 'OUTLINER_OB_EMPTY' sv_icon = 'SV_SURFACE_REMOVE_KNOT' -- GitLab From 0708a8b4b196cbbae0f3ec61d772c1f2707d9ecc Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 14:52:38 +0500 Subject: [PATCH 17/21] Icons. --- nodes/curve/insert_knot.py | 2 +- nodes/curve/remove_excessive_knots.py | 2 +- nodes/curve/remove_knot.py | 2 +- ui/icons/sv_curve_clean_knots.png | Bin 0 -> 1040 bytes ui/icons/sv_curve_insert_knot.png | Bin 0 -> 1242 bytes ui/icons/sv_curve_remove_knot.png | Bin 0 -> 1024 bytes ui/icons/svg/sv_curve_clean_knots.svg | 212 ++++++++++++++++++++++++++ ui/icons/svg/sv_curve_insert_knot.svg | 211 +++++++++++++++++++++++++ ui/icons/svg/sv_curve_remove_knot.svg | 202 ++++++++++++++++++++++++ 9 files changed, 628 insertions(+), 3 deletions(-) create mode 100644 ui/icons/sv_curve_clean_knots.png create mode 100644 ui/icons/sv_curve_insert_knot.png create mode 100644 ui/icons/sv_curve_remove_knot.png create mode 100644 ui/icons/svg/sv_curve_clean_knots.svg create mode 100644 ui/icons/svg/sv_curve_insert_knot.svg create mode 100644 ui/icons/svg/sv_curve_remove_knot.svg diff --git a/nodes/curve/insert_knot.py b/nodes/curve/insert_knot.py index d9e803d06..4e5773731 100644 --- a/nodes/curve/insert_knot.py +++ b/nodes/curve/insert_knot.py @@ -21,7 +21,7 @@ class SvCurveInsertKnotNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCurveInsertKnotNode' bl_label = 'Insert Knot (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_CURVE_INSERT_KNOT' knot : FloatProperty( name = "Knot", diff --git a/nodes/curve/remove_excessive_knots.py b/nodes/curve/remove_excessive_knots.py index b7d2e36e7..d3ee061b7 100644 --- a/nodes/curve/remove_excessive_knots.py +++ b/nodes/curve/remove_excessive_knots.py @@ -22,7 +22,7 @@ class SvCurveRemoveExcessiveKnotsNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCurveRemoveExcessiveKnotsNode' bl_label = 'Remove Excessive Knots (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_CURVE_CLEAN_KNOTS' tolerance : FloatProperty( name = "Tolerance", diff --git a/nodes/curve/remove_knot.py b/nodes/curve/remove_knot.py index 86d7b8cbc..6254b3117 100644 --- a/nodes/curve/remove_knot.py +++ b/nodes/curve/remove_knot.py @@ -21,7 +21,7 @@ class SvCurveRemoveKnotNode(bpy.types.Node, SverchCustomTreeNode): bl_idname = 'SvCurveRemoveKnotNode' bl_label = 'Remove Knot (NURBS Curve)' bl_icon = 'OUTLINER_OB_EMPTY' - sv_icon = 'SV_FLIP_CURVE' + sv_icon = 'SV_CURVE_REMOVE_KNOT' knot : FloatProperty( name = "Knot", diff --git a/ui/icons/sv_curve_clean_knots.png b/ui/icons/sv_curve_clean_knots.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb3ae6917a08de2690df1b3304229845e011e82 GIT binary patch literal 1040 zcmV+r1n>KaP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11D#1k zK~#90?VH<66j2<LDt!f(m*Jp_@!)xi2#TLBa%w8U|a0QZ37z^GiAV(|c5fy+P`Fw$&y<0sLMCh+G$ zgVX09py)kpiNNB-{&t`@^)Lpo$d{o;xAA+R*6G8dShMn#v<&b70bLCM!0Y!@-r$%B z)AZfB!;kj%bWi^h2x)IXS*>FtOf%fmla2vXz%$?|fYt`2OAxw&2f!O31bmORKMA{y z17q=x|H^z{mQD{C1@O^pR&q{co(dXKhyC>RksHBu zyb11f=zl6zg(C8SN$fMAOrb7m?8koaA-4kRb_r~9=yxssbu(bIL;nk@Dio0e{KEbc zSnF0$Wp%&}DEB7Be*&e?A!yyt17jy3F8~|75UzrLkvfz;6>f%an9uA}#(?@mW}P4m z<(1%q@^U=BnPo)6VIuuPzxZjC8?d9Lh1|lK6QQ%dp0{;%uEf{vS3o*h15^u8P1XR_ z0#uVVK(zqX%y|Qp7ecL-l_^z%ZlT1a`ed^bBmg}pp~~8yW3C1I zuXv38{Qta4QL+dE5$p}vrcRbTj$%LfpJ;Gzl0hr>2Ha37N*1L+6ng`9E7T>8E7%YI zGv>olX$ZU4E<$brS`()lPAK>K!B;q$@u1P;FM@Bsi4bj@GNPq za~6jV2D}4Ks|v2%fFK;eEQysM%z#mtm&tKwpi`8FOvvOW7{vTf&dS*9Dq!PnSW_)h znIxgr;st4qYGq~HU-V6MX4m*O!|pbzN5EMndP-Rof3 zlfhb^u;kgFmH4+lAGiSQ#axTqk2yCF#omV!wtImI=}NO?afQECfAz6(zN|X{0000< KMNUMnLSTZ0jp%~_ literal 0 HcmV?d00001 diff --git a/ui/icons/sv_curve_insert_knot.png b/ui/icons/sv_curve_insert_knot.png new file mode 100644 index 0000000000000000000000000000000000000000..082ba43539ddba9c3afdf4a37e9a0c8e4088d36f GIT binary patch literal 1242 zcmV<01SR{4P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11ZPP^ zK~#90?VDd{97P<*KXbRanp%rNEwN2Xn`*6s7AgKKLJ0T=sWnvmgD*BBHV;0E6yNGg zu}|WIppE1~AA~Am6H%)~7AN~&l3;-Qf+9!cGfFr=AT2G#I0s4T)G;I<%1ROy9 zs&$cM0a}5Bnl_0N=lw>Fq#8XzguDdYtZ6%df#4yz0;GUfG;QCb?-_L&N+J&d8#QhF zSvsmWgafeq-OM&8rC%5cDcgfAN`bRyAai>)1FoN*8}83n%psOxucm$!H6JjB10V!H zd6E|GQJx=IEhOz8{9M(7a7Tci8hlElANdDrI8P{&cnRE#n)8>F13ofsEQtaul9L;)Vw)PEI)v7`xbJ?at&;#d?07iWUMK;?3`gcj6erX@&;D+CkO3s0vFFV_ z&W>GxwCf-85l@xZZ|X_2sV5!umDRv0U#bt8oU;?$QB(d{dmhk!-Da*`6*S=sX6o`8 z=3VpeF{}x&_r8Pd-27zd6u`CvwrL7r+X34&1+eXaZLCd&$EU}TQieW$cE08pzoD=uz|qrh z*)qKZ3j&Bygp|IEGyS9ggp^dQf!nr-k=8a5IL&TJr7szLGiH@Of)khiBH6F)jd7e{ z0Y0$lR9tKYJc0$#y%}0j940vUDX1J1RvH_DJi!7OOMFN&9}^t>Jh0xPU$O8E`n1*e zEcz7-cLB2m2cO4W3Tz_T0bE4?>%VT#wP;BJFQXsyBybj$6S$(t0M7!ash+Mr2i$4N zN8_vodVzO<3oM_j?*Y$Q@w9|`=G<$5TYy`Oe{TcsLG9TI`+UIzUIad@!@g0Z_w}EJa z_JGfUJtTO2@9^KU)Wjyr-%$BHU!c<3W(=G&QWk?A1|ARj9S^llpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11C2>U zK~#90?b}ai9Ay~C@y~A7^$(%gDg~R`L$I|dC{+~9rHEJ&MG*>O52@%$1n(Z|!Gooj zdJ;qh4{9%>D1u^(7(`f+s-R$lR#8b5bSwBLskYg!hd0$tn2EdDnRzFh{lZIMDdPevs_^an#~TD~3JNOaHbk4b|WXI~Ve666YpEY4*bYl>7$X>r>AX zYH*Ivt6Y%fgFBeQtvy^nf;&_G_k7=KmQl_6ff;bK(C1&yJYLP(SQ!IwM@s&bFqY4h zG6vxGl>B3P7%N)|O2sRVMa@E^#JA8&!>tkE?G?>hLA=`}DMhx$M_5q6J{rp+mOq?Y<(sJ29cz z$gJT1rEIN0;S2*1TR?1v0f;RiHp2kK77&|Z0AdS>&AJajw61Wo(V*S#`ua1cPUWH_ zS^!QQJ{-&R5(*DMn?{B90<}-MryV9jmav_vP5hE{|5@^>3K4iv^6byA*1_T@VT~^- z058Q9RTMjf%}PlDNLv{iD4vt-{H%m;!l1DYrzHpAsffA?<2A|7|Aj3Pbrr(>(szEm z5K&hlTqk|uWtnFAM~0UO4{7LFJH$`;sGU{f*X^M(=Fk!!+lCT zzOVUjSenIViSLB{JnsnG+L{?WGgr<#{TA--@}8Ek`T1+1YM;b3J`tL~HO2V=Y{1jF uOE``3qcAp~S^Ryb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_curve_insert_knot.svg b/ui/icons/svg/sv_curve_insert_knot.svg new file mode 100644 index 000000000..69134eb0b --- /dev/null +++ b/ui/icons/svg/sv_curve_insert_knot.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/ui/icons/svg/sv_curve_remove_knot.svg b/ui/icons/svg/sv_curve_remove_knot.svg new file mode 100644 index 000000000..d368f1f9c --- /dev/null +++ b/ui/icons/svg/sv_curve_remove_knot.svg @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + -- GitLab From 77d2fa4cc8d83606dd68b2a8046d270ba126f521 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 16:29:11 +0500 Subject: [PATCH 18/21] Replace prints with debug. --- utils/curve/nurbs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/curve/nurbs.py b/utils/curve/nurbs.py index 2c8bbe2f3..1a22c344a 100644 --- a/utils/curve/nurbs.py +++ b/utils/curve/nurbs.py @@ -24,6 +24,7 @@ from sverchok.utils.nurbs_common import ( from sverchok.utils.surface.nurbs import SvNativeNurbsSurface, SvGeomdlSurface from sverchok.utils.surface.algorithms import nurbs_revolution_surface from sverchok.utils.geom import bounding_box +from sverchok.utils.logging import getLogger from sverchok.dependencies import geomdl if geomdl is not None: @@ -939,6 +940,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): def remove_knot(self, u, count=1, target=None, tolerance=1e-6, if_possible=False): # Implementation adapted from Geomdl + logger = getLogger() if (count is None) == (target is None): raise Exception("Either count or target must be specified") @@ -1021,7 +1023,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): if dist <= tolerance: can_remove = True else: - print(f"F.1 Dist={dist}") + logger.debug(f"remove_knot: stop, distance={dist}") else: alpha_i = knot_removal_alpha_i(u, knotvector, i) ptn = alpha_i * temp_j[ii + t + 1] + (1.0 - alpha_i)*temp_i[ii - 1] @@ -1029,7 +1031,7 @@ class SvNativeNurbsCurve(SvNurbsCurve): if dist <= tolerance: can_remove = True else: - print(f"F.2 T={t} i={i} j={j}, ii={ii}, jj={jj}, A={alpha_i}, temp[ii+t+1]={temp_j[ii+t+1]}, temp[ii-1]={temp_i[ii-1]}, ptn={ptn}, ctrlpts[i]={ctrlpts[i]} Dist={dist}") + logger.debug(f"remove_knot: stop, distance={dist}") # Check if we can remove the knot and update new control points array if can_remove: -- GitLab From 1b8481515980376b2f7f8c751244c8515c51ac2e Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 16:56:08 +0500 Subject: [PATCH 19/21] Start documentation. --- docs/nodes/curve/curve_index.rst | 3 ++ docs/nodes/curve/insert_knot.rst | 33 +++++++++++++++ docs/nodes/curve/remove_excessive_knots.rst | 36 +++++++++++++++++ docs/nodes/curve/remove_knot.rst | 45 +++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 docs/nodes/curve/insert_knot.rst create mode 100644 docs/nodes/curve/remove_excessive_knots.rst create mode 100644 docs/nodes/curve/remove_knot.rst diff --git a/docs/nodes/curve/curve_index.rst b/docs/nodes/curve/curve_index.rst index 98575bb6d..03b076c6b 100644 --- a/docs/nodes/curve/curve_index.rst +++ b/docs/nodes/curve/curve_index.rst @@ -51,6 +51,9 @@ Curves nurbs_curve approximate_nurbs_curve interpolate_nurbs_curve + insert_knot + remove_knot + remove_excessive_knots catenary_curve bezier_fit circlify diff --git a/docs/nodes/curve/insert_knot.rst b/docs/nodes/curve/insert_knot.rst new file mode 100644 index 000000000..290c61a62 --- /dev/null +++ b/docs/nodes/curve/insert_knot.rst @@ -0,0 +1,33 @@ +Insert Knot (NURBS Curve) +========================= + +Functionality +------------- + +This node performs "knot insertion" operation on a NURBS curve object. + +Given a NURBS Curve, the node adds the provided value into curve's knotvector. +If the provided value is already present in the knotvector, it's multiplicity +will be increased. + +This node can work only with NURBS and NURBS-like curves. + +It is possible to provide a list of knot values (and corresponding +multiplicities) for each curve. + +Inputs +------ + +This node has the following inputs: + +* **Curve**. The Curve object to work with. This input is mandatory. +* **Knot**. The value of the new knot. The default value is 0.5. +* **Count**. Number of times the knot is to be inserted. The default value is 0. + +Outputs +------- + +This node has the following output: + +* **Curve**. The resulting Curve object. + diff --git a/docs/nodes/curve/remove_excessive_knots.rst b/docs/nodes/curve/remove_excessive_knots.rst new file mode 100644 index 000000000..fe296645b --- /dev/null +++ b/docs/nodes/curve/remove_excessive_knots.rst @@ -0,0 +1,36 @@ +Remove Excessive Knots (NURBS Curve) +==================================== + +Functionality +------------- + +This node performs "knot removal" procedure for a NURBS curve, trying to remove +as many knots, and as many times, as it is possible, without changing the shape +of the curve too much. Allowed change of shape is controlled by "tolerance" +parameter. + +This node can work only with NURBS and NURBS-like curves. + +Inputs +------ + +This node has the following input: + +* **Curve**. The curve to work on. This input is mandatory. + +Parameters +---------- + +This node has the following parameter: + +* **Tolerance**. This parameter is available in the N panel only. This defines + how much is it allowed to change the shape of the curve by knot removal + procedure. The default value is ``10^-6``. + +Outputs +------- + +This node has the following output: + +* **Curve**. The resulting curve. + diff --git a/docs/nodes/curve/remove_knot.rst b/docs/nodes/curve/remove_knot.rst new file mode 100644 index 000000000..0c816c625 --- /dev/null +++ b/docs/nodes/curve/remove_knot.rst @@ -0,0 +1,45 @@ +Remove Knot (NURBS Curve) +========================= + +Functionality +------------- + +This node performs "knot removal" procedure for a NURBS curve. + +Given a NURBS Curve object and a knot value, the node reduces the multiplicity +of this knot in curve's knotvector. This procedure can not always be performed. +In general, this procedure changes the shape of the curve. There is "tolerance" +parameter, defining how much is it allowed to change the shape of the curve. + +This node can work only with NURBS and NURBS-like curves. + +Inputs +------ + +This node has the following inputs: + +* **Curve**. The curve to work on. This input is mandatory. +* **Knot**. The value of the knot to be removed. The default value is 0.5. +* **Count**. Number of times the knot is to be removed. The default value is 1. + +Parameters +---------- + +This node has the following parameters: + +* **Only if possible**. If this flag is checked, the node will try to remove + the knot **Count** times; if it is not possible to remove it that many times, + the node will just remove it as many times as it can. If not checked, then + the node will fail (become red) in such a situation, and the processing will + stop. Unchecked by default. +* **Tolerance**. This parameter is available in the N panel only. This defines + how much is it allowed to change the shape of the curve by knot removal + procedure. The default value is ``10^-6``. + +Outputs +------- + +This node has the following output: + +* **Curve**. The resulting curve. + -- GitLab From 13e29db085375970ee307a65ee23f377f55359b6 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 17:32:42 +0500 Subject: [PATCH 20/21] Add example. --- docs/nodes/curve/insert_knot.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/nodes/curve/insert_knot.rst b/docs/nodes/curve/insert_knot.rst index 290c61a62..6247784b9 100644 --- a/docs/nodes/curve/insert_knot.rst +++ b/docs/nodes/curve/insert_knot.rst @@ -31,3 +31,10 @@ This node has the following output: * **Curve**. The resulting Curve object. +Example of Usage +---------------- + +Insert a knot twice into a circle curve, and move the new control point: + +.. image:: https://user-images.githubusercontent.com/284644/126066221-95589c16-c04d-4b9c-add7-19c6ad4ab906.png + -- GitLab From d314ba23614ab583bc41711c11672580f9a87d82 Mon Sep 17 00:00:00 2001 From: Ilya Portnov Date: Sun, 18 Jul 2021 17:53:54 +0500 Subject: [PATCH 21/21] Documentation. --- docs/nodes/surface/insert_knot.rst | 41 ++++++++++++++++ docs/nodes/surface/remove_excessive_knots.rst | 39 +++++++++++++++ docs/nodes/surface/remove_knot.rst | 49 +++++++++++++++++++ docs/nodes/surface/surface_index.rst | 3 ++ 4 files changed, 132 insertions(+) create mode 100644 docs/nodes/surface/insert_knot.rst create mode 100644 docs/nodes/surface/remove_excessive_knots.rst create mode 100644 docs/nodes/surface/remove_knot.rst diff --git a/docs/nodes/surface/insert_knot.rst b/docs/nodes/surface/insert_knot.rst new file mode 100644 index 000000000..4ac2627dd --- /dev/null +++ b/docs/nodes/surface/insert_knot.rst @@ -0,0 +1,41 @@ +Insert Knot (NURBS Surface) +=========================== + +Functionality +------------- + +This node performs "knot insertion" operation on a NURBS Surface object. + +Given a NURBS Surface, the node adds the provided value in the surface's U- or +V-knotvector. If the provided value is already present in the knotvector, it's +multiplicity will be increased. + +This node can work only with NURBS surfaces. + +It is possible to provide a list of knot values (and corresponding +multiplicities) for each surface. + +Inputs +------ + +This node has the following inputs: + +* **Surface**. The Surface object to work with. This input is mandatory. +* **Knot**. The value of the new knot. The default value is 0.5. +* **Count**. Number of times the knot is to be inserted. The default value is 0. + +Parameters +---------- + +This node has the following parameter: + +* **Parameter**. This defines the parametric direction, for which to insert the + knot. The available values are **U** and **V**. The default value is **U**. + +Outputs +------- + +This node has the following output: + +* **Surface**. The resulting Surface object. + diff --git a/docs/nodes/surface/remove_excessive_knots.rst b/docs/nodes/surface/remove_excessive_knots.rst new file mode 100644 index 000000000..e126c7ad5 --- /dev/null +++ b/docs/nodes/surface/remove_excessive_knots.rst @@ -0,0 +1,39 @@ +Remove Excessive Knots (NURBS Surface) +====================================== + +Functionality +------------- + +This node performs "knot removal" procedure for a NURBS surface, trying to +remove as many knots, and as many times, as it is possible, without changing +the shape of the surface too much. Allowed change of shape is controlled by +"tolerance" parameter. + +This node can only work with NURBS surfaces. + +Inputs +------ + +This node has the following input: + +* **Surface**. The Surface object to work on. This input is mandatory. + +Parameters +---------- + +This node has the following parameters: + +* **Direction**. This defines, in which parametric directions should the knot + removal procedure be performed. The available options are **U+V** (both + directions), **U** and **V**. The default value is **U+V**. +* **Tolerance**. This parameter is available in the N panel only. This defines + how much is it allowed to change the shape of the surface by knot removal + procedure. The default value is ``10^-6``. + +Outputs +------- + +This node has the following output: + +* **Surface**. The resulting Surface object. + diff --git a/docs/nodes/surface/remove_knot.rst b/docs/nodes/surface/remove_knot.rst new file mode 100644 index 000000000..6b8af1a54 --- /dev/null +++ b/docs/nodes/surface/remove_knot.rst @@ -0,0 +1,49 @@ +Remove Knot (NURBS Surface) +=========================== + +Functionality +------------- + +This node performs "knot removal" procedure for a NURBS Surface. + +Given a NURBS Surface object and a knot value, the node reduces the +multiplicity of this knot in surface's U- or V-knotvector. This procedure can +not always be performed. In general, this procedure changes the shape of the +surface. There is "tolerance" parameter, defining how much is it allowed to +chagne the shape of the surface. + +This node can work only with NURBS surfaces. + +Inputs +------ + +This node has the following inputs: + +* **Surface**. The surface to work on. This input is mandatory. +* **Knot**. The value of the knot to be removed. The default value is 0.5. +* **Count**. Number of times the knot is to be removed. The default value is 1. + +Parameters +---------- + +This node has the following parameters: + +* **Direction**. This defines the parametric direction on the surface, for + which to perform knot removal procedure. The available options are **U** and + **V**. The default value is **V**. +* **Only if possible**. If this flag is checked, the node will try to remove + the knot **Count** times; if it is not possible to remove it that many times, + the node will just remove it as many times as it can. If not checked, then + the node will fail (become red) in such a situation, and the processing will + stop. Unchecked by default. +* **Tolerance**. This parameter is available in the N panel only. This defines + how much is it allowed to change the shape of the surface by knot removal + procedure. The default value is ``10^-6``. + +Outputs +------- + +This node has the following output: + +* **Surface**. The resulting Surface object. + diff --git a/docs/nodes/surface/surface_index.rst b/docs/nodes/surface/surface_index.rst index efad5c18a..b0fac6dc0 100644 --- a/docs/nodes/surface/surface_index.rst +++ b/docs/nodes/surface/surface_index.rst @@ -38,6 +38,9 @@ Surface nurbs_sweep nurbs_birail gordon_surface + insert_knot + remove_knot + remove_excessive_knots intersect_curve_surface nearest_point ortho_project -- GitLab