[SOLVED]2.5 Return The Center Point Of A Triangle?

Hi All,

Does anyone know how to get the center point of a triangle?

I need a definition like this…


def returnCenterPoint(p1,p2,p3):
    #Code goes here.
    return x,y,z

There are actually a lot of definitions for center, but the simplest is just the average of the vertices.

you can also use obj.data.faces[i].center if it is face center you are looking for

I have posted a couple of questions about vertex parenting and I am looking for a way to emulate that because the current API has some problems working with my frameChange event.

@liero: I am actually using your triangle for every object script you posted a while back.
Consider this variation on your code.


import bpy
import mathutils
from mathutils import Vector

def fetchIfObject (passedName= ""):
    try:
        result = bpy.data.objects[passedName]
    except:
        result = None
    return result

def createEmpty(passedName):
    try:
        ob = bpy.data.objects.new(passedName, None)
        bpy.context.scene.objects.link(ob)
    except:
        ob = None
    return ob

def returnScene (passedIndex = 0):
    try:
        return bpy.context.scene
    except:
        return bpy.data.scenes[passedIndex]

############################################################################
# Code for vertex parenting objects to a generated mesh.
############################################################################ 
# Vertex parenting global variables.
vp_points = []
vp_faces = []
vp_objects = []
vp_D1 = Vector([0.06, 0.08, 0.0])
vp_D2 = Vector([0.06, -0.08, 0.0])
vp_D3 = Vector([-0.1, 0.0, 0.0])
vp_scale = 6

def returnTriangleForEachObject(passedNameList):
    global vp_points,vp_faces,vp_objects
    
    vp_points = []
    vp_faces = []
    vp_objects = []
    c = 0
    l = len(passedNameList)
    if l > 0:
        for name in passedNameList:
            ob = fetchIfObject(name)
            if ob != None:
                vp_objects.append(ob)
                dd = ob.location
                vp_points.append(dd+vp_D1*vp_scale)
                vp_points.append(dd+vp_D2*vp_scale)
                vp_points.append(dd+vp_D3*vp_scale)
                vp_faces.append([c,c+1,c+2])
                c+=3
    else:
        # If no list is passed assume single object at world origin.
        dd = Vector([0.0,0.0,0.0])
        vp_points.append(dd+vp_D1*vp_scale)
        vp_points.append(dd+vp_D2*vp_scale)
        vp_points.append(dd+vp_D3*vp_scale)
        vp_faces.append([c,c+1,c+2])        

    me = bpy.data.meshes.new('me_btVertexParent')
    me.from_pydata(vp_points,[],vp_faces)
    
    # Make sure all verts are deselected.
    for v in me.vertices:
        v.select = False
    me.update()
    return me

# Typically called after returnTriangleForEachObject returned the mesh, which is passed to here.
def vertexParentObjectsToMesh(passedNameList, passedMesh):
    if passedMesh != None:
        # Make sure all verts are deselected.
        for v in passedMesh.vertices:
            v.select = False
    for c in range(len(passedNameList)):
        ob = fetchIfObject(passedNameList[c])
        print("Vertex parenting [" +passedNameList[c]+ "] to  [" + passedMesh.name + "].")
        if ob != None:
            ob.parent = None
            ob.select = True
            for n in range(3):
                passedMesh.vertices[c*3+n].select = True
            bpy.ops.object.editmode_toggle()
            bpy.ops.object.vertex_parent_set()
            bpy.ops.object.editmode_toggle()
            for n in range(3):
                passedMesh.vertices[c*3+n].select = False
            ob.select = False
        else:
            print("WARNING: [" + passedNameList[c] +"] in vertex parent list, but not available?")

# Create a bunch of empties.
ob_list = []            
for n in range(8):
    name = "mt_" + str(n)
    ob_list.append(name)
    ob = fetchIfObject(name)
    if ob == None:
        ob = createEmpty(name)
    if ob != None:
        ob.parent = None
        ob.parent_type = 'OBJECT'
        ob.location.x = 2.5 * n
    else:
        print("Failed to create an Empty." + str(n))

# Create a mesh with a triangle located at the location of each empty.
ob_parent_name = "VertexParent"

