nortikin / sverchok

Sverchok
http://nortikin.github.io/sverchok/
GNU General Public License v3.0
2.26k stars 234 forks source link

Random Points on Mesh node new functionalities #4434

Closed ArpegorPSGH closed 2 weeks ago

ArpegorPSGH commented 2 years ago

Could it be possible to add to the volumetric mode of Random Points on Mesh a parameterization via a scalar field, like in Popuplate Solid? It is just so slow to convert a mesh to a solid, and then back when you only need to perform one not-so-heavy operation.

portnov commented 2 years ago

Good idea :) It's just a question of someone finding time to implement.

ArpegorPSGH commented 2 years ago

So could you label this issue as a proposal?

zeffii commented 2 years ago

@ArpegorPSGH another approach (which may be faster than converting to Solid) is to use a combination of a Vector P Field node and a Point inside Mesh node.

https://github.com/nortikin/sverchok/blob/master/docs/nodes/analyzer/points_inside_mesh.rst

creating a moderately sized homogenousely spaced vector field is not necessarily a low computation operation, finding if points are inside or outside a mesh is more bruteforce but computation time will depend on complexity of shapemesh and point density of the field.

ArpegorPSGH commented 2 years ago

Well, I already circumvented it, but not elegantly, by generating my points inside a very simple mesh converted to solid, then filtrating with points inside mesh. However, I cannot know how many points I get in the end, I can just compare it to the target, and then regenerate more points to get closer. Being able to get the exact number of points in one pass is what I would like to do.

zeffii commented 2 years ago

Being able to get the exact number of points in one pass is what I would like to do

in all likelyhood the operation you describe is relatively heavy. To set a desired number of "inside mesh" points, that might involve an iterative process. (a complex volume will be heavier)

ArpegorPSGH commented 2 years ago

I don't think there is a need for multiple passes, as the node shouldn't start at one extremity of the volume, but instead just generate one point at a time, following the scalar field distribution limited by the mesh bounding box, then if the point is inside, it is accepted, if not it is rejected. So, each point is calculated only once.

zeffii commented 2 years ago

in that case i look forward to seeing the solution :)

portnov commented 2 years ago

The algorithm is not very simple, but not too complicated also. https://github.com/nortikin/sverchok/blob/6291c91cc9fb2b90330321262a05695a593471fd/utils/field/probe.py#L40

Points are generated in bunches, not one-by-one, in order to effectively use numpy vectorization, which is implemented in many scalar field classes.

ArpegorPSGH commented 2 years ago

Well, the principle is te same, but indeed using numpy great vectorization performance is more efficient.

zeffii commented 2 years ago

@portnov something like

"""
>in shape_v v
>in shape_f s
in count    s d=50 n=2
out verts   v
"""

from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.field.scalar import SvBvhAttractorScalarField
from sverchok.nodes.analyzer.bbox_mk3 import bounding_box
from sverchok.utils.sv_mesh_utils import point_inside_mesh
from sverchok.utils.bvh_tree import bvh_tree_from_polygons

for geom in zip(shape_v, shape_f):

    bbox = bounding_box([geom[0]], 
        box_dimensions='3D',
        output_verts=False, 
        output_mat=False, 
        output_mean=False, 
        output_limits=True)

    field = SvBvhAttractorScalarField(verts=geom[0], faces=geom[1]) # ?
    # bbox: nested tuple: ((min_x, min_y, min_z), (max_x, max_y, max_z))
    n_bbox = tuple(((bbox[4][0][0], bbox[5][0][0], bbox[6][0][0]), (bbox[7][0][0], bbox[8][0][0], bbox[9][0][0])))

    new_points, _ = field_random_probe(field, n_bbox, count) #,
        #    threshold=0, proportional=False, field_min=None, field_max=None,
        #    min_r=0, min_r_field=None,
        #    random_radius = False,
        #    seed=0, predicate=None)
    bvh = bvh_tree_from_polygons(geom[0], geom[1], all_triangles=False, epsilon=0.0, safe_check=True)
    points = [p for p in new_points if point_inside_mesh(bvh, p)]
    verts.append(points)

image

portnov commented 2 years ago

@zeffii you can pass lambda p: point_inside_mesh(bvh, p) as predicate parameter to field_random_probe. It will be a bit faster, as you remove another pass through all points. And obviously you can use any other scalar field.

zeffii commented 2 years ago

