Tuesday, July 16, 2013

Stick Figure Maker 1.0

Here's a VPython script that lets you click and drag to create stick figures in any position, so that you can take a screenshot and use them in test questions, etc. It's quick and dirty, but it should work for you, if you have VPython installed.
  • Drag any of the red handles to reposition - the head's not independently movable yet
  • Position the floor, if you need to
  • Click the toggle switch to hide the handles
  • Screenshot
  • Done!
There's a short YouTube video of the process in action here.

Some examples:
Walking
Running
Ninja Attack!
I borrowed the kernel of the drag-and-drop code from Sherwood/Chabay. Let me know if you find it useful!

The source:

from __future__ import division
from visual import *
from visual.controls import *  

def toggle_visible(scene):
    for obj in scene.objects:
        if obj.__class__ == sphere:
            if obj.radius == .5:
                obj.radius = .35
                obj.color = color.black
            elif obj.radius == .35:
                obj.radius = .5
                obj.color = color.red  

scene.background = color.white

# physical parameters
ULlen = 8
LLlen = 8
UAlen = 6
LAlen = 6
bodlen = 10
thickness = .3
# initial joint locations
CJ = vector(0,0,0)
LF = vector(-3,-15,0)
RF = vector(3,-15,0)
LK = CJ+(LF-CJ)/2
RK = CJ+(RF-CJ)/2
NJ = vector(0,bodlen,0)
LH = vector(-2,-2,0)
RH = vector(2,-2,0)
LE = NJ+(LH-NJ)/2
RE = NJ+(RH-NJ)/2

#objects: joints and limbs
ground = box(color = color.black, pos = (0,-16,0), height = 1, length = 50, width = 3)
leftUL = cylinder(color=color.black, pos = CJ, radius = thickness, axis = LK-CJ)
rightUL = cylinder(color=color.black, pos = CJ, radius = thickness, axis = RK-CJ)
leftK = sphere(color=color.red, radius = .5, pos = LK)
rightK = sphere(color=color.red, radius = .5, pos = RK)
leftLL = cylinder(color=color.black, pos = LK, radius = thickness, axis = LF-LK)
rightLL = cylinder(color=color.black, pos = RK, radius = thickness, axis = RF-RK)
leftF = sphere(color=color.red, radius = .5, pos = LF)
rightF = sphere(color=color.red, radius = .5, pos = RF)
body = cylinder(color=color.black, pos = CJ, radius = .05+thickness, axis = NJ-CJ)
leftUA = cylinder(color=color.black, pos = NJ, radius = thickness, axis = LE-NJ)
rightUA = cylinder(color=color.black, pos = NJ, radius = thickness, axis = RE-NJ)
leftE = sphere(color=color.red, radius = .5, pos = LE)
rightE = sphere(color=color.red, radius = .5, pos = RE)
leftLA = cylinder(color=color.black, pos = LE, radius = thickness, axis = LH-LE)
rightLA = cylinder(color=color.black, pos = RE, radius = thickness, axis = RH-RE)
leftH = sphere(color=color.red, radius = .5, pos = LH)
rightH = sphere(color=color.red, radius = .5, pos = RH)
C = sphere(color=color.black, radius = .36, pos = CJ)
neck = sphere(color=color.red, radius = .5, pos = NJ)

# toggle to show/hide handles
c = controls(x = 800, y=0, width = 300, height = 200, range = 100)
tR = toggle(pos = (-30,25), width = 10, height = 30, text0='',text1='Show positioning handles',
            action = lambda:toggle_visible(scene))

# head
R = 3.1
headpos = NJ+vector(0,R,0)
headshape = shapes.ellipse(thickness =.035, height =2*R, width = 4.7)
headpath = [headpos,headpos+(0,0,-.05)]
head = extrusion(pos=headpath, shape = headshape, color = color.black)

pick = None

