Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix selection nearest point on init_guess to nearest edge. #4871

Merged
merged 6 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/nodes/curve/nearest_point.rst
Original file line number Diff line number Diff line change
@@ -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
------------

Expand Down Expand Up @@ -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
------

Expand Down
2 changes: 2 additions & 0 deletions nodes/curve/nearest_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
121 changes: 74 additions & 47 deletions utils/manifolds.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down