Featured post

new redirect for blender.org bpy docs.

http://www.blender.org/api/blender_python_api_current/ As of 10/11 november 2015 we can now link to the current api docs and not be worr...

Showing posts with label Coordinates. Show all posts
Showing posts with label Coordinates. Show all posts

June 16, 2013

Normalizing spline points. (make equidistant)

In college I had the opportunity to learn 3dsMax to a reasonable level. It is great in many ways, and one modifier was particularly nice: the normalize spline modifier. Normalize spline places the curve points at evenly spaced distances. If you don't know why that is awesome, you'll probably have to see it to understand.

Prompted by a post on blender.stackexchange i've started writing a blender version of the modifier, I don't foresee it to be a full blown replica but time permitting it will at least produce a replica polyline (edge mesh) with BGL overlaying points so interaction can help a user decide what distances the points should occur at.

more later.

preview code:

Note: This only draws the verts using BGL at their current locations. A nice short bgl snippet. https://gist.github.com/zeffii/5796615
.
Using Sverchok and Scripted Node : https://gist.github.com/zeffii/782e84fbdc8e8f23f64b

June 04, 2013

two ways to min/max an xyz coordinate list

August 12, 2011

Scripted Keyframe animation from imported mocha file

This will provide some insight into the pleasantly trivial, but still involved, nature of scripting keyframes for a spline object. Currently i only do basic 'POLY' form of spline. read the code here :

github.com/zeffii/AE--afterfx--Mocha-shape-import

August 11, 2011

scripted keyframing of Curve Coordinates

after poking around, it seems this is an ok way to script keyframes for a 'POLY' curve.
If you want NURBS or BEZIER, i suggest reading the rotobezier.py addon by zanqdo, available here, especially CURVE_OT_insert_keyframe


July 18, 2011

printing verts, face indices, and their normals

import bpy

current_obj = bpy.context.active_object

print("="*40) # printing marker
for polygon in current_obj.data.polygons:
    verts_in_face = polygon.vertices[:]
    print("face index", polygon.index)
    print("normal", polygon.normal)
    for vert in verts_in_face:
        print("vert", vert, " vert co", current_obj.data.vertices[vert].co)

July 13, 2011

OpenGL / bgl in blender 2.5

select two empties, run this code.

import bpy
import bgl
import blf
import bpy_extras

from mathutils import Vector
from bpy.props import StringProperty, FloatProperty
from bpy_extras import view3d_utils

'''
helper functions
'''

def get_objects(context):

sel_obs = context.selected_objects
names = [object.name for object in sel_obs if object.type=='EMPTY']
if len(names) == 2:
return names
else:
return None


def get_distance(names_of_empties):

if names_of_empties == None:
return 0.0

coordlist = []
for name in names_of_empties:
coordlist.append(bpy.data.objects[name].location)

return (coordlist[0]-coordlist[1]).length


def get_distance_from_context(context):
distance = get_distance(get_objects(context))
return distance


def get_coordinates_from_empties(object_list):
coordlist = [obj.location for obj in object_list]
return coordlist


def get_difference(axis, coord):

if axis == 'z':
return abs((coord[0]-coord[1]).z)
elif axis == 'y':
return abs((coord[0]-coord[1]).y)
elif axis == 'x':
return abs((coord[0]-coord[1]).x)
else:
return None


def return_sorted_coordlist(coords):
def MyFn(coord):
return coord.z
return sorted(coords, key=MyFn, reverse=True)


'''
openGL drawing
'''


def draw_text(col, y_pos, display_text, view_width, context):

# calculate text width, then draw
font_id = 0
blf.size(font_id, 18, 72) #fine tune

text_width, text_height = blf.dimensions(font_id, display_text)
right_align = view_width-text_width-18
blf.position(font_id, right_align, y_pos, 0)
blf.draw(font_id, display_text)
return


def draw_tetrahedron(region, rv3d, context, clist):

