From a8e2b73b0dbd02ab72cf74a75ca7784f5364946e Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 01:19:48 +0300 Subject: [PATCH 1/6] Fix selection nearest point on init_guess to nearest edge. --- docs/nodes/curve/nearest_point.rst | 6 ++++++ nodes/curve/nearest_point.py | 2 ++ utils/manifolds.py | 31 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/docs/nodes/curve/nearest_point.rst b/docs/nodes/curve/nearest_point.rst index 71d8446b63..2173768a4c 100644 --- a/docs/nodes/curve/nearest_point.rst +++ b/docs/nodes/curve/nearest_point.rst @@ -1,6 +1,9 @@ Nearest Point on Curve ====================== +.. image:: https://user-images.githubusercontent.com/14288520/211775155-aaa814c0-b5d1-4a9f-8301-da1e60a9a97c.png + :target: https://user-images.githubusercontent.com/14288520/211775155-aaa814c0-b5d1-4a9f-8301-da1e60a9a97c.png + Dependencies ------------ @@ -29,6 +32,9 @@ In case there are several points on the curve with equal distance to the original point, the node will return one of them (it is not guaranteed which one). +.. image:: https://user-images.githubusercontent.com/14288520/211776462-f01c0f6f-d6bd-41f6-9758-fd590ebb0666.png + :target: https://user-images.githubusercontent.com/14288520/211776462-f01c0f6f-d6bd-41f6-9758-fd590ebb0666.png + Inputs ------ diff --git a/nodes/curve/nearest_point.py b/nodes/curve/nearest_point.py index 4dc51032fa..e719a792aa 100644 --- a/nodes/curve/nearest_point.py +++ b/nodes/curve/nearest_point.py @@ -21,12 +21,14 @@ class SvExNearestPointOnCurveNode(SverchCustomTreeNode, bpy.types.Node): samples : IntProperty( name = "Init Resolution", + description = "Initial number of segments to subdivide curve in, for the first step of algorithm. The higher values will lead to more precise initial guess, so the precise algorithm will be faster", default = 50, min = 3, update = updateNode) precise : BoolProperty( name = "Precise", + description = "If not checked, then the precise calculation step will not be executed, and the node will just output the nearest point out of points generated at the first step - so it will be “roughly nearest point”", default = True, update = updateNode) diff --git a/utils/manifolds.py b/utils/manifolds.py index e930b455ea..4f7172c606 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -108,15 +108,13 @@ def init_guess(curve, points_from): points = curve.evaluate_array(us).tolist() #print("P:", points) - kdt = kdtree.KDTree(len(us)) - for i, v in enumerate(points): - kdt.insert(v, i) - kdt.balance() + polygons = [(i, i+1, i) for i in range(len(points)-1)] + tree = BVHTree.FromPolygons( points, polygons, all_triangles = True ) us_out = [] nearest_out = [] for point_from in points_from: - nearest, i, distance = kdt.find(point_from) + nearest, normal, i, distance = tree.find_nearest( point_from ) us_out.append(us[i]) nearest_out.append(tuple(nearest)) @@ -132,20 +130,21 @@ def goal(t): for src_point, init_t, init_point in zip(src_points, init_ts, init_points): delta_t = (t_max - t_min) / samples logger.debug("T_min %s, T_max %s, init_t %s, delta_t %s", t_min, t_max, init_t, delta_t) + if init_t <= t_min: - if init_t - delta_t >= t_min: - bracket = (init_t - delta_t, init_t, t_max) - else: - bracket = None # (t_min, t_min + delta_t, t_min + 2*delta_t) - elif init_t >= t_max: - if init_t + delta_t <= t_max: - bracket = (t_min, init_t, init_t + delta_t) - else: - bracket = None # (t_max - 2*delta_t, t_max - delta_t, t_max) - else: + init_t = t_min + delta_t/2 + bracket = (t_min, init_t, t_min+delta_t) + bounds = (t_min, t_min+delta_t) + elif t_mint_min else t_min, init_t+delta_t if init_t+delta_t= t_max: + init_t = t_max - delta_t/2 + bracket = (t_max-delta_t, init_t, t_max) + bounds = (init_t-delta_t, t_max) + result = minimize_scalar(goal, - bounds = (t_min, t_max), + bounds = bounds, bracket = bracket, method = method ) From 36ae1ac17c162afbddb353baa725953bfeebd2ad Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 01:42:35 +0300 Subject: [PATCH 2/6] replace init_guess nearest point to interval to search nearest point. --- utils/manifolds.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/utils/manifolds.py b/utils/manifolds.py index 4f7172c606..fbdd0a9f2f 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -115,7 +115,7 @@ def init_guess(curve, points_from): nearest_out = [] for point_from in points_from: nearest, normal, i, distance = tree.find_nearest( point_from ) - us_out.append(us[i]) + us_out.append( (us[i], us[i+1]) ) # interval to search minimum nearest_out.append(tuple(nearest)) return us_out, np.array(nearest_out) @@ -124,24 +124,16 @@ def goal(t): dv = curve.evaluate(t) - np.array(src_point) return np.linalg.norm(dv) - init_ts, init_points = init_guess(curve, src_points) + intervals, init_points = init_guess(curve, src_points) result_ts = [] if precise: - for src_point, init_t, init_point in zip(src_points, init_ts, init_points): - delta_t = (t_max - t_min) / samples - logger.debug("T_min %s, T_max %s, init_t %s, delta_t %s", t_min, t_max, init_t, delta_t) - - if init_t <= t_min: - init_t = t_min + delta_t/2 - bracket = (t_min, init_t, t_min+delta_t) - bounds = (t_min, t_min+delta_t) - elif t_mint_min else t_min, init_t+delta_t if init_t+delta_t= t_max: - init_t = t_max - delta_t/2 - bracket = (t_max-delta_t, init_t, t_max) - bounds = (init_t-delta_t, t_max) + for src_point, interval, init_point in zip(src_points, intervals, init_points): + + init_t = (interval[0]+interval[1])/2 + bracket = (interval[0], init_t, interval[1]) + bounds = (interval[0], interval[1]) + + logger.debug("T_min %s, T_max %s, init_t %s", t_min, t_max, init_t) result = minimize_scalar(goal, bounds = bounds, From b4255f7e9c4fa2609cf85d16bcf4bdd38e1e22c4 Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 12:31:22 +0300 Subject: [PATCH 3/6] Refactor precise. Now precise off get more precise than before. --- utils/manifolds.py | 113 +++++++++++++++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/utils/manifolds.py b/utils/manifolds.py index fbdd0a9f2f..f2716fc004 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -115,7 +115,39 @@ def init_guess(curve, points_from): nearest_out = [] for point_from in points_from: nearest, normal, i, distance = tree.find_nearest( point_from ) - us_out.append( (us[i], us[i+1]) ) # interval to search minimum + + # find t of arc: + points_i0 = points[i] + points_i1 = points[i+1] + dist_ix = abs(points_i1[0] - points_i0[0]) + dist_iy = abs(points_i1[1] - points_i0[1]) + dist_iz = abs(points_i1[2] - points_i0[2]) + max_axis = 0 + max_dist = dist_ix + if max_dist < dist_iy: + max_dist = dist_iy + max_axis = 1 + if max_dist < dist_iz: + max_dist = dist_iz + max_axis = 2 + dist_nearest_to_p0 = abs(nearest[max_axis]-points_i0[max_axis]) + if dist_nearest_to_p0==0: + t01=0 + raw_t = us[i] + nearest_t = points[i] + elif dist_nearest_to_p0==1: + t01 = 1 + raw_t = us[i+1] + nearest_t = points[i+1] + else: + t01 = dist_nearest_to_p0/max_dist + raw_t = us[i] + t01*(us[i+1]-us[i]) # approximate nearest t by chorda + nearest_t = None #curve.evaluate(raw_t).tolist() # later + + # t0 - [0-1] in interval us[i]-us[i+1] + # raw_t - translate t0 to curve t + # nearest_t - if t0==0 or 1 then use points, else None and calc later + us_out.append( (us[i], us[i+1], t01, raw_t, points[i], nearest_t, points[i+1] ) ) # interval to search minimum nearest_out.append(tuple(nearest)) return us_out, np.array(nearest_out) @@ -126,43 +158,56 @@ def goal(t): intervals, init_points = init_guess(curve, src_points) result_ts = [] - if precise: - for src_point, interval, init_point in zip(src_points, intervals, init_points): - - init_t = (interval[0]+interval[1])/2 - bracket = (interval[0], init_t, interval[1]) - bounds = (interval[0], interval[1]) - - logger.debug("T_min %s, T_max %s, init_t %s", t_min, t_max, init_t) - - result = minimize_scalar(goal, - bounds = bounds, - bracket = bracket, - method = method - ) + result_points = [] + for src_point, interval, init_point in zip(src_points, intervals, init_points): - if not result.success: - if hasattr(result, 'message'): - message = result.message - else: - message = repr(result) - raise Exception("Can't find the nearest point for {}: {}".format(src_point, message)) + t01 = interval[2] + res_t = interval[3] + res_point = interval[5] # remark: may be None. Calc of None later after getting final t. - t0 = result.x - if t0 < t_min: - t0 = t_min - elif t0 > t_max: - t0 = t_max - result_ts.append(t0) - else: - result_ts = init_ts + if precise==True: + if t01==0 or t01==1: + pass + else: + raw_t = interval[3] + bracket = (interval[0], raw_t, interval[1]) + bounds = (interval[0], interval[1]) + + logger.debug("T_min %s, T_max %s, init_t %s", t_min, t_max, raw_t) + + result = minimize_scalar(goal, + bounds = bounds, + bracket = bracket, + method = method + ) + + if not result.success: + if hasattr(result, 'message'): + message = result.message + else: + message = repr(result) + raise Exception("Can't find the nearest point for {}: {}".format(src_point, message)) + + res_t = result.x + + result_ts.append(res_t) + result_points.append(res_point) if output_points: - if precise: - result_points = curve.evaluate_array(np.array(result_ts)) - return list(zip(result_ts, result_points)) - else: - return list(zip(result_ts, init_points)) + result_ts_none = [] + # get t where points is None value + for i in range(len(result_points)): + if result_points[i]==None: + result_ts_none.append(result_ts[i]) + + if len(result_ts_none)>0: + # evaluate that points and save values: + result_points_none = curve.evaluate_array(np.array(result_ts_none)).tolist() + for i in range(len(result_points)): + if result_points[i]==None: + result_points[i] = result_points_none.pop(0) + + return list(zip(result_ts, np.array(result_points) )) else: return result_ts From 92dca0a89eb0a3a0ce403af8b4fba866ec4dcb91 Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 14:31:37 +0300 Subject: [PATCH 4/6] optimize code. --- utils/manifolds.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/utils/manifolds.py b/utils/manifolds.py index f2716fc004..9a8326a85f 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -119,17 +119,9 @@ def init_guess(curve, points_from): # find t of arc: points_i0 = points[i] points_i1 = points[i+1] - dist_ix = abs(points_i1[0] - points_i0[0]) - dist_iy = abs(points_i1[1] - points_i0[1]) - dist_iz = abs(points_i1[2] - points_i0[2]) - max_axis = 0 - max_dist = dist_ix - if max_dist < dist_iy: - max_dist = dist_iy - max_axis = 1 - if max_dist < dist_iz: - max_dist = dist_iz - max_axis = 2 + dist_axis = (abs(points_i1[0] - points_i0[0]), abs(points_i1[1] - points_i0[1]), abs(points_i1[2] - points_i0[2]) ) + max_axis = np.argmax(dist_axis) + max_dist = dist_axis[max_axis] dist_nearest_to_p0 = abs(nearest[max_axis]-points_i0[max_axis]) if dist_nearest_to_p0==0: t01=0 From c8c9e8faefe0afb239331cd2672e8547b931dc22 Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 17:19:19 +0300 Subject: [PATCH 5/6] little fix --- utils/manifolds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/manifolds.py b/utils/manifolds.py index 9a8326a85f..b3e33b5b06 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -127,7 +127,7 @@ def init_guess(curve, points_from): t01=0 raw_t = us[i] nearest_t = points[i] - elif dist_nearest_to_p0==1: + elif dist_nearest_to_p0==max_dist: t01 = 1 raw_t = us[i+1] nearest_t = points[i+1] From f1e629267ea06835aa2b738f96be157e3724ea2b Mon Sep 17 00:00:00 2001 From: satabol Date: Thu, 12 Jan 2023 17:50:28 +0300 Subject: [PATCH 6/6] convert some operation to numpy. --- utils/manifolds.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/utils/manifolds.py b/utils/manifolds.py index b3e33b5b06..ec6d8b65ed 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -115,22 +115,21 @@ def init_guess(curve, points_from): nearest_out = [] for point_from in points_from: nearest, normal, i, distance = tree.find_nearest( point_from ) + nearest = np.array(nearest) # find t of arc: - points_i0 = points[i] - points_i1 = points[i+1] - dist_axis = (abs(points_i1[0] - points_i0[0]), abs(points_i1[1] - points_i0[1]), abs(points_i1[2] - points_i0[2]) ) - max_axis = np.argmax(dist_axis) - max_dist = dist_axis[max_axis] - dist_nearest_to_p0 = abs(nearest[max_axis]-points_i0[max_axis]) + p0 = np.array(points[i]) + p1 = np.array(points[i+1]) + max_dist = abs(p0-p1).max() + dist_nearest_to_p0 = abs(nearest-p0).max() if dist_nearest_to_p0==0: t01=0 raw_t = us[i] - nearest_t = points[i] + nearest_t = p0 elif dist_nearest_to_p0==max_dist: t01 = 1 raw_t = us[i+1] - nearest_t = points[i+1] + nearest_t = p1 else: t01 = dist_nearest_to_p0/max_dist raw_t = us[i] + t01*(us[i+1]-us[i]) # approximate nearest t by chorda @@ -139,7 +138,7 @@ def init_guess(curve, points_from): # t0 - [0-1] in interval us[i]-us[i+1] # raw_t - translate t0 to curve t # nearest_t - if t0==0 or 1 then use points, else None and calc later - us_out.append( (us[i], us[i+1], t01, raw_t, points[i], nearest_t, points[i+1] ) ) # interval to search minimum + us_out.append( (us[i], us[i+1], t01, raw_t, p0, nearest_t, p1 ) ) # interval to search minimum nearest_out.append(tuple(nearest)) return us_out, np.array(nearest_out) @@ -189,14 +188,14 @@ def goal(t): result_ts_none = [] # get t where points is None value for i in range(len(result_points)): - if result_points[i]==None: + if result_points[i] is None: result_ts_none.append(result_ts[i]) if len(result_ts_none)>0: # evaluate that points and save values: result_points_none = curve.evaluate_array(np.array(result_ts_none)).tolist() for i in range(len(result_points)): - if result_points[i]==None: + if result_points[i] is None: result_points[i] = result_points_none.pop(0) return list(zip(result_ts, np.array(result_points) ))