Skip to content

Commit

Permalink
Nearest Point on Mesh (#3982)
Browse files Browse the repository at this point in the history
* Nearest Point on Mesh

* docs examples

* cleaining

* docs fixing
  • Loading branch information
vicdoval committed Mar 19, 2021
1 parent 75b9b44 commit 82af1bf
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/nodes/analyzer/analyzer_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Analyzers
select_similar
proportional
normals
nearest_point_on_mesh
bvh_overlap_polys
object_insolation
path_length_2
Expand Down
53 changes: 53 additions & 0 deletions docs/nodes/analyzer/nearest_point_on_mesh.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Nearest Point on Mesh
=====================

Functionality
-------------

Finds the closest point in a specified mesh.

Inputs
------

Vertices, Faces: Base mesh for the search
Points: Points to query
Distance: Maximum query distance (only for Nearest in Range mode)

Parameters
----------

*Mode*:
- Nearest: Nearest point on the mesh surface
- Nearest in range: Nearest points on the mesh within a range (one per face)

*Flat Output*: (only in Nearest in Range) Flattens the list of every vertex to have only a list for every inputted list.

*Safe Check*: (in N-Panel) When disabled polygon indices referring to unexisting points will crash Blender. Not performing this check makes node faster

Outputs
-------

*Location*: Position of the closest point in the mesh

*Normal*: mesh normal at closets point

*Index*: Face index of the closest point

*Distance*: Distance from the queried point to the closest point

Examples
--------

Used as skin-wrap modifier:

.. image:: https://user-images.githubusercontent.com/10011941/111774583-f3733480-88af-11eb-9559-78392166b00c.png


Determine which polygons are nearer than a distance:

.. image:: https://user-images.githubusercontent.com/10011941/111812010-f255fd80-88d7-11eb-8f48-de67716dd93a.png


Placing objects on mesh:

.. image:: https://user-images.githubusercontent.com/10011941/111810852-bf5f3a00-88d6-11eb-9cff-eb2a6c18a01a.png
2 changes: 1 addition & 1 deletion index.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@
SvKDTreeNodeMK2
SvKDTreeEdgesNodeMK2
SvKDTreePathNode
SvNearestPointOnMeshNode
SvBvhOverlapNodeNew
SvMeshFilterNode
SvEdgeAnglesNode
Expand Down Expand Up @@ -715,7 +716,6 @@
SvSampleUVColorNode
SvSubdivideLiteNode
SvExtrudeSeparateLiteNode
SvBVHnearNewNode
SvUnsubdivideNode
SvLimitedDissolveMK2
SvArmaturePropsNode
Expand Down
167 changes: 167 additions & 0 deletions nodes/analyzer/nearest_point_on_mesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
from itertools import cycle
import bpy
from bpy.props import EnumProperty, BoolProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.utils.bvh_tree import bvh_tree_from_polygons
from sverchok.utils.nodes_mixins.recursive_nodes import SvRecursiveNode

def take_third(elem):
return elem[2]

def append_multiple(container, data):
for r, rl in zip(container, data):
r.append(rl)

def translate_data(data):
try:
return data[0][:], data[1][:], data[2], data[3]
except TypeError:
return [], [], -1, -1


def svmesh_to_bvh_lists(vsock, fsock, safe_check):
for vertices, polygons in zip(vsock, fsock):
yield bvh_tree_from_polygons(vertices, polygons, all_triangles=False, epsilon=0.0, safe_check=safe_check)

def nearest_point_in_mesh(verts, faces, points, safe_check=True):
'''Expects multiple objects lists (level of nesting 3)'''
output = [[] for i in range(4)]
for bvh, pts in zip(svmesh_to_bvh_lists(verts, faces, safe_check), points):
res_local = list(zip(*[translate_data(bvh.find_nearest(P)) for P in pts]))
append_multiple(output, res_local)

return output

def nearest_in_range(verts, faces, points, distance, safe_check=True, flat_output=True):
'''
verts, faces and points: Expects multiple objects lists (level of nesting 3)
distace: expects a list with level of nesting of 2
'''
output = [[] for i in range(4)]
for bvh, pts, dist in zip(svmesh_to_bvh_lists(verts, faces, safe_check), points, distance):

res_local = [[] for i in range(4)]

for pt, d in zip(pts, cycle(dist)):
res = bvh.find_nearest_range(pt, d)
#claning results:
res = sorted(res, key=take_third)
unique = []
if flat_output:
for r in res:
if not r[2] in unique:
unique.append(r[2])
append_multiple(res_local, translate_data(r))

else:
sub_res_local = [[] for i in range(4)]
for r in res:
if not r[2] in unique:
unique.append(r[2])
append_multiple(sub_res_local, translate_data(r))

append_multiple(res_local, sub_res_local)

append_multiple(output, res_local)

return output


class SvNearestPointOnMeshNode(bpy.types.Node, SverchCustomTreeNode, SvRecursiveNode):
"""
Triggers: BVH Closest Point
Tooltip: Find nearest point on mesh surfaces
"""
bl_idname = 'SvNearestPointOnMeshNode'
bl_label = 'Nearest Point on Mesh'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_POINT_ON_MESH'

modes = [
("find_nearest", "Nearest", "", 0),
("find_nearest_range", "Nearest in range", "", 1),
]

def update_sockets(self, context):
self.inputs['Distance'].hide_safe = self.mode == 'find_nearest'
updateNode(self, context)

mode: EnumProperty(
name="Mode", items=modes,
default='find_nearest',
update=update_sockets)

safe_check: BoolProperty(
name='Safe Check',
description='When disabled polygon indices refering to unexisting points will crash Blender but makes node faster',
default=True)

flat_output: BoolProperty(
name='Flat Output',
description='Ouput a single list for every list in stead of a list of lists',
default=True,
update=updateNode)

def draw_buttons(self, context, layout):
layout.prop(self, 'mode')
if self.mode == 'find_nearest_range':
layout.prop(self, 'flat_output')

def draw_buttons_ext(self, context, layout):
layout.prop(self, 'list_match')
layout.prop(self, 'mode')
layout.prop(self, 'safe_check')

def sv_init(self, context):
si = self.inputs.new
so = self.outputs.new
si('SvVerticesSocket', 'Verts')
si('SvStringsSocket', 'Faces').nesting_level = 3
for s in self.inputs[:2]:
s.is_mandatory = True
si('SvVerticesSocket', 'Points').use_prop = True
d = si('SvStringsSocket', 'Distance')
d.use_prop = True
d.default_property = 10.0
d.hide_safe = True

so('SvVerticesSocket', 'Location')
so('SvVerticesSocket', 'Normal')
so('SvStringsSocket', 'Index')
so('SvStringsSocket', 'Distance')


def process_data(self, params):
verts, faces, points, distance = params
if self.mode == 'find_nearest':
return nearest_point_in_mesh(verts, faces, points,
safe_check=self.safe_check)
else:
return nearest_in_range(verts, faces, points, distance,
safe_check=self.safe_check,
flat_output=self.flat_output)

def register():
bpy.utils.register_class(SvNearestPointOnMeshNode)


def unregister():
bpy.utils.unregister_class(SvNearestPointOnMeshNode)
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SvBVHnearNewNode(bpy.types.Node, SverchCustomTreeNode):
bl_label = 'bvh_nearest'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_POINT_ON_MESH'

replacement_nodes =[('SvNearestPointOnMeshNode', None, None)]
modes = [
("find_nearest", "nearest", "", 0),
("find_nearest_range", "nearest in range", "", 1),
Expand Down
1 change: 0 additions & 1 deletion tests/docs_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ def test_node_docs_existance(self):
symmetrize.py
vd_attr_node_mk2.py
scalar_field_point.py
bvh_nearest_new.py
quads_to_nurbs.py
location.py
sun_position.py""".split("\n")
Expand Down
20 changes: 20 additions & 0 deletions utils/bvh_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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

from mathutils.bvhtree import BVHTree

def bvh_safe_check(verts, pols):
len_v = len(verts)
for p in pols:
for c in p:
if c > len_v:
raise Exception(f"Index {c} should be less than vertices length ({len_v})")

def bvh_tree_from_polygons(vertices, polygons, all_triangles=False, epsilon=0.0, safe_check=True):
if safe_check:
bvh_safe_check(vertices, polygons)
return BVHTree.FromPolygons(vertices, polygons, all_triangles=all_triangles, epsilon=epsilon)

0 comments on commit 82af1bf

Please sign in to comment.