# highest point is apex
apex, baseco = return_sorted_coordlist(clist)

# define the base of the tetrahydron
base1 = Vector((apex.x, apex.y, baseco.z))
base2 = Vector((apex.x, baseco.y, baseco.z))
base3 = baseco

# converting to screen coordinates
screen_apex = view3d_utils.location_3d_to_region_2d(region, rv3d, apex)
screen_base1 = view3d_utils.location_3d_to_region_2d(region, rv3d, base1)
screen_base2 = view3d_utils.location_3d_to_region_2d(region, rv3d, base2)
screen_base3 = view3d_utils.location_3d_to_region_2d(region, rv3d, base3)

# bgl.glBegin(bgl.GL_LINE)

# colour + line setup, 50% alpha, 1 px width line
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(0.1, 0.3, 1.0, 0.8)
bgl.glLineWidth(1)

# from top to base coordinates
bgl.glColor4f(0.6, 0.6, 0.6, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base3)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base1)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.2)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_apex)
bgl.glVertex2f(*screen_base2)
bgl.glEnd()

# between base coordinates
bgl.glColor4f(1.0, 0.1, 0.1, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base3)
bgl.glVertex2f(*screen_base2)
bgl.glEnd()

bgl.glColor4f(0.0, 1.0, 0.1, 0.8)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base2)
bgl.glVertex2f(*screen_base1)
bgl.glEnd()

bgl.glColor4f(0.1, 0.3, 1.0, 0.2)
bgl.glBegin(bgl.GL_LINES)
bgl.glVertex2f(*screen_base1)
bgl.glVertex2f(*screen_base3)
bgl.glEnd()


return


def draw_callback_px(self, context):
rounding = 6

objlist = context.selected_objects
names_of_empties = [i.name for i in objlist]
distance_value = get_distance(names_of_empties)
coordinate_list = get_coordinates_from_empties(objlist)

region = bpy.context.region
rv3d = bpy.context.space_data.region_3d
view_width = context.region.width

# major rewrite candidate
l_distance = str(round(distance_value, rounding))
x_distance = round(get_difference('x', coordinate_list),rounding)
y_distance = round(get_difference('y', coordinate_list),rounding)
z_distance = round(get_difference('z', coordinate_list),rounding)
l_distance = str(l_distance)+" lin"
x_distance = str(x_distance)+" x"
y_distance = str(y_distance)+" y"
z_distance = str(z_distance)+" z"

y_heights = 88, 68, 48, 20
y_heights = [m-9 for m in y_heights] # fine tune

str_dist = x_distance, y_distance, z_distance, l_distance
for i in range(len(y_heights)):
draw_text(True, y_heights[i], str_dist[i], view_width, context)

# 50% alpha, 2 pixel width line
bgl.glEnable(bgl.GL_BLEND)

bgl.glColor4f(0.7, 0.7, 0.7, 0.5)
bgl.glLineWidth(1)

bgl.glBegin(bgl.GL_LINE_STRIP)
for coord in coordinate_list:
vector3d = (coord.x, coord.y, coord.z)
vector2d = view3d_utils.location_3d_to_region_2d(region, rv3d, vector3d)
bgl.glVertex2f(*vector2d)
bgl.glEnd()

draw_tetrahedron(region, rv3d, context, coordinate_list)

# restore opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)



'''
tool panel and button definitions
'''


class ToolPropsPanel(bpy.types.Panel):
bl_label = "Empties Calliper"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"

scn = bpy.types.Scene
ctx = bpy.context

@classmethod
def poll(self, context):
names_of_empties = get_objects(context)
if names_of_empties != None:
return True

def draw(self, context):

display_distance_field = False

layout = self.layout
scn = context.scene

names_of_empties = get_objects(context)
names_str = str(names_of_empties)

if names_of_empties != None:
display_distance_field = True
distance_value = get_distance(names_of_empties)


distance_value = str(distance_value)