@portnov sweet :) cobbling this together was relatively painless.

zeffii commented 2 years ago

something like

"""
>in shape_v v
>in shape_f s
in count    s d=50 n=2
out verts   v
"""

from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.field.scalar import SvBvhAttractorScalarField
from sverchok.nodes.analyzer.bbox_mk3 import bounding_box
from sverchok.utils.sv_mesh_utils import point_inside_mesh
from sverchok.utils.bvh_tree import bvh_tree_from_polygons

for geom in zip(shape_v, shape_f):

    bbox = bounding_box(
        [geom[0]], 
        box_dimensions='3D', output_verts=False, output_mat=False, output_mean=False, 
        output_limits=True)

    co = lambda n: bbox[n][0][0] 
    #        ((min_x, min_y, min_z), (max_x, max_y, max_z))
    n_bbox = ((co(4), co(5), co(6)), (co(7), co(8), co(9)))

    field = SvBvhAttractorScalarField(verts=geom[0], faces=geom[1])
    bvh = bvh_tree_from_polygons(geom[0], geom[1], all_triangles=False, epsilon=0.0, safe_check=True)
    points, _ = field_random_probe(field, n_bbox, count, predicate=lambda p: point_inside_mesh(bvh, p))
    verts.append(points)
portnov commented 2 years ago

There is also bounding_box method in sverchok.utils.geom.

zeffii commented 2 years ago

ok. perfect. @portnov

"""
>in shape_v   v
>in shape_f   s
in count      s d=50 n=2
out out_verts v
"""
# - number of points are not guaranteed
# - points created as uniform random, not homogenous (reasonably equidistant from other points)

from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.sv_mesh_utils import point_inside_mesh as pim
from sverchok.utils.field.scalar import SvBvhAttractorScalarField as Field
from sverchok.utils.bvh_tree import bvh_tree_from_polygons as bvh_tree
from sverchok.utils.geom import bounding_box

for verts, faces in zip(shape_v, shape_f):

    bbox = bounding_box(verts)
    field = Field(verts=verts, faces=faces)
    bvh = bvh_tree(verts, faces, all_triangles=False, epsilon=0.0, safe_check=True)
    points, _ = field_random_probe(field, (bbox.min, bbox.max), count, predicate=lambda p: pim(bvh, p))
    out_verts.append(points)
zeffii commented 2 years ago

but isn't this kind of ...very much... like image

portnov commented 2 years ago

If you replace the scalar field with another one, the result can be very much unlike :)

"""
>in shape_v   v
>in shape_f   s
in field_in SF
in threshold_in s
in count      s d=50 n=2
out out_verts v
"""

from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.sv_mesh_utils import point_inside_mesh as pim
from sverchok.utils.bvh_tree import bvh_tree_from_polygons as bvh_tree
from sverchok.utils.geom import bounding_box

for verts, faces, field, threshold in zip(shape_v, shape_f, field_in, threshold_in):

    bbox = bounding_box(verts)
    bvh = bvh_tree(verts, faces, all_triangles=False, epsilon=0.0, safe_check=True)
    points, _ = field_random_probe(field,
                    (bbox.min, bbox.max), count,
                    threshold = threshold[0],
                    predicate=lambda p: pim(bvh, p))
    out_verts.append(points)

Screenshot_20220427_214426

portnov commented 2 years ago

yet another field, with the same script. Screenshot_20220427_214849 Screenshot_20220427_221139

zeffii commented 2 years ago

yes! i see. many possibilities

ArpegorPSGH commented 2 years ago

Great. Now could it be possible to add a distance parameter to set a minimum distance between points, and to merge that functionalities in the Random Points on Mesh Node?

zeffii commented 2 years ago

with a distance parameter, the "number of points" becomes a function of the minimum distance.

a merge by distance post processing step would achieve this ( as a cheap solution, otherwise the process is iterative and more computationally intense as i suggest above)

"""
>in shape_v      v
>in shape_f      s
in count         s d=50 n=2
in min_distance  s d=0.10 n=2
out out_verts    v
"""

import bmesh
from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.sv_mesh_utils import point_inside_mesh as pim
from sverchok.utils.field.scalar import SvBvhAttractorScalarField as Field
from sverchok.utils.bvh_tree import bvh_tree_from_polygons as bvh_tree
from sverchok.utils.geom import bounding_box