me = returnTriangleForEachObject(ob_list)
ob_parent = fetchIfObject(ob_parent_name)
if ob_parent == None:
    # Need to create the parent object and link it to the new mesh.
    ob_parent = bpy.data.objects.new(ob_parent_name, me)
    localScene = returnScene()
    localScene.objects.link(ob_parent)
    ob_parent.hide_render = True
    ob_parent.draw_type = 'WIRE'
else:
    # Parent object already exists, so just re-assign the new mesh for the old one.
    ob_parent.data = me

# Now that we have ojbects and meshes, lets parent them together.
bpy.data.scenes[0].objects.active = ob_parent
vertexParentObjectsToMesh(ob_list,me)

It creates a line of empties and then uses the location for the empties to create the triangles for the mesh that they are vertex parent to. Now image that each of those empties could be a letter. This means you can apply a modifier to the VertexParent mesh and it will deform all the empties or letters. So I have all that working in my BlendText 2.5 script.

The problem I am trying to solve occurs when I begin to animate the spacing between my letters. This forces a re-valuation of that base mesh triangle spacing. Which requires a re-vertex parent to make sure they maintain the correct placement on the mesh. The current state of the API has some problems when I try to re-vertex parent, inside a scripted expression during an animation render frame change.

So I thought I would try to make my own vertex parenting solution by fetching the center of a triangle and managing the bind to the point via custom code.

The face center approach may work, however.

Attachments


animating the mesh and not the objects
@liero: With BlendText you will be able to do both. Your duplitext file is what inspired me to add this base mesh to the BlendText 2.5 rebuild. In the 2.49 version of BlendText, the parent object was an empty. Now it will be a mesh, so you can achieve the broad stroke duplitext style control from modifiers as well as detail animate the tracking, extrude, bevel, cap, etc… of the text.

Here is the above example converted to using your concept of the face center instead of vertex parenting.

Run the code, then apply a bend modifier to the VertexParent object that is created by the script.
Adjust the factor of the bend, then run the script again and the empties will re-align to the centers of each triangle in the VertexParent mesh.


 import bpy
import mathutils
from mathutils import Vector

def fetchIfObject (passedName= ""):
    try:
        result = bpy.data.objects[passedName]
    except:
        result = None
    return result

def createEmpty(passedName):
    try:
        ob = bpy.data.objects.new(passedName, None)
        bpy.context.scene.objects.link(ob)
    except:
        ob = None
    return ob

def returnScene (passedIndex = 0):
    try:
        return bpy.context.scene
    except:
        return bpy.data.scenes[passedIndex]

############################################################################
# Code for vertex parenting objects to a generated mesh.
############################################################################ 
# Vertex parenting global variables.
vp_points = []
vp_faces = []
vp_objects = []
vp_D1 = Vector([0.06, 0.08, 0.0])
vp_D2 = Vector([0.06, -0.08, 0.0])
vp_D3 = Vector([-0.1, 0.0, 0.0])
vp_scale = 6

def returnTriangleForEachObject(passedNameList):
    global vp_points,vp_faces,vp_objects
    
    vp_points = []
    vp_faces = []
    vp_objects = []
    c = 0
    l = len(passedNameList)
    if l > 0:
        for name in passedNameList:
            ob = fetchIfObject(name)
            if ob != None:
                vp_objects.append(ob)
                dd = ob.location
                vp_points.append(dd+vp_D1*vp_scale)
                vp_points.append(dd+vp_D2*vp_scale)
                vp_points.append(dd+vp_D3*vp_scale)
                vp_faces.append([c,c+1,c+2])
                c+=3
    else:
        # If no list is passed assume single object at world origin.
        dd = Vector([0.0,0.0,0.0])
        vp_points.append(dd+vp_D1*vp_scale)
        vp_points.append(dd+vp_D2*vp_scale)
        vp_points.append(dd+vp_D3*vp_scale)
        vp_faces.append([c,c+1,c+2])        

    me = bpy.data.meshes.new('me_btVertexParent')
    me.from_pydata(vp_points,[],vp_faces)
    
    # Make sure all verts are deselected.
    for v in me.vertices:
        v.select = False
    me.update()
    return me

'''
# Typically called after returnTriangleForEachObject returned the mesh, which is passed to here.
def vertexParentObjectsToMesh(passedNameList, passedMesh):
    if passedMesh != None:
        # Make sure all verts are deselected.
        for v in passedMesh.vertices:
            v.select = False
    for c in range(len(passedNameList)):
        ob = fetchIfObject(passedNameList[c])
        print("Vertex parenting [" +passedNameList[c]+ "] to  [" + passedMesh.name + "].")
        if ob != None:
            ob.parent = None
            ob.select = True
            for n in range(3):
                passedMesh.vertices[c*3+n].select = True
            bpy.ops.object.editmode_toggle()
            bpy.ops.object.vertex_parent_set()
            bpy.ops.object.editmode_toggle()
            for n in range(3):
                passedMesh.vertices[c*3+n].select = False
            ob.select = False
        else:
            print("WARNING: [" + passedNameList[c] +"] in vertex parent list, but not available?")
'''

def alignObjectsToMesh(passedNameList, passedMesh):
    #Assumes the length of the passedNameList is the same as the face count in the passedMesh.
    if passedMesh != None:
        c = 0
        faces = passedMesh.faces
        print("alignObjectsToMesh: Object Count:"+str(len(passedNameList))+", Face Count:" + str(len(faces))+".")
        for face in faces:
            ob = fetchIfObject(passedNameList[c])
            if ob != None:
                #Set the location of this object to the center of the face.
                print("#" + str(c) + " " + str(ob.location) + ", " + str(face.center) + ".")
                ob.location = face.center
            c = c + 1
            
# Create a bunch of empties and a list of their names.
ob_list = []            
for n in range(8):
    name = "mt_" + str(n)
    ob_list.append(name)
    ob = fetchIfObject(name)
    if ob == None:
        ob = createEmpty(name)
    if ob != None:
        ob.parent = None
        ob.parent_type = 'OBJECT'
        ob.location.x = 2.5 * n
    else:
        print("Failed to create an Empty." + str(n))

# Create a mesh with a triangle located at the location of each empty.
ob_parent_name = "VertexParent"

# Make a mesh with a triangle at the location of each object in the list.
me = returnTriangleForEachObject(ob_list)
ob_parent = fetchIfObject(ob_parent_name)
if ob_parent == None:
    # Need to create the parent object and link it to the new mesh.
    ob_parent = bpy.data.objects.new(ob_parent_name, me)
    localScene = returnScene()
    localScene.objects.link(ob_parent)
    ob_parent.hide_render = True
    ob_parent.draw_type = 'WIRE'
else:
    # Parent object already exists, so just re-assign the new mesh for the old one.
    ob_parent.data = me

# Now that we have a parent object, lets parent the list of empties to it.
for name in ob_list:
    ob = fetchIfObject(name)
    if ob != None:
        ob.parent = ob_parent

# Get the state of the mesh with modifiers applied.
me = ob_parent.to_mesh(returnScene(),True, 'RENDER')

# Now align the original empties to the position of the triangles in the parent mesh with modifiers applied.
alignObjectsToMesh(ob_list,me)

In BlendText, this style of loose binding will occur every time the frame changes.

I guess my problem now is that even though I have the location from the face center, I still need the orientation of the triangle?

Can that be derived from the 3 points?

Attachments


Thanks again, the rotation_mode code seems to work.

Here is the revised definition for the last code I posted.


def alignObjectsToMesh(passedNameList, passedMesh):
    #Assumes the length of the passedNameList is the same as the face count in the passedMesh.
    if passedMesh != None:
        c = 0
        faces = passedMesh.faces
        for face in faces:
            ob = fetchIfObject(passedNameList[c])
            if ob != None:
                #Set the location of this object to the center of the face.
                ob.location = face.center
                ob.rotation_mode = 'QUATERNION'
                ob.rotation_quaternion = face.normal.to_track_quat('-Y','Z')
                ob.rotation_mode = 'XYZ'
              c = c + 1

Here is the new code with a BlendText object being affected by the Simple Deform Curve Modifier and the Wave Modifier.