# drawing
row1 = layout.row(align=True)
row1.operator("hello.hello", text=names_str)

if display_distance_field == True:
row3 = layout.row(align=True)
row3.label(distance_value)


class OBJECT_OT_HelloButton(bpy.types.Operator):
bl_idname = "hello.hello"
bl_label = "Say Hello"

def modal(self, context, event):
context.area.tag_redraw()

if event.type == 'MOUSEMOVE':
print("mouse moved")
elif event.type in ('RIGHTMOUSE', 'ESC'):
context.region.callback_remove(self._handle)
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.area.type == 'VIEW_3D':
context.window_manager.modal_handler_add(self)

# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self._handle = context.region.callback_add(
draw_callback_px,
(self, context),
'POST_PIXEL')

return {'RUNNING_MODAL'}
else:
self.report({'WARNING'},
"View3D not found, cannot run operator")

return {'CANCELLED'}



bpy.utils.register_module(__name__)



and

latest version (wed 13 july 2011):

i'll want something like

July 11, 2011

getting index of currently selected vertex ( or vertices)

this tells you the index of the currently selected vertex (or if more than one, then it returns the list of selected vertices)
# you must be in 'OBJECT' mode to run these.
>>> [i.index for i in bpy.context.active_object.data.vertices if i.select == True]
[5]
# or slightly shorter, when i.select returns False the element is discarded,
# but when i.select returns True, the if statement compares True with True. (overkill)
>>> [i.index for i in bpy.context.active_object.data.vertices if i.select]
[5]
# or use shorthand to access active_object (more pythonic ):
>>> current_object = bpy.context.active_object
>>> [i.index for i in current_object.data.vertices if i.select]
[5]

# to get a coordinate: (will return all selected vertices if more than one selected)
>>> [i.co for i in bpy.context.active_object.data.vertices if i.select]
[Vector((2.4389050006866455, -3.9095726013183594, 0.9682117700576782))]
Here is a similar snippet that works with bmesh and updates without requiring to enter/exit edit mode.

What about Faces?

Please see this post, it shows two ways.

June 27, 2011

randomizing geometry

this code randomizes all internal vertices on a surface of faces, it takes into account the shortest edge connected to each vertex. What it doesn't do is store the initial vertex position, so any consecutive randomize will random atop of the newly randomized coordinate

# must be in object mode
# with no verts selected on the object.

import bpy
import random
from mathutils import Vector

index_list = []
tracker_list = []

for i in bpy.context.active_object.data.edges:
index_list.append(i.vertices[0])
index_list.append(i.vertices[1])
tracker_list.append([i.vertices[0], i.vertices[1]])

# rudimentary numeric sorting, came in handy for debugging, but
# not essential for operational code.
sorted_list = sorted(index_list)

# use a set to eliminate multiples fast
sorted_set = set(sorted_list)

# track all verts with 4 or more other verts attached, store as list_internals
list_internals = []
for num in sorted_set:
if sorted_list.count(num) >= 4: # quad mesh only.
list_internals.append(num)

'''
# this selects all internal geometry.
for i in list_internals:
bpy.context.active_object.data.vertices[i].select = True
'''
print("="*20)

# store the length of the shortest connected edge, for each internal vertex
min_length_list = []
meshdata = bpy.context.active_object.data.vertices
for vert in list_internals:

# find each edge that uses this vert
# print(vert, "used in")
temp_storage = []
for pair in tracker_list:
if vert in pair:
vec1 = meshdata[pair[0]].co
vec2 = meshdata[pair[1]].co
dist = (vec1-vec2).length
temp_storage.append(dist)
# print(pair, "length is", dist)

shortest_attached_edge = min(temp_storage)
# print("shorted length is: ", shortest_attached_edge)

# store [vertnum, shortest_attached_edge]
min_length_list.append([vert, shortest_attached_edge])

for i in min_length_list:
print(i)

# randomize to .4 or shortest edge.
def randomize_vector(vector_in, amount):
amount = amount * 0.2
# random.uniform(0.7, 1.3)
vecx = random.uniform(vector_in.x-amount, vector_in.x+amount)
vecy = random.uniform(vector_in.y-amount, vector_in.y+amount)
vecz = random.uniform(vector_in.z-amount, vector_in.z+amount)

return Vector((vecx, vecy, vecz))

for i in min_length_list:
print(meshdata[i[0]].co, "randomize by ", 0.4*i[1] )
newvec = randomize_vector(meshdata[i[0]].co, 0.4*i[1])
print(newvec)
meshdata[i[0]].co = newvec


becomes

May 27, 2011

Scripted Animation using keyframe_insert

Inspired by a post on BA, it just so happens that i can use this for my own particle script. It's pretty basic but easy enough to elaborate on.

While this first solution will work, it is not optimal on massive datasets because it uses bpy.ops inside the positions loop. The short story about bpy.ops is that ops operations are really meant to be called from the UI directly, where you won't notice the slowdown (blender state refreshes) between clicks.

(using bpy.ops like in the following snippet is not recommended)
import bpy

start_pos = (0,0,0)
bpy.ops.mesh.primitive_uv_sphere_add(segments=32, size=0.3, location=start_pos)
bpy.ops.object.shade_smooth()

positions = (0,0,2),(0,1,2),(3,2,1),(3,4,1),(1,2,1)
frame_num = 0
for position in positions:
    bpy.context.scene.frame_set(frame_num)
    bpy.context.active_object.location = position
    bpy.ops.anim.keyframe_insert(type='Location', confirm_success=True)
    frame_num += 10
    

Here is a version that uses the data_path and avoids using bpy.ops in the loop ( recommended )

May 17, 2011

Blender 2.5x Python Curve from a List of Coordinates.

Given a list of coordinates in the form of Vector((x,y,z)) it is possible to string them together to get a curve shape. Each Point on a curve is has 4 values (x,y,z,w). W=weight and is a value between 0.010 and 100.

It took me a while to get some intuition in this part of blender bpy. This post is my effort to clear it up for myself and hopefully for others who may struggle with it in the future.
import bpy
from mathutils import Vector

w = 1 # weight
cList = [Vector((0,0,0)),Vector((1,0,0)),Vector((2,0,0)),Vector((2,3,0)),
        Vector((0,2,1))]

curvedata = bpy.data.curves.new(name='Curve', type='CURVE')
curvedata.dimensions = '3D'

objectdata = bpy.data.objects.new("ObjCurve", curvedata)
objectdata.location = (0,0,0) #object origin
bpy.context.scene.objects.link(objectdata)

polyline = curvedata.splines.new('POLY')
polyline.points.add(len(cList)-1)
for num in range(len(cList)):
    x, y, z = cList[num]
    polyline.points[num].co = (x, y, z, w)

or a slightly modified version, for turning it into a reusable function
import bpy
from mathutils import Vector

w = 1 # weight
listOfVectors = [Vector((0,0,0)),Vector((1,0,0)),Vector((2,0,0)),Vector((2,3,0)),
        Vector((0,2,1))]

def MakePolyLine(objname, curvename, cList):
    curvedata = bpy.data.curves.new(name=curvename, type='CURVE')
    curvedata.dimensions = '3D'

    objectdata = bpy.data.objects.new(objname, curvedata)
    objectdata.location = (0,0,0) #object origin
    bpy.context.scene.objects.link(objectdata)

    polyline = curvedata.splines.new('POLY')
    polyline.points.add(len(cList)-1)
    for num in range(len(cList)):
        x, y, z = cList[num]
        polyline.points[num].co = (x, y, z, w)

MakePolyLine("NameOfMyCurveObject", "NameOfMyCurve", listOfVectors)

If the list of vectors has only one Vector, then the curve will use one predefined zero vector (Vector((0,0,0))) and draw a straight line from 0,0,0 to your single Vector. Go into edit mode to see where the points are placed.