for verts, faces in zip(shape_v, shape_f):

    bbox = bounding_box(verts)
    field = Field(verts=verts, faces=faces)
    bvh = bvh_tree(verts, faces, all_triangles=False, epsilon=0.0, safe_check=True)
    points, _ = field_random_probe(field, (bbox.min, bbox.max), count, predicate=lambda p: pim(bvh, p))

    bm = bmesh_from_pydata(points, [], [])
    bm_verts = bm.verts
    bmesh.ops.remove_doubles(bm, verts=bm_verts, dist=min_distance)
    sparse_verts, _, _ = pydata_from_bmesh(bm)

    out_verts.append(sparse_verts or points)

image

ArpegorPSGH commented 2 years ago

Do you get the exact number wanted with that? Also, adding a min distance does not mean discarding the scalar field, it should still be used for generating points.

portnov commented 2 years ago

@zeffii In field_random_probe function, you can provide min_r or min_r_field parameter.

zeffii commented 2 years ago

Do you get the exact number wanted with that?

maybe i'm being thick here, but the exact number of points is a function of point distance and vice versa

zeffii commented 2 years ago

@portnov

"""
>in shape_v      v
>in shape_f      s
in count         s d=50 n=2
in min_distance  s d=0.10 n=2
out out_verts    v
"""

import bmesh
from sverchok.utils.field.probe import field_random_probe
from sverchok.utils.sv_mesh_utils import point_inside_mesh as pim
from sverchok.utils.field.scalar import SvBvhAttractorScalarField as Field
from sverchok.utils.bvh_tree import bvh_tree_from_polygons as bvh_tree
from sverchok.utils.geom import bounding_box

for verts, faces in zip(shape_v, shape_f):

    bbox = bounding_box(verts)
    field = Field(verts=verts, faces=faces)
    bvh = bvh_tree(verts, faces, all_triangles=False, epsilon=0.0, safe_check=True)
    points, _ = field_random_probe(field, (bbox.min, bbox.max), count, min_r=min_distance, predicate=lambda p: pim(bvh, p))

    out_verts.append(points)

nice. much simpler, but does quickly throw:

sverchok.utils.field.probe 86: Maximum number of iterations (1000) reached, stop.

portnov commented 2 years ago

Yeah, obviously, if you set high enough "minimal radius", it will be hard to find a place where you can put another point.

ArpegorPSGH commented 2 years ago

That error is not a problem, it just means your parameters are not realistic given your mesh. Of course, the minimal radius should be small compared to the size of your mesh and the number of points you want. It is just to not have too large a density of points where the scalar field is the highest.

zeffii commented 2 years ago

That error is not a problem, it just means your parameters are not realistic given your mesh.

naturally. exactly my point. it makes little sense to have two parameters (exposed to the user) affecting a system which are in essence eachother's reciprocal.

zeffii commented 2 years ago

i'm probably missing the point, but i'll bow out of this convo now :)

ArpegorPSGH commented 2 years ago

They are reciprocal parameters only if you consider homogeneous distribution or if you fill te entire mesh up to the maximal possible density. It definitely makes sense if you have a custom scalar field and just want to be sure you won't get any points too close to each other where the scalar field is the highest.

nortikin commented 2 years ago

image ahaha!!! open3d helping alot to see geometry

ArpegorPSGH commented 2 years ago

How does this help with our problematic?

enzyme69 commented 2 years ago

Why I cannot install open3D on mac... python mismatch?

Traceback (most recent call last):
  File "/Applications/BLENDER_31/Blender.app/Contents/Resources/3.1/scripts/modules/addon_utils.py", line 351, in enable
    mod = __import__(module_name)
  File "/Users/jimmygunawan/Library/Application Support/Blender/3.1/scripts/addons/sverchok-open3d-master/__init__.py", line 36, in <module>
    from sverchok_open3d import icons, settings, sockets, examples, menu
  File "/Users/jimmygunawan/Library/Application Support/Blender/3.1/scripts/addons/sverchok-open3d-master/settings.py", line 5, in <module>
    PYPATH = bpy.app.binary_path_python
AttributeError: 'bpy.app' object has no attribute 'binary_path_python'
Durman commented 2 years ago

@enzyme69 I think it's better to open separate issue

satabol commented 8 months ago

Hi @enzyme69 Can you update Sverchok-open3d addon? It works now.

portnov commented 2 weeks ago

I think this can be closed now, #5146 is merged.