# the loop: adjusts axes and positions based on the handles that you're dragging
while 1:
    rate(1000)
   
    if scene.mouse.events:
        m1 = scene.mouse.getevent() # obtain drag or drop event
        if m1.drag and m1.pick: #
            drag_pos = m1.pickpos # where on the ball the mouse was
            pick = m1.pick # pick is now True (nonzero)
            scene.cursor.visible = False # make cursor invisible
        elif m1.drop: # released the mouse button at end of drag
            pick = None # end dragging (None is False)
            scene.cursor.visible = True # cursor visible again
           
           
    if pick == ground:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            ground.pos.y = loc.y

    if pick == neck:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # neck motion
            neck.pos = (loc-CJ)*bodlen/mag(loc-CJ)
            body.axis = neck.pos-CJ
            head.pos = (bodlen+R)*(neck.pos-CJ)/mag(neck.pos-CJ)

            leftUA.axis = (leftE.pos-neck.pos)*UAlen/mag(leftE.pos-neck.pos)
            leftUA.pos = neck.pos
            leftE.pos = neck.pos + leftUA.axis
           
            leftLA.axis = (leftH.pos-leftE.pos)*LAlen/mag(leftH.pos-leftE.pos)
            leftLA.pos = leftE.pos
            leftH.pos = leftE.pos + leftLA.axis

            rightUA.axis = (rightE.pos-neck.pos)*UAlen/mag(rightE.pos-neck.pos)
            rightUA.pos = neck.pos
            rightE.pos = neck.pos + rightUA.axis
           
            rightLA.axis = (rightH.pos-rightE.pos)*LAlen/mag(rightH.pos-rightE.pos)
            rightLA.pos = rightE.pos
            rightH.pos = rightE.pos + rightLA.axis

            head.visible = False
            R = 3.1
            headpos = neck.pos*(bodlen+R)/bodlen
            headshape = shapes.ellipse(thickness =.035, height =2*R, width = 4.7, rotate = -atan(neck.pos.x/neck.pos.y))
            headpath = [headpos,headpos+(0,0,-.05)]
            head = extrusion(pos=headpath, shape = headshape, color = color.black)
            head.visible = True


    if pick == leftK:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # left knee motion
            leftK.pos = (loc-CJ)*ULlen/mag(loc-CJ)
            leftUL.axis = leftK.pos-CJ
            leftLL.axis = (leftF.pos-leftK.pos)*LLlen/mag(leftF.pos-leftK.pos)
            leftLL.pos = leftK.pos
            leftF.pos = leftK.pos + leftLL.axis
           
    if pick == rightK:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # right knee motion
            rightK.pos = (loc-CJ)*ULlen/mag(loc-CJ)
            rightUL.axis = rightK.pos-CJ
            rightLL.axis = (rightF.pos-rightK.pos)*LLlen/mag(rightF.pos-rightK.pos)
            rightLL.pos = rightK.pos
            rightF.pos = rightK.pos + rightLL.axis

    if pick == leftF:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # left foot motion
            leftLL.axis = (loc-leftK.pos)*LLlen/mag(loc-leftK.pos)
            leftF.pos = leftK.pos + leftLL.axis

    if pick == rightF:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # right foot motion
            rightLL.axis = (loc-rightK.pos)*LLlen/mag(loc-rightK.pos)
            rightF.pos = rightK.pos + rightLL.axis

    if pick == leftE:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # right elbow motion
            leftE.pos = neck.pos+(loc-neck.pos)*UAlen/mag(loc-neck.pos)
            leftUA.axis = leftE.pos-neck.pos
            leftLA.axis = (leftH.pos-leftE.pos)*LAlen/mag(leftH.pos-leftE.pos)
            leftLA.pos = leftE.pos
            leftH.pos = leftE.pos + leftLA.axis

    if pick == rightE:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # right elbow motion
            rightE.pos = neck.pos+(loc-neck.pos)*UAlen/mag(loc-neck.pos)
            rightUA.axis = rightE.pos-neck.pos
            rightLA.axis = (rightH.pos-rightE.pos)*LAlen/mag(rightH.pos-rightE.pos)
            rightLA.pos = rightE.pos
            rightH.pos = rightE.pos + rightLA.axis

    if pick == leftH:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # left hand motion
            leftLA.axis = (loc-leftE.pos)*LAlen/mag(loc-leftE.pos)
            leftH.pos = leftE.pos + leftLA.axis

    if pick == rightH:
        new_pos = scene.mouse.project(normal=(0,0,1)) # project onto xz plane
        if new_pos != drag_pos: # if the mouse has moved since last position
            loc = scene.mouse.pos
            # right hand motion
            rightLA.axis = (loc-rightE.pos)*LAlen/mag(loc-rightE.pos)
            rightH.pos = rightE.pos + rightLA.axis

No comments:

Post a Comment