Each object can have multiple curves associated with it, they are accessed like this
# returns the number of curves associated with this object
>>> len(bpy.context.active_object.data.splines)

# returns the curve information associated with the first spline (index = 0) 
>>> bpy.context.active_object.data.splines[0]
bpy.data.curves["NameOfMyCurve"].splines[0]

# this returns the coordinates on that curve as a list.
>>> [point.co for point in bpy.context.active_object.data.splines[0].points]
if you want a smooth curve from these points, declare at the time of creation what type from these options ('POLY', 'BEZIER', 'BSPLINE', 'CARDINAL', 'NURBS')
import bpy  
from mathutils import Vector  
  
w = 1 # weight  
listOfVectors = [Vector((0,0,0)),Vector((1,0,0)),Vector((2,0,0)),Vector((2,3,0)),  
        Vector((0,2,1))]  
  
def MakePolyLine(objname, curvename, cList):  
    curvedata = bpy.data.curves.new(name=curvename, type='CURVE')  
    curvedata.dimensions = '3D'  
  
    objectdata = bpy.data.objects.new(objname, curvedata)  
    objectdata.location = (0,0,0) #object origin  
    bpy.context.scene.objects.link(objectdata)  
  
    polyline = curvedata.splines.new('NURBS')  
    polyline.points.add(len(cList)-1)  
    for num in range(len(cList)):  
        x, y, z = cList[num]  
        polyline.points[num].co = (x, y, z, w)  
  
    polyline.order_u = len(polyline.points)-1
    polyline.use_endpoint_u = True
    
  
MakePolyLine("NameOfMyCurveObject", "NameOfMyCurve", listOfVectors)
And here a more stripped down version
import bpy  
from mathutils import Vector  

# weight  
w = 1 

# we don't have to use the Vector() notation.  
listOfVectors = [(0,0,0),(1,0,0),(2,0,0),(2,3,0),(0,2,1)]  
  
def MakePolyLine(objname, curvename, cList):  
    curvedata = bpy.data.curves.new(name=curvename, type='CURVE')  
    curvedata.dimensions = '3D'  
  
    objectdata = bpy.data.objects.new(objname, curvedata)  
    objectdata.location = (0,0,0) #object origin  
    bpy.context.scene.objects.link(objectdata)  
  
    polyline = curvedata.splines.new('NURBS')  
    polyline.points.add(len(cList)-1)  
    for num in range(len(cList)):  
        polyline.points[num].co = (cList[num])+(w,)  
  
    polyline.order_u = len(polyline.points)-1
    polyline.use_endpoint_u = True
    
  
MakePolyLine("NameOfMyCurveObject", "NameOfMyCurve", listOfVectors)

May 14, 2011

Blender Python Printing Vertex Coordinates of selected Mesh

Below you will find the necessary code to get vertex information for yourself in a few easy steps. Running Blender in debug mode has index visualizer built in to allow you to see index information in realtime.
import bpy
for item in bpy.data.objects:
    
    print(item.name)
    if item.type == 'MESH':
        for vertex in item.data.vertices:
            print(vertex.co)
or
import bpy
for item in bpy.data.objects:
    
    print(item.name)
    if item.type == 'MESH':
        vert_list = [vertex.co for vertex in item.data.vertices]
        for vert in vert_list:
            print(vert)
we can go even further.. print faces(polygons), vertices, indices, face normals
# blender 2.6x
import bpy

current_obj = bpy.context.active_object

print("="*40) # printing marker
for face in current_obj.data.polygons:
    verts_in_face = face.vertices[:]
    print("face index", face.index)
    print("normal", face.normal)
    for vert in verts_in_face:
        print("vert", vert, " vert co", current_obj.data.vertices[vert].co)
if you need world coordinates, blenderscripting -- matrixworld will be a good place to find information. Compare the following snippet with the previous one.