3
$\begingroup$

Let's say you have this cube here, with points A, B, C, and D, which are all on the same plane.

enter image description here

Then, let's say you have a plane that is perfectly co-planar with points A, B, C and D. It is a separate object that can be joined with the original object. Here is a visualization.

enter image description here

What I need to do, in Python ideally (but for the purpose of this question any solution would be good) is to select points A, B, C and D as they rest on the same plane as the second object. It should be possible, because they are perfectly co-planar.

There is also a previous question on here that asks how to select all vertices inside of a 3D object. It is similar to this one.

How do I select all vertices within a specific 3D area?


Also, even better, is if you could do the same for an object that is not a plane, but also has no volume, like this:

enter image description here

$\endgroup$

2 Answers 2

2
$\begingroup$

Select all co-planar verts in other edit meshes based on 3 selected verts of active mesh.

enter image description here

  • 3 selected verts form a plane. The coordinate of one and the cross product of vectors formed by two "edges" is the normal.

  • For all other meshes select their verts based on there distance from plane defined in step above.

  • mathutils.geometry.distance_point_to_plane is a utility function that returns the distance of a point to a plane.

  • The vert coordinates of other meshes are converted to the local space of active object to perform the test. Could instead use global coordinates. To make a local normal global see Python: vertex normal according to World

Test script. Select objects of interest and enter edit mode (2.8+). Select 3 verts of same object and run script. Any verts in the same plane (within tolerance) are selected in other meshes.

import bpy
import bmesh

from mathutils.geometry import distance_point_to_plane as dp2p

TOL = 1e-4

context = bpy.context

ob = context.edit_object
mw = ob.matrix_world
mwi = mw.inverted()
obs = [o for o in context.selected_objects if o.type == 'MESH']
obs.remove(ob)

bm = bmesh.from_edit_mesh(ob.data)

# using face normal
'''
f = bm.faces.active
plane_no = f.normal
plane_co = f.calc_center_median()
'''
# using 3 verts from select history
v1, v2, v3 = bm.select_history[-3:]
plane_co = v1.co
plane_no = (v1.co - v2.co).cross(v3.co - v2.co).normalized()
# should assert all verts and not inline.
for o in obs:
    bm = bmesh.from_edit_mesh(o.data)
    M = mwi @ o.matrix_world
    for v in bm.verts:
        v.select_set(
                abs(dp2p(M @ v.co, plane_co, plane_no)) <= TOL              )
    bmesh.update_edit_mesh(o.data)
$\endgroup$
4
  • $\begingroup$ Doesn't matter really, but could you avoid the repeated mwi @ in the loop by defining the plane in World Space, rather than in ob 's space, and comparing there, instead? $\endgroup$
    – Robin Betts
    Feb 9, 2021 at 8:35
  • 1
    $\begingroup$ For sure, my usual MO is globals, changed my mind and went local (instead of converting norm to global) . Edited: Much cheaper to only make convert to local space matrix once per object. $\endgroup$
    – batFINGER
    Feb 9, 2021 at 10:32
  • 1
    $\begingroup$ @RobinBetts leads to a question on error aggregation on multiple bm.transform(mw) .... select ... bm.transform(mwi) on bound edit meshes, Is a quick call. Have instead often made a copy of the bound edit mesh to transform and select back using indices of original. $\endgroup$
    – batFINGER
    Feb 9, 2021 at 10:43
  • $\begingroup$ From what I have tested this works perfectly. Thank you so, so much! I did a little reading about float points, and you can not have "true" perfection in terms of a point lying perfectly planar to another point; there is always some sort of inherent inaccuracy, correct? That I assume is why there is a tolerance there. Anyway, thank you for this! $\endgroup$ Feb 9, 2021 at 19:15
0
$\begingroup$

One way to do this would be via a vertex weight proximity technique.

Start by scaling plane up a billion-fold or whatever (about the median of its vertices would be fine), so that the face actually represents the plane, rather than the bounded plane (face.) Then, give the cube a new vertex group and assign all verts to it. Give the cube a vertex weight proximity modifier, targeting plane, referencing that vertex group, on geometry/face mode, with a highest of 0.0 and a lowest of 1E-31 (or whatever). Apply the modifier, then run a weight paint/clean operation if needed. Only verts that were within 1E-31 units of the plane remain assigned to that vertex group.

Clearly, with the x1 billion scale and the 1E-31 proximity modifier, this is valid only to a certain precision, but you can get any particular precision you'd like (and we're talking floating point values, so there's not really such thing as perfect precision anyways.)

I'm not sure that I understand your "even better" question, but if A, B and C are points on cube that you want to select on the basis of proximity to the triangular prism, I think you can see that you can use the same technique (obviously, don't scale the prism up a billion-fold.)

Not Python, but you indicated an openness to any technique, and of course all of the operations I mentioned should be perfectly scriptable.

Just to make sure you know, that the fact that a point exists on a plane does not mean that any faces that it is part of are co-planar with that plane. A might be on the plane while B, C and D are not.

(And if you're curious, how I'd handle the linked problem is by running a shrinkwrap->outside followed a vertex weight prox. I don't think it's the kind of answer the original asker was looking for, but it may be the kind of answer you're looking for.)

$\endgroup$
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .