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..ec6d8b65ed 100644 --- a/utils/manifolds.py +++ b/utils/manifolds.py @@ -108,16 +108,37 @@ 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) - us_out.append(us[i]) + nearest, normal, i, distance = tree.find_nearest( point_from ) + nearest = np.array(nearest) + + # find t of arc: + 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 = p0 + elif dist_nearest_to_p0==max_dist: + t01 = 1 + raw_t = us[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 + 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, p0, nearest_t, p1 ) ) # interval to search minimum nearest_out.append(tuple(nearest)) return us_out, np.array(nearest_out) @@ -126,52 +147,58 @@ 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: - 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: - bracket = (t_min, init_t, t_max) - result = minimize_scalar(goal, - bounds = (t_min, t_max), - 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] 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] is None: + result_points[i] = result_points_none.pop(0) + + return list(zip(result_ts, np.array(result_points) )) else: return result_ts