I need to know the location of the selected point, edge, face in 3D space. How do I do that?
Here, this should work most of the time:
import bpy
import bmesh
bm = bmesh.new()
ob = bpy.context.active_object
bm = bmesh.from_edit_mesh(ob.data)
points = []
for v in bm.verts:
if (v.select == True):
obMat = ob.matrix_world
points.append(obMat @ v.co)
#Here, we multiply the vertex coordinate by the object's world matrix, in case the object is
# transformed.
#It is important to put the obMat before the v.co
# the @ (matrix multiplication) operator is NOT commutative.
for p in points:
pOb = bpy.data.objects.new("VertexPoint", None)
bpy.context.collection.objects.link(pOb)
pOb.location = p
#This for loop creates an empty at the location, useful for testing!
I’m not sure this is the fastest way to do this (for loop through all the verts seems bad to me) but it will work. If you need to optimize performance I can try to help with that, too.
Also note that this only works in edit mode! Since I assume you will want to run the script from edit mode.
Finally, if you need the script to work on a mesh with shape keys, you’ll need to do a bit more work. I won’t figure this out for now since I don’t think you need that.
Let me know if you want me to comment the above code more.
thank you my friend for the answer, it helped me a lot. And how can this be optimized for better speed? I do gizmo for, and speed is important to me, if there is a possibility of optimization then I would be grateful
If you’re using a gizmo, you may be able to use a modal operator to do this stuff in the setup phase (you can at least keep the bmesh around by doing this). After poking around, I found that what you need to find the selected vertices quickly is bm.select_history
. bm.select_history[-1]
will return the active element. If you loop through the list, it will return them in the reverse order of their selection. Be aware that this will give you any type of element (whichever is selected), faces, edges, vertices; so you’ll have to check which kind it is and handle each one a little differently.
If you want to center a gizmo on all of the selected elements, I recommend writing a function to find the center of the bounding box containing each element. This is where the gizmo is when you have the “median point” pivot point selected in Blender.
thank you very much, it works. But how would you implement the part associated with the allocation of edges and faces. I didn’t quite understand how to define a bounding box
obj = bpy.context.edit_object
bm = bmesh.from_edit_mesh(obj.data)
uniques = context.objects_in_mode_unique_data
bms = {}
for o in uniques:
bms[o] = bmesh.from_edit_mesh(o.data)
verts = []
for o in bms:
verts.extend([o.matrix_world @ v.co for v in bms[o].verts if v.select])
if len(bm.select_history) >= 1:
elem = bm.select_history[-1]
if isinstance(elem, bmesh.types.BMVert):
matrix_select = mathutils.Matrix.Translation(elem.co)
ob = obj.matrix_world @ matrix_select
elif isinstance(elem, bmesh.types.BMEdge):
matrix_select = mathutils.Matrix.Translation(elem.co)
ob = obj.matrix_world @ matrix_select
elif isinstance(elem, bmesh.types.BMFace):
matrix_select = mathutils.Matrix.Translation(elem.co)
ob = obj.matrix_world @ matrix_select
else:
obj_loc, obj_rot, obj_sca = bpy.context.active_object.matrix_world.decompose()
object_new_loc = mathutils.Matrix.Translation(sum(verts, mathutils.Vector()) / len(verts))
object_new_sca = mathutils.Matrix.Scale(obj_sca[0], 4, (1, 0, 0)) @ Matrix.Scale(obj_sca[1], 4, (0, 1, 0)) @ Matrix.Scale(obj_sca[2], 4, (0, 0, 1))
ob = object_new_loc @ obj_rot.to_matrix().to_4x4() @ object_new_sca
del obj_loc
Here is how I’ve done it in the past. NOTE: This code is from my addon ChainTools and is governed by the GPLv3 license. I’d also appreciate it if you note where you got the code from in your script if you reuse it (but you don’t have to).
#GPLv3
def BoundingBoxFromPoints(points):
"""Takes a list of points, returns a tuple containing the corner points
containting theminimum and maximum values in x, y, z directions"""
xMin, yMin, zMin = float( 'inf'),float( 'inf'),float( 'inf')
xMax, yMax, zMax = float('-inf'),float('-inf'),float('-inf')
for p in points:
if (p.x < xMin):
xMin = p.x
if (p.y < yMin):
yMin = p.y
if (p.z < zMin):
zMin = p.z
if (p.x > xMax):
xMax = p.x
if (p.y > yMax):
yMax = p.y
if (p.z > zMax):
zMax = p.z
return ( mathutils.Vector((xMin,yMin,zMin)), mathutils.Vector((xMax, yMax, zMax)) )
def BoundingBoxCenter(bb):
"""Takes a tuple containing a pair of point representing the corners of a
Bounding Box, returns the midpoint the line between the two points."""
return ( bb[0].lerp(bb[1], 0.5) )
All the function does is find the greatest and least x,y, and z values in the selection. It returns a pair of points representing the corners of the box that fits these values.
you mean this code for finding the center point of everything selecting?
I do it differently
uniques = context.objects_in_mode_unique_data
bms = {}
for obj in uniques:
bms[obj] = bmesh.from_edit_mesh(obj.data)
verts = []
for ob in bms:
verts.extend([ob.matrix_world @ v.co for v in bms[ob].verts if v.select])
obj_loc, obj_rot, obj_sca = bpy.context.active_object.matrix_world.decompose()
object_new_loc = mathutils.Matrix.Translation(sum(verts, mathutils.Vector()) / len(verts))
object_new_sca = mathutils.Matrix.Scale(obj_sca[0], 4, (1, 0, 0)) @ Matrix.Scale(obj_sca[1], 4, (0, 1, 0)) @ Matrix.Scale(obj_sca[2], 4, (0, 0, 1))
ob = object_new_loc @ obj_rot.to_matrix().to_4x4() @ object_new_sca
I currently having problems in finding the coordinate of the active edge and polygon. Can you help with that?
Add them together and then divide by the number of vertices
To find the bounding box center of selected mesh elements you can cheat by using the “Snap Cursor To Selected” while the Pivot Point is set to “Bounding Box Center”.
Then you get the 3D cursor matrix and multiply it by the object matrix and you have the bounding box center in world space. It should be faster.
thank you for the answer, but unfortunately I need to 3D cursor is not involved. I’m making a replacement for the standard gizmo.
I need not be… Take this code:
if len(verts) > 0:
old_cursor_loc = scene.cursor.location.copy()
bpy.ops.view3d.snap_cursor_to_selected()
scene.pdt_pivotloc = scene.cursor.location
scene.cursor.location = old_cursor_loc
return {"FINISHED"}
So , first your store the current cursor location using a copy of it. then use the snap_cursor_to_selected()
call to set cursor to this location, set something (your gizmo) to the cursor location as snapped to the geometry, then put the cursor back where it was, nobody will ever know what you did in the code as it all happens way too fast in Blender. - A good cheat!!!
Cheers, Clock.
Oh, thanks. this is a good cheat, I could not think of such an approach. this is good knowledge for me in learning blender programming
Hey man, any chance I could bug you for a call over an issue specific to this thread? Its for a startup project im doing with some friends. Hope to hear from you soon! All the best, Will