User Controls

From GeoMod

Jump to: navigation, search

The following code allows the use of buttons and slider bars. The built in VPython controls can be tricky to use and are not easy to label. These, hopefully, are more useful.

Contents

The code

called uControls.py in the examples. Download uControls.py

from visual import *
#see elipse.py for usage of slider

class slider:
    def __init__(self, pos=vector(0,0,0), 
                 min_val=0.0, max_val=1.0,
                 init_val=0.0,
                 stop_but= 0, stop_val=0.0,
                 scale=1.0,
                 title = None,
                 axis = vector(1,0,0)):
        '''
        pos = position of the slider bar
        min_val = minimum value on the slider bar
        max_val = maximum value on the slider bar
        init_val = initial value of slider bar (if beyond min_val or max_val is reset to min_val or max_val)
        stop_but = set to 0 for no stop button and to 1 for a stop button (not yet in)

        Note: The minimum y position of the slider is 0.0 and the maximum is self.scale
        '''
        self.pos = pos
        self.min_val = min_val
        self.max_val = max_val
        print min_val, max_val
        self.stop_val = 0.0
        self.scale = scale
        self.value = init_val
        self.title = title
        self.axis = axis

        '''confirm that the maximum is greater than the minimum'''
        if self.max_val <= self.min_val:
            print "Problem with slider. Minimum value greater than (or equal to) maximum value"

        self.bits = frame(pos=self.pos, axis=self.axis)
        self.cyl = cylinder(frame=self.bits, radius=0.05*self.scale, axis=(0,self.scale,0), color=color.yellow)
        self.but = sphere(frame=self.bits, pos=(0,self.get_y(), 0), radius = self.cyl.radius * 2.0, color = color.green)
        if self.title:
            self.title = label(frame=self.bits, pos=self.cyl.axis*1.2, text=self.title,height=10, box=0, xoffset=0, yoffset=0, space = 2)
        self.val_label = label(frame=self.bits, pos=self.cyl.axis*-0.2, text=str(round(self.get_val(),1)),height=10, box=0, xoffset=0, yoffset=0, space = 2)
        
        #adjust for boundary values
        self.boundary_set()

    def get_y(self):
        return self.scale * (self.value - self.min_val) / (self.max_val - self.min_val)

    def get_val(self):
        return self.min_val + (self.but.pos.y / self.scale)*(self.max_val - self.min_val)

    def set_value(self, val=None):
        if val:
            self.value = val
        self.but.pos = vector(0,self.get_y(),0)
        print "get_Y", self.value, self.get_y()

    def boundary_set(self):
        '''to keep values in bounds'''
        if self.value < self.min_val:
            self.value = self.min_val
            self.but.pos.y = 0.0
        if self.value > self.max_val:
            self.value = self.max_val
            self.but.pos.y = self.scale
            
    def but_move(self, cursor_pos):
        nx = 0.0 #self.bits.x
        ny = cursor_pos.y 
        nz = 0.0 #self.bits.z
        if ny < 0.0: #self.bits.y:
            ny = 0.0 #self.bits.y
            nv = self.min_val
        elif ny > self.cyl.axis.y: #self.bits.y + self.cyl.axis.y:
            ny = self.cyl.axis.y #self.bits.y + self.cyl.axis.y
            nv = self.max_val

        nv = self.min_val + ((ny) / self.scale)*(self.max_val - self.min_val)

        print ny, nv
        self.value = nv
        self.val_label.text = str(round(self.get_val(),1))

        return vector(nx, ny, nz)

class uSwitch:

    def __init__(self, radius=1.0,init_val=0, pos=vector(0,0,0), title=None):

        self.title = title
        self.value = init_val
        

        self.bits = frame(pos=pos)
        self.button = sphere(frame=self.bits, radius = radius, color = self.get_color())
        if title:
            self.title = label(frame=self.bits, pos=(0,radius*1.2,0),
                               text=self.title, height=10, box=0,
                               xoffset=0, yoffset=0, space = 2)
        self.val_label = label(frame=self.bits, pos=(0,-radius*1.2,0),
                               text=self.get_val(),height=10, box=0, xoffset=0, yoffset=0, space = 2)
        
    def switch(self):
        if self.value == 1:
            self.value = 0
            #self.button.color = color.red
        else:
            self.value = 1
            #self.button.color = color.blue
        self.val_label.text = self.get_val()
        self.button.color = self.get_color()
        #print self.get_val()

    def get_val(self):
        if self.value == 1:
            val = "On"
        else:
            val = "Off"
        return val

    def get_color(self):
        if self.value == 1:
            col = color.red
        else:
            col = color.blue
        return col

Useage

Usage is a bit tricky since you need to put code into two or three places

  1. create control
  2. get the user's interaction (mouse click for example)
  3. do operations based on changed values from the control

Example use of the slider

This example is of an eliptical orbit of a planet around the sun. The slider changes the elipticiy of the orbit. Note: The bits of code directly related to the control of the slider are bounded by;

####Control###################

####\Control###################


ellipse.py:

from visual import *
####Control###################
from uControls import *
####\Control###################

class orbits:

    def __init__(self):
        self.orbit_path = []
        self.planets = []
        


    def draw(self,e=0.0,rad=1.0, mu=10.0, planet_size=1.0, frate=10000000):
        self.orbit_path.append( curve(color=color.green))
        self.planets.append( sphere(color=(0.7,0.7,1),radius=planet_size))
        if e >= 1.0:
            e = 0.99
        if e < 0.0:
            e = 0.0
        fx = 2.0 - 2.0*min(abs((1-e*e)/(1-e)), abs(-(1-e*e)/(1+e)))
        self.planets[-1].fx = fx
        self.planets[-1].e = e
        self.planets[-1].rad = rad
        for i in range(361):
            rate(frate)
            theta = radians(float(i))
            #h_sq = a*(1-e*e)*mu
            r = (1-e*e)/(1-e*cos(theta))
            nx = rad*(r*cos(theta) - fx)
            ny = rad*r*sin(theta) 

            #print i, f, r, cos(theta), sin(theta), nx, ny
            
            self.orbit_path[-1].append(pos=(nx, ny, 0.0))
            self.planets[-1].pos=vector(nx,ny,0.0)

    def re_draw(self,e=0.0,rad=1.0,mu=10.0, frate=10000000):
        #redraw last path
        if e >= 1.0:
            e = 0.99
        if e < 0.0:
            e = 0.0
        fx = 2.0 - 2.0*min(abs((1-e*e)/(1-e)), abs(-(1-e*e)/(1+e)))
        self.planets[-1].fx = fx
        self.planets[-1].e = e
        self.planets[-1].rad = rad
        for i in range(361):
            rate(frate)
            theta = radians(float(i))
            #h_sq = a*(1-e*e)*mu
            r = (1-e*e)/(1-e*cos(theta))
            nx = rad*(r*cos(theta) - fx)
            ny = rad*r*sin(theta) 

            
            self.orbit_path[-1].pos[i]=vector(nx, ny, 0.0)
            


    def move_planets(self, theta):

        for i in self.planets:
            #to show the planet orbiting the Sun
            theta = radians(float(theta))
            r = (1-i.e*i.e)/(1-i.e*cos(theta))
            nx = i.rad*(r*cos(theta) - i.fx)
            ny = i.rad*r*sin(theta)
            i.pos = vector(nx,ny,0.0)



mu = 10.0 #gravitational acceleration
e = 0.6 #eccentricity
a = 10.0 #distance of sun from center of elipse

# to draw an elipse


sun = sphere(color=color.yellow)
#orbitor = sphere()

####Control###################
e_slide = slider(pos=vector(10,5,0), init_val=e, title="elipticity",scale=5.0)
####Control###################

orbs = orbits()
orbs.draw(e=e, rad=a,mu=mu,frate=100,planet_size=0.25)

scene.center = sun.pos
scene.autoscale = 0

runtime = 0.0
framerate = 60
pick = None
planet_angle = 0

while 1:
    rate(framerate)
    runtime += 1.0/framerate

####Control###################
    #mouse events
    if scene.mouse.events:
        m1 = scene.mouse.getevent() # obtain drag or drop event
        if m1.drag and (m1.pick == e_slide.but):
            drag_pos = m1.pickpos
            pick = m1.pick
            scene.cursor.visible = 0 # make cursor invisible
        elif m1.drop:
            pick = None # end dragging
            scene.cursor.visible = 1 # cursor visible

    if pick:
        new_pos = scene.mouse.project(normal=(0,0,1))
        if new_pos != drag_pos:
            pick.pos += new_pos - drag_pos
            drag_pos = new_pos
            if pick == e_slide.but:
                pick.pos = e_slide.but_move(pick.pos)
                e = e_slide.value   #so e changes when the value of the slider changes
                #print e
                #make adjustments based on new value of the slider
                orbs.re_draw(e=e, rad=a, mu=mu)
####\Control###################

    planet_angle += 1
    if planet_angle >= 361:
        planet_angle = 1
    orbs.move_planets(planet_angle)


Example use of switch button

The button switches from red to blue (on/off) and changes its value from 1 to 0.

stars_nebula3.py

from visual import *
from time import clock
from random import Random, random
####Control###################
from uControls import *
####\Control###################

# Stars interacting gravitationally
# Program uses Numeric Python arrays for high speed computations

win=600

Nstars = 50  # change this to have more or fewer stars

G = 6.7e-11 # Universal gravitational constant

# Typical values
Msun = 2E30
Rsun = 2E9
Rtrail = 2e8
L = 6e10 #1.2e11
vsun = 0.8*sqrt(G*Msun/Rsun)

print """
Right button drag to rotate view.
Left button drag up or down to move in or out.
"""
origx = 0
origy = 0
w = 704+4+4
h = 576+24+4
scene.width=w
scene.height=h
scene.x = origx
scene.y = origy

#setting up camera moves lurbano
maxRange = 2*L
minRange = 0.75*L
lastRange = maxRange
Rsteps = 200
start_steps = 400

scene = display(title="Stars", width=w, height=h, x = 0, y=0,
                range=lastRange, forward=(-1,-1,-1))
scene.autocenter = 0
##xaxis = curve(pos=[(0,0,0), (L,0,0)], color=(0.5,0.5,0.5))
##yaxis = curve(pos=[(0,0,0), (0,L,0)], color=(0.5,0.5,0.5))
##zaxis = curve(pos=[(0,0,0), (0,0,L)], color=(0.5,0.5,0.5))

Stars = []
colors = [color.red, color.green, color.blue,
          color.yellow, color.cyan, color.magenta]
poslist = []
plist = []
mlist = []
rlist = []

rv = Random(10)
print rv

for i in range(Nstars):
    x = -L+2*L*rv.random()
    y = -L+2*L*rv.random()
    z = -L+2*L*rv.random()
    r = Rsun/2+Rsun*rv.random()
    Stars = Stars+[sphere(pos=(x,y,z), radius=r, color=colors[i % 6])]
    #Stars[-1].trail = curve(pos=[Stars[-1].pos], color=colors[i % 6], radius=Rtrail)
    Stars[-1].trail = curve( color=colors[i % 6], radius=Rtrail)
    Stars[-1].alive = 1
    Stars[-1].showtrail = 0
    Stars[-1].vec = 0.0
    mass = Msun*r**3/Rsun**3
    px = mass*(-vsun+2*vsun*rv.random())
    py = mass*(-vsun+2*vsun*rv.random())
    pz = mass*(-vsun+2*vsun*rv.random())
    poslist.append((x,y,z))
    plist.append((px,py,pz))
    mlist.append(mass)
    rlist.append(r)

pos = array(poslist)
p = array(plist)
m = array(mlist)
m.shape = (Nstars,1) # Numeric Python: (1 by Nstars) vs. (Nstars by 1)
radius = array(rlist)

vcm = sum(p)/sum(m) # velocity of center of mass
p = p-m*vcm # make total initial momentum equal zero

####Control###################
#create control window - lurbano
cwin = display(title="Controls", width=200, height=100,
               x=0, y=h+24)
#button to show trails
trail_but = uSwitch(title="Trails")
#button for viewing from the perspective of the star
ride_but = uSwitch(title="Ride", pos=(2.5,0,0))
ride_star = -1
####\Control###################

t = 0.0
dt = 1000.0
Nsteps = 0
pos = pos+(p/m)*(dt/2.) # initial half-step
time = clock()
Nhits = 0

while 1:
    # Compute all forces on all stars
    r = pos-pos[:,NewAxis] # all pairs of star-to-star vectors
    for n in range(Nstars):
        r[n,n] = 1e6  # otherwise the self-forces are infinite
    rmag = sqrt(add.reduce(r*r,-1)) # star-to-star scalar distances
    hit = less_equal(rmag,radius+radius[:,NewAxis])-identity(Nstars)
    hitlist = sort(nonzero(hit.flat)) # 1,2 encoded as 1*Nstars+2
    
    F = G*m*m[:,NewAxis]*r/rmag[:,:,NewAxis]**3 # all force pairs
    for n in range(Nstars):
        F[n,n] = 0  # no self-forces
    p = p+sum(F,1)*dt

    # Having updated all momenta, now update all positions         
    pos = pos+(p/m)*dt

    # Update positions of display objects; add trail
    for i in range(Nstars):
        Stars[i].vec = Stars[i].pos - pos[i]
        Stars[i].pos = pos[i]
####Control###################
####the trail_but.value comes from the button class ###################
        if (Nsteps % 5 == 0 and
            trail_but.value == 1 and Stars[i].alive == 1) or Stars[i].showtrail == 1:
####\Control###################
            Stars[i].trail.append(pos=pos[i])
            Stars[i].trail.visible = 1

    # If any collisions took place, merge those stars
    for ij in hitlist:
        i, j = divmod(ij,Nstars) # decode star pair
        if not Stars[i].visible: continue
        if not Stars[j].visible: continue
        # m[i] is a one-element list, e.g. [6e30]
        # m[i,0] is an ordinary number, e.g. 6e30
        newpos = (pos[i]*m[i,0]+pos[j]*m[j,0])/(m[i,0]+m[j,0])
        newmass = m[i,0]+m[j,0]
        newp = p[i]+p[j]
        newradius = Rsun*((newmass/Msun)**(1./3.))
        iset, jset = i, j
        if radius[j] > radius[i]:
            iset, jset = j, i
        Stars[iset].radius = newradius
        m[iset,0] = newmass
        pos[iset] = newpos
        p[iset] = newp
        Stars[jset].trail.visible = 0
        Stars[jset].visible = 0
        p[jset] = vector(0,0,0)
        m[jset,0] = Msun*1E-30  # give it a tiny mass
        Nhits = Nhits+1
        pos[jset] = (10.*L*Nhits, 0, 0) # put it far away
        Stars[jset].alive = 0
        Stars[jset].showtrail = 0
        if jset == ride_star:
            ride_star = -1
        

    if Nsteps == 100:
        print '%3.1f seconds for %d steps with %d stars' % (clock()-time, Nsteps, Nstars)
    Nsteps = Nsteps+1
    t = t+dt
    rate(100)

####Control###################
    if cwin.mouse.events:
        m1 = cwin.mouse.getevent() # obtain drag or drop event
        if m1.pick == trail_but.button and m1.release:
            trail_but.switch()
            if trail_but.value == 0:
                for i in range(Nstars):
                    Stars[i].trail.visible = 0 #pos = Stars[i].trails.pos * 0.0
                    Stars[i].trail.pos=[]
                    
        if m1.pick == ride_but.button and m1.release:
            ride_but.switch()
            if ride_but.value == 0:
                scene.forward = vector(-1,-1,-1)
                scene.range = lastRange
                ride_star = -1
####\Control###################
                
                
    if scene.mouse.events:
        m1 = scene.mouse.getevent()
        nct = 0
        for i in Stars:
            if m1.pick == i and m1.release:
                if i.showtrail == 1:
                    i.showtrail = 0
                    i.trail.visible = 0
                    i.trail.pos=[]
                else:
                    i.showtrail = 1

####Control###################
                if ride_but.value == 1:
####\Control###################
                    ride_star = nct
            nct += 1

####Control###################
    if ride_but.value == 1 and ride_star <> -1:
####\Control###################
        scene.forward = -Stars[ride_star].pos
        lastRange = scene.range
        scene.range = 1.1 *mag(Stars[ride_star].pos)

Personal tools