GlowScript 2.
9 VPython
# Basic 3D Editor Tool
# A POC that vpython resources could be explored further and used to create a 3D
editor tool
# Development inprogress
# by Desmond Umebeh 7/5/2020
# Email: desmondumebeh@[Link]
#############################################Program
Classes#########################################################
# Editor class
class editor:
def __init__(self,objInstance):
self.GRID_TOTAL_LENGTH = 10
self.GRID_STEP_SIZE = 0.5
self.GRID_HALF_LENGTH = self.GRID_TOTAL_LENGTH/2
[Link] = self.GRID_HALF_LENGTH+self.GRID_STEP_SIZE
[Link] = [] # hold objects which makeup the grid
[Link] = 0.02*self.GRID_STEP_SIZE; #height
[Link] = 0.02*self.GRID_STEP_SIZE; #width
[Link] = canvas(background=[Link]) # Setup scene default
background color
[Link] = True # switch to enable scene background color
toggle
[Link] = False # snap to grid switch
[Link] = False # rotate switch
[Link] = False # object grouping switch
[Link] = None # track current mouse position on the
scene
[Link] = None # track last mouse position on the scene
[Link] = objInstance # hold an instance of class object3D
[Link] = None #Hold content of loaded file
[Link] = [] #Holds list of objects on the scene
#editor class method: Draw grid
def drawGrid(self):
for eachStraightLine_Back in range(-self.GRID_HALF_LENGTH, [Link],
self.GRID_STEP_SIZE):
[Link](box( pos=vector(eachStraightLine_Back,0,-
self.GRID_HALF_LENGTH),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
axis=vector(0,1,0), color =
[Link](.9),pickable = False)) #draw boxes and add to grindline list
for eachCrossedLine_Back in range(-self.GRID_HALF_LENGTH, [Link],
self.GRID_STEP_SIZE):
[Link](box( pos=vector(0,eachCrossedLine_Back,-
self.GRID_HALF_LENGTH),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
axis=vector(1,0,0), color =
[Link](.9),pickable = False)) #draw boxes and add to grindline list
####################################################show bottom
grid############################################################
for eachStraightLine_Bottom in range(-self.GRID_HALF_LENGTH,
[Link], self.GRID_STEP_SIZE):
[Link](box( pos=vector(eachStraightLine_Bottom,-
self.GRID_HALF_LENGTH,0),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
axis=vector(0,0,1), color =
[Link](.9),pickable = False)) #draw boxes and add to grindline list
for eachCrossedLine_Bottom in range(-self.GRID_HALF_LENGTH, [Link],
self.GRID_STEP_SIZE):
[Link](box( pos=vector(0,-
self.GRID_HALF_LENGTH,eachCrossedLine_Bottom),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
color = [Link](.9),pickable = False))
#draw boxes and add to grindline list
####################################################show right
grid#############################################################
for eachStraightLine_Right in range(-self.GRID_HALF_LENGTH, [Link],
self.GRID_STEP_SIZE):
[Link](box( pos=vector(self.GRID_HALF_LENGTH,0,eachStraightLine_Righ
t),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
axis=vector(0,1,0), color =
[Link](.9),pickable = False)) #draw boxes and add to grindline list
for eachCrossedLine_Bottom in range(-self.GRID_HALF_LENGTH, [Link],
self.GRID_STEP_SIZE):
[Link](box( pos=vector(self.GRID_HALF_LENGTH,eachCrossedLine_Bottom,
0),
size=vector(self.GRID_TOTAL_LENGTH,[Link],[Link]),
axis=vector(0,0,1), color =
[Link](.9),pickable = False)) #draw boxes and add to grindline list
#editor class method: setup editor initial scene
def setScene(self):
[Link]= 9.5; # Prevent scene autoscaling
[Link] = vec(-11.216, -1.99808e-16, 12.0396) #set camera
position
[Link] = vec(11.216, 1.99808e-16, -12.0396) #set camera
axis
#editor class method: draw 3D axes
def draw3DAxisLines(self):
xAxis = arrow(pos=vec(5,-5,-5), axis=vec(-12,0,0), shaftwidth = 0.05,
headwidth = .5, headlength =1, color=[Link],pickable = False)
#draw x-axis idicator
xAxisText = text(text='x', color=[Link], pos=vec(-6.5,-4.5,-5),
billboard = True) #x-axis label
yAxis = arrow(pos=vec(5,-5,-5), axis=vec(0,12,0), shaftwidth = 0.05,
headwidth = .5, headlength =1, color=[Link],pickable =
False) #draw y-axis indicator
yAxisText = text(text='y', color=[Link], pos=vec(4.2,6.5,-5),
billboard = True) #y-axis label
zAxis = arrow(pos=vec(5,-5,-5), axis=vec(0,0,12), shaftwidth = 0.05,
headwidth = .5, headlength =1, color=[Link],pickable =
False) #draw z-axis indicator
zAxisText = text(text='z', color=[Link], pos=vec(5.2,-4.2,6.5),
billboard = True) #z-axis label
#editor class method: implement snap to grid
def snap(self, objectPos):
objectPos.x = self.GRID_STEP_SIZE*round(objectPos.x/self.GRID_STEP_SIZE)
#modify x pos in steps of 0.5units
objectPos.y = self.GRID_STEP_SIZE*round(objectPos.y/self.GRID_STEP_SIZE)
#modify y pos in steps of 0.5units
objectPos.z = self.GRID_STEP_SIZE*round(objectPos.z/self.GRID_STEP_SIZE)
#modify z pos in steps of 0.5units
return objectPos # return snapped position
#editor class method: allow toggle of snap to grid switch
def checkSnap(self):
if ![Link]():
[Link] = True
else:
[Link] = False
#editor class method: get snap switch state
def isSnap(self):
return [Link]
#editor class method: get object grouping switch state
def isGroupEnable(self):
return [Link]
#editor class method: allow toggle of object grouping switch
def groupObj(self):
if ![Link]():
[Link] = True
else:
[Link] = False
#editor class method: check object boundaries within grid
def enableObjScope(self,object):
if ((object.x <= (self.GRID_HALF_LENGTH)) & (object.x >= -
(self.GRID_HALF_LENGTH)) & (object.y <= (self.GRID_HALF_LENGTH))
& (object.y >= -(self.GRID_HALF_LENGTH)) & (object.z <=
(self.GRID_HALF_LENGTH)) & (object.z >= -(self.GRID_HALF_LENGTH))): #limit object
scope to grid
return True
#editor class method: toggle editor scene color between day & night
def toggleScene(self):
if [Link]: #check if switch is set to true
[Link]= [Link] #change editor scene color to white
[Link] = False #change switch to false
[Link] ="Night" #change displayed text on widget to
"Night"
else: #otherwise
[Link]= [Link] #change editor scene color to black
[Link] = True #change switch to true
[Link]="Day" #change displayed text on widget to
"Day"
#editor class method: get current object picked by mouse pointer
def getCurrentObj(self):
theObj = [Link]
return theObj
#editor class method: get editor grid half length
def getGridHalfLength(self):
return self.GRID_HALF_LENGTH
#editor class method: create user selected object from menu widget
def createObj(self):
[Link]([Link]) #call create method of class
object3D to draw object
[Link] = 0 #reset widget menu
#editor class method: change selected object color
def changeColor(self):
if [Link]() != None: #check selected object, ignore if none
[Link]([Link](),[Link])#call objColor
method of class object3D to change to selected color
[Link] = 0 #reset widget menu
#editor class method: adjust sliders to match current object properties
def resetSliders(self):
if isinstance([Link](),sphere): #check if current object is
sphere
self.length_slider.value = 0 #reset length slider value to zero
self.height_slider.value = 0 #reset height slider value to zero
self.width_slider.value = 0 #reset width slider value to zero
self.length_slider.disabled = True #gray out length slider
self.height_slider.disabled = True #gray out height slider
self.width_slider.disabled = True #gray out width slider
self.radius_slider.disabled = False #enable radius slider
else:
self.radius_slider.value = 0 #reset radius slider value to zero
self.length_slider.disabled = False #enable length slider
self.height_slider.disabled = False #enable height slider
self.width_slider.disabled = False #enable width slider
self.radius_slider.disabled = True #gray out radius slider
#editor class method: adjust object dimesions to slider values
def radiusSlide(self):
[Link]().radius = self.radius_slider.value #set object radius
to radius_slider value
def lengthSlide(self):
[Link]().length = self.length_slider.value #set object length
to length_slider value
def heightSlide(self):
[Link]().height = self.height_slider.value #set object height
to height_slider value
def widthSlide(self):
[Link]().width = self.width_slider.value #set object width to
width_slider value
#editor class method: Adjust sliders to inhereit current object dimension
values
def getObjSliderSet(self):
self.radius_slider.value = [Link]().radius
self.length_slider.value = [Link]().length
self.width_slider.value = [Link]().width
self.height_slider.value = [Link]().height
self.opacity_slider.value = [Link]().opacity
#editor class method: toggle rotate switch
def checkRotate(self):
if ![Link]:
[Link] = True
else:
[Link] = False
#editor class method: get rotate switch state
def isRotate(self):
return [Link]
#editor class method: set last mouse position
def setLastMousePos(self):
[Link] = [Link](normal=vec(0,0,1)) #get
and project mouse position to xy plane when right click button pressed
#editor class method: object rotation
def rotateObj(self,object):
[Link] = [Link](normal=vec(0,0,1))
#project current mouse position onto a 2D plane
[Link] = [Link] - [Link] # defines
the resultant vector
# if ([Link]!= None):
[Link](angle=(-[Link])*0.1, axis=[Link](vec(0,0,1)))
#rotate object
[Link] = [Link] #update last mouse
position
#editor class method: Clear selected object
def clearObj(self):
# [Link]([Link]())
if [Link]() != None:
[Link]([Link]()) #delete current object
#editor class method: return mouse position
def getMousePosition(self):
return [Link]
#editor class method: object cloning
def cloneObj(self):
if [Link]() != None:
[Link]().clone(pos =vec(-2,0,0)) #clones current object.
#editor class method: modify current object opacity
def objOpacity(self):
if [Link]() != None:
[Link]().opacity = self.opacity_slider.value #set object
transparency to opacity slider value
#editor class method: File Operations
def readFile(self):
if [Link] == 1:
[Link] = 0 #reset menu
print_options(delete=True) #clear printing region
[Link] = read_local_file([Link].title_anchor) #trigger read
operation
print("-------------------------------------------------------------------")
print("File Name: "+[Link]) # The file name
print("File Size: "+[Link]+"kb") # File size in bytes
print("File Type: "+[Link]) # What kind of file
print("Creation Date: " + [Link]) # Creation date if
available
print("-------------------------------------------------------------------")
print([Link]) # The file contents
print("################ End #################")
if [Link] == 2:
[Link] = 0 #reset menu
xfile = winput(prompt ="Import JavaScript File",
type = "string", text = "Enter directory") #create
a widget input allowing user to enter file directory
self.get_library(xfile) #import javascript file
#editor class method: resused method, points to actual called method for each
widget
def clickThrough(self, thisWidget):
eval([Link])
#editor class method: class widgets
def widgetControl(self, editor_object):
[Link] = button(text="Day", pos=[Link].title_anchor,
bind=editor_object.clickThrough, editor_object=editor_object,
method="thisWidget.editor_object.toggleScene()") #Switch scene background color
[Link] = menu( choices=['Choose a 3D
object','Sphere','Box','Cylinder','Cone'], bind=editor_object.clickThrough,
editor_object=editor_object,
method="thisWidget.editor_object.createObj()") #Creates object menu, allows user
select oject to display
[Link].append_to_caption(' ')
[Link] = menu( choices=['Choose object color','Red', 'Green',
'Blue','default'],bind=editor_object.clickThrough,
editor_object=editor_object,
method="thisWidget.editor_object.changeColor()" )#Modify object color
[Link].append_to_caption(' ')
[Link] = checkbox(bind=editor_object.clickThrough, text='Snap to
grid',editor_object=editor_object,
method="thisWidget.editor_object.checkSnap()")#Enable snap to grid feature
[Link].append_to_caption(' ')
[Link] = checkbox(bind=editor_object.clickThrough,
text='Group Objects',editor_object=editor_object,
method="thisWidget.editor_object.groupObj()")#Trigger the compound object feature
[Link].append_to_caption(' ')
[Link] = checkbox(bind=editor_object.clickThrough,
text='Rotate Object',editor_object=editor_object,
method="thisWidget.editor_object.checkRotate()")#Enable rotation
[Link].append_to_caption('\n\n')
self.radius_slider = slider( bind=editor_object.clickThrough, min=0.1,
max=5,editor_object=editor_object,
method="thisWidget.editor_object.radiusSlide()")#Radius slider bar
[Link].append_to_caption('Radius\n')
self.length_slider = slider( bind=editor_object.clickThrough, min=0.1,
max=10,editor_object=editor_object,
method="thisWidget.editor_object.lengthSlide()")#Length slider bar
[Link].append_to_caption('Length ')
self.height_slider = slider( bind=editor_object.clickThrough , min=0.1,
max=10,editor_object=editor_object,
method="thisWidget.editor_object.heightSlide()")#Height slider bar
[Link].append_to_caption('Height ')
self.width_slider = slider( bind=editor_object.clickThrough, min=0.1,
max=10,editor_object=editor_object,
method="thisWidget.editor_object.widthSlide()")#Width slider bar
[Link].append_to_caption('Width\n\n')
self.opacity_slider = slider( bind=editor_object.clickThrough, min=0.2,
max=1,editor_object=editor_object,
method="thisWidget.editor_object.objOpacity()")#call method to adjust object
opacity
[Link].append_to_caption('Opacity\n\n')
[Link] = menu( pos =[Link].title_anchor, choices=['File
Operation','Load File', 'Import File'],bind=editor_object.clickThrough,
editor_object=editor_object,
method="thisWidget.editor_object.readFile()" )#File operation
[Link] = button(text="Clone Object",
bind=editor_object.clickThrough,
editor_object=editor_object,
method="thisWidget.editor_object.cloneObj()") #Trigger object cloning
[Link].append_to_caption(' ')
[Link] = button(text="Delete", bind=editor_object.clickThrough,
editor_object=editor_object,
method="thisWidget.editor_object.clearObj()") #call method to delete currrent
object
########################################################class
trackLine##########################################################################
#
class trackLine:
def __init__(self,editorInstance):
[Link] = editorInstance
[Link] = label( pos=[Link], text=[Link],
box = False,visible = False, opacity = 0)
#Define track lines on all 3 dimensional axis, not visible by default
[Link] = box(pos=vector(0,0,0),
size=vector([Link].GRID_HALF_LENGTH,5*[Link]
ht,
5*[Link]), color =
[Link], visible = False, axis = vector(1,0,0))
[Link] = box(pos=vector(0,0,0),
size=vector([Link].GRID_HALF_LENGTH,5*[Link]
ht,
5*[Link]), color =
[Link], visible = False, axis = vector(0,1,0))
[Link] = box(pos=vector(0,0,0),
size=vector([Link].GRID_HALF_LENGTH,5*[Link]
ht,
5*[Link]), color =
[Link], visible = False, axis = vector(0,0,1))
#trackLine class method: show track lines
def showTrackLine(self,obj):
[Link] = True #Turn on x-axis track line visibility
[Link] = True #Turn on y-axis track line visibility
[Link] = True #Turn on z-axis track line visibility
#Calculate and track object position along the x-axis
[Link].x = (([Link].GRID_HALF_LENGTH +
[Link].x)/2)
[Link].y = ([Link].y)
[Link].z = ([Link].z)
[Link].x = [Link].GRID_HALF_LENGTH - [Link].x
#Calculate and track object position along the y-axis
[Link].x = ([Link].x)
[Link].y = (-[Link].GRID_HALF_LENGTH +
[Link].y)/2
[Link].z = ([Link].z)
[Link].x = [Link].GRID_HALF_LENGTH + [Link].y
#Calculate and track object position along the z-axis
[Link].x = ([Link].x)
[Link].y = ([Link].y)
[Link].z = (-[Link].GRID_HALF_LENGTH +
[Link].z)/2
[Link].x = [Link].GRID_HALF_LENGTH + [Link].z
#trackLine class method: display object position label on scene
def showObjPos(self,obj):
[Link] = True
[Link] = [Link]
[Link].x = [Link].x
[Link].y = [Link].y
[Link].z = [Link].z
#trackLine class method: disable object track line
def removeTrackLine(self):
[Link] = False #Turn on x-axis track line visibility
[Link] = False #Turn on y-axis track line visibility
[Link] = False #Turn on z-axis track line visibility
#trackLine class method: disable object position label
def removeObjPos(self):
[Link] = False
############################################################Class
object3D##########################################################
class object3D:
def __init__ (self):
[Link] = 0.5 #default object radius
[Link] = 0.5 #default object length
[Link] = 0.5 #default object height
[Link] = 0.5 #default object width
#object3D class method: draw selected object
def create(self,index):
if index==1:
return sphere(pos=vec(0,0,0), radius = [Link], visible = True,
pickable = True)
if index==2:
return box(pos=vec(0,0,0), length=[Link],height=[Link],
width=[Link], visible = True, pickable = True) #display sphere on grid
if index==3:
return cylinder(pos=vec(0,0,0), radius = [Link], axis=
vec([Link],[Link],[Link]), visible = True, pickable = True)
#display
if index==4:
return cone(pos=vector(0,0,0),radius = [Link], axis=
vec([Link],[Link],[Link]), visible = True, pickable = True)
#display sphere on grid
#object3D class method: modify object color
def objColor(self,obj,index):
if index ==1:
[Link] = [Link] #set red
if index ==2:
[Link] = [Link] #set green
if index ==3:
[Link] = [Link] #set blue
if index == 4:
[Link] = vec(1,1,1) #set to default gray color
#object3D class method: delete object
def removeObj(self,obj):
[Link] = False
[Link] = False
del obj
#object3D class method: get object type
def objType(self,obj):
return type(obj)
######################################Program
Main########################################################
myObj3D = object3D() #create an instance of object3D class
thisEditor = editor(myObj3D) #instantiate an editor
[Link]() #create and setup scene
[Link]() #draw 3D grid on scene
thisEditor.draw3DAxisLines() #draw 3D lines on scene
[Link](thisEditor) #setup associated widgets
obj = None # object pointer
thisTrackLine = trackLine(thisEditor) #instantiate trackline
#Binding functions
[Link]("mousemove",movemoveActions) # Call function to modify curent
object position using the mouse
[Link]("mouseup",mouseupActions)
[Link]("mousedown", mousedownActions)
[Link]('keydown',keydownActions) # Call function to modify current
object position using the keyboard direction keys
[Link]('keyup',mouseupActions) #Call dragFalse to disable object
position modification.
def mousedownActions():
global obj #allow modification to be made to object pointer
obj = [Link]() #assign current object to object pointer
[Link]() #adjust sliders to match current object properties
[Link]() #Adjust sliders to inhereit current object
dimension values
[Link]() #get and project mouse position to xy plane when
user press right mouse button
if [Link](): #check if user enabled rotation
[Link]() #disable trackline on scene
else:
[Link](obj) #show current object position label
if [Link](): #checks if user enabled object grouping
if [Link]() != None: #check if an object is selected
[Link]([Link]()) #add user
selected object to object list for grouping
def movemoveActions():
global obj #allow modification to be made to object pointer
temp=[Link]() #temproary hold mouse position
if [Link](): #check if snap to grid is enabled by user
temp = [Link](temp) #if true, round mouse position to snap on grid
intersections
if [Link](temp): #check if mouse position falls within grid
defined boundaries
if [Link](): #check if user enabled rotation
[Link]() #disable trackline on scene if rotation is
enabled
[Link](obj) #rotate the object
else:
[Link]=temp #update object position to mouse position on the grid if
rotation is disabled
[Link](obj) #display object trackline
[Link](obj) #display object position label
def mouseupActions():
global obj #allow modification to be made to object pointer
[Link]() #remove object track lines
[Link](obj) #remove object position label
def keydownActions():
global obj #allow modification to be made to object pointer
if obj != None: #check if object is selected
temp = [Link] #assign object position to a temproary variable
dv = 0.05 #object move step value using keyboard
theKey = keysdown() #get the pressed key
if 'left' in theKey: #check if left directional key is pressed
temp.x-=dv #move object towards the left on thex-axis at step value of
0.05
if (temp.x < -[Link]()): #check grid boundaries
temp.x+=dv #if boundary value is surpassed modify to last value
within grid boundary
if 'right' in theKey: #check if right directional key is pressed
temp.x+=dv #move object towards the right on thex-axis at step value of
0.05
if (temp.x > [Link]()): #check grid boundaries
temp.x-=dv #if boundary value is surpassed modify to last value
within grid boundary
if 'alt' in theKey: #check if 'alt' key is pressed
if 'up' in theKey: #check if up directional key is pressed with the alt
key
temp.z-=dv #move object away from the user along z-axis at step
value of 0.05
if (temp.z < -[Link]()): #check grid
boundaries
temp.z+=dv #if boundary value is surpassed modify to last value
within grid boundary
if 'down' in theKey: #check if down directional key is pressed with the
alt key
temp.z+=dv #move object towards the user along z-axis at step value
of 0.05
if (temp.z > [Link]()): #check grid
boundaries
temp.z-=dv #if boundary value is surpassed modify to last value
within grid boundary
elif 'up' in theKey: #check if up directional key is pressed
temp.y+=dv #move object upwards on the y-axis at step value of 0.05
if (temp.y > [Link]()): #check grid boundaries
temp.y-=dv #if boundary value is surpassed modify to last value
within grid boundary
elif 'down' in theKey: #check if down directional key is pressed
temp.y-=dv #move object downwards on the y-axis at step value of 0.05
if (temp.y < -[Link]()): #check grid boundaries
temp.y+=dv #if boundary value is surpassed modify to last value
within grid boundary
# group objects in objList
if 'ctrl' in theKey: #check if ctrl key is pressed by user
compound([Link]) #group objects contained in list if
ctrl key is pressed
[Link] = [] # clear object list
#delete object if delete key is pressed
if 'delete' in theKey:
[Link](obj) #delete object
obj = None
[Link]() #disable trackline
[Link](obj) #disable object position
[Link]=temp #update object position
[Link](obj)#show 3D trackline
[Link](obj) #print object position label on scene