Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
| Download
# physutil.py (v1.27)1# An open-source module for highly vpython animations23from __future__ import division4import unittest5import csv;6import sys;7from numpy import ndarray as numpy_ndarray;89"""10#11#12# UNIT TESTING / IMPORT SETUP CODE ------------------------------------------------------------13#14#15"""1617# Determine whether we are being used as a module or just running unittests (for mock purposes this is important)18if __name__ == "__main__":19# If we are unit testing, set up mock objects (must be done before classes are defined below!)20from vpython import vector21class Mock:22def __init__(self, name, *args, **kwargs):23self.name = name24self.called = 02526def __call__(self, *args, **kwargs):27self.args = args28self.kwargs = kwargs29for name in kwargs:30setattr(self, name, kwargs[name])31self.called += 132return self3334def reset(self):35self.called = 03637color = Mock("color")38color.red = "red"39color.green = "green"40color.blue = "blue"41color.yellow = "yellow"42color.orange = "orange"43color.cyan = "cyan"44color.magenta = "magenta"45color.white = "white"4647arrow = Mock("arrow")48label = Mock("label")49points = Mock("points")50curve = Mock("curve")51gdisplay = Mock("gdisplay")52gcurve = Mock("gcurve")53gcurve.plots = []54def mockPlot(pos):55gcurve.plots.append(pos)56gcurve.plot = mockPlot5758"""59vector = Mock("vector")60def call(x, y, z):61vector.x = x62vector.y = y63vector.z = z64vector.__call__ = call65"""6667else:68# These are the actual imports for the utility69from vpython import *70#from visual.graph import *717273"""74#75#76# ACTUAL PHYSUTIL CODE FOLLOWS --------------------------------------------------77#78#79"""8081# Initialize window positions for students (if we aren't unit testing)82if __name__ != "__main__":83scene.x = 5084scene.y = 508586# Helper function for returning proper size of something87def obj_size(obj):88if type(obj) == box or type(obj) == pyramid:89return obj.size90elif type(obj) == sphere:91return vector(obj.radius, obj.radius, obj.radius)9293class MotionMap:94"""95This class assists students in constructing motion maps96using either arrows (measuring a quantity) or "breadcrumbs"97(with timestamps).98"""99100def __init__(self, obj, tf, numMarkers, markerType="arrow",101markerScale=1, markerColor=color.red,102labelMarkerOrder=True, labelMarkerOffset=vector(0,0,0),103dropTime=False, timeOffset=vector(0,0,0), arrowOffset=vector(0,0,0), labelColor=color.white):104# MotionMap105# obj - object to track in mapping / placing markers106# tf - expected tFinal, used to space marker placement over time107# numMarkers - number of markers to place108# markerType - determines type of motionmap; options are "arrow" or "breadcrumbs"109# markerScale - replaces pSize / quantscale from motionmodule.py depending on type110# markerColor - color of markers111# labelMarkerOrder - drop numbers of markers?112# labelMarkerOffset - amount to offset numbering by113# dropTime - boolean determining whether a timestamp should be placed along with the marker114# timeOffset - if dropTime is True, determines the offset, if any, of the label from the marker115# arrowOffset - shift an arrow by an amount (x,y,z), useful for two arrows views116117self.obj = obj118self.tf = tf119self.numMarkers = numMarkers120self.markerType = markerType121self.markerScale = markerScale122self.markerColor = markerColor123self.labelMarkerOrder = labelMarkerOrder124self.labelMarkerOffset = labelMarkerOffset125self.timeOffset = timeOffset126self.dropTime = dropTime127self.arrowOffset = arrowOffset128self.labelColor = labelColor129130# Calculate size of interval for each step, set initial step index131try:132self.interval = self.tf / self.numMarkers133except TypeError as err:134print("**********TYPE ERROR**********")135print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")136print("******************************")137print(err)138raise err139self.curMarker = 0140141142def update(self, t, quantity=vector(0,0,0)):143try:144# Display new arrow if t has broken next threshold145if t > (self.interval * self.curMarker):146# Increment threshold147self.curMarker += 1148149# Display marker!150if self.markerType == "arrow":151arrow(pos=self.obj.pos+self.arrowOffset,152axis=self.markerScale*quantity, color=self.markerColor)153elif self.markerType == "breadcrumbs":154points(pos=self.obj.pos,155size=10*self.markerScale*quantity, color=self.markerColor)156157#Also display timestamp if requested158if self.dropTime is not False:159epsilon = vector(0,self.markerScale*.5,0)+self.timeOffset160droptimeText = label(pos=self.obj.pos+epsilon, text='t='+str(t)+'s', height=10, box=False, color=self.labelColor)161162# Same with order label163if self.labelMarkerOrder is not False:164label(pos=self.obj.pos-vector(0,self.markerScale*.5,0)+self.labelMarkerOffset, text=str(self.curMarker), height=10, box=False, color=self.labelColor)165except TypeError as err:166print("**********TYPE ERROR**********")167print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")168print("******************************")169print(err)170raise err171172class MotionMapN:173"""174This class assists students in constructing motion maps175using either arrows (measuring a quantity) or "breadcrumbs"176(with timestamps).177"""178179def __init__(self, obj, dt, numSteps, markerType="arrow",180markerScale=1, markerColor=color.red,181labelMarkerOrder=True, labelMarkerOffset=vector(0,0,0),182dropTime=False, timeOffset=vector(0,0,0), arrowOffset=vector(0,0,0), labelColor=color.white):183# MotionMapN184# obj - object to track in mapping / placing markers185# dt - time between steps186# numSteps - number of steps between markers187# markerType - determines type of motionmap; options are "arrow" or "breadcrumbs"188# markerScale - replaces pSize / quantscale from motionmodule.py depending on type189# markerColor - color of markers190# labelMarkerOrder - drop numbers of markers?191# labelMarkerOffset - amount to offset numbering by192# dropTime - boolean determining whether a timestamp should be placed along with the marker193# timeOffset - if dropTime is True, determines the offset, if any, of the label from the markers194# arrowOffset - shift an arrow by an amount (x,y,z), useful for two arrows views195196self.obj = obj197self.dt = dt198self.numSteps = numSteps199self.markerType = markerType200self.markerScale = markerScale201self.markerColor = markerColor202self.labelMarkerOrder = labelMarkerOrder203self.labelMarkerOffset = labelMarkerOffset204self.timeOffset = timeOffset205self.dropTime = dropTime206self.arrowOffset = arrowOffset207self.labelColor = labelColor208209# Calculate size of interval for each step, set initial step index210try:211self.interval = self.dt * self.numSteps212except TypeError as err:213print("**********TYPE ERROR**********")214print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")215print("******************************")216print(err)217raise err218self.curMarker = 0219220221def update(self, t, quantity=vector(0,0,0)):222try:223224threshold = self.interval * self.curMarker225# Display new arrow if t has broken new threshold226if t >= threshold:227228# Increment marker count229self.curMarker += 1230231# Display marker!232if self.markerType == "arrow":233arrow(pos=self.obj.pos+self.arrowOffset,234axis=self.markerScale*quantity, color=self.markerColor)235elif self.markerType == "breadcrumbs":236points(pos=self.obj.pos,237size=10*self.markerScale*quantity, color=self.markerColor)238239#Also display timestamp if requested240if self.dropTime is not False:241epsilon = vector(0,self.markerScale*.5,0)+self.timeOffset242droptimeText = label(pos=self.obj.pos+epsilon, text='t='+str(t)+'s', height=10, box=False, color=self.labelColor)243244# Same with order label245if self.labelMarkerOrder is not False:246label(pos=self.obj.pos-vector(0,self.markerScale*.5,0)+self.labelMarkerOffset, text=str(self.curMarker), height=10, box=False, color=self.labelColor)247248except TypeError as err:249print("**********TYPE ERROR**********")250print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")251print("******************************")252print(err)253raise err254255class PhysAxis:256"""257This class assists students in creating dynamic axes for their models.258"""259260def __init__(self, obj, numLabels, axisType="x", axis=vector(1,0,0), startPos=None,261length=None, labels = None, labelOrientation="down", axisColor=color.yellow, labelColor=color.white):262# PhysAxis263# obj - Object which axis is oriented based on by default264# numLabels - number of labels on axis265# axisType - sets whether this is a default axis of x or y, or an arbitrary axis266# axis - unit vector defining the orientation of the axis to be created IF axisType = "arbitrary"267# startPos - start position for the axis - defaults to (-obj_size(obj).x/2,-4*obj_size(obj).y,0)268# length - length of the axis - defaults to obj_size(obj).x269# labelOrientation - how labels are placed relative to axis markers - "up", "down", "left", or "right"270271try:272self.intervalMarkers = []273self.intervalLabels = []274self.labelText = labels275self.obj = obj276self.lastPos = vector(self.obj.pos.x, self.obj.pos.y, self.obj.pos.z)277self.numLabels = numLabels278self.axisType = axisType279self.axis = axis if axisType != "y" else vector(0,1,0)280self.length = length if (length is not None) else obj_size(obj).x281self.startPos = startPos if (startPos is not None) else vector(-obj_size(obj).x/2,-4*obj_size(obj).y,0) + self.obj.pos282self.axisColor = axisColor283self.labelColor = labelColor284285if labelOrientation == "down":286self.labelShift = vector(0,-0.05*self.length,0)287elif labelOrientation == "up":288self.labelShift = vector(0,0.05*self.length,0)289elif labelOrientation == "left":290self.labelShift = vector(-0.1*self.length,0,0)291elif labelOrientation == "right":292self.labelShift = vector(0.1*self.length,0,0)293294self.__reorient()295except TypeError as err:296print("**********TYPE ERROR**********")297print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")298print("******************************")299print(err)300raise err301302def update(self):303try:304# Determine if reference obj. has shifted since last update, if so shift us too305if self.obj.pos != self.lastPos:306diff = self.obj.pos - self.lastPos307308for i in range(len(self.intervalMarkers)):309self.intervalMarkers[i].pos += diff310self.intervalLabels[i].pos += diff311self.axisCurve.pos = [x + diff for x in self.axisCurve.pos]312313self.lastPos = vector(self.obj.pos.x, self.obj.pos.y, self.obj.pos.z)314except TypeError as err:315print("**********TYPE ERROR**********")316print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")317print("******************************")318print(err)319raise err320321def reorient(self, axis=None, startPos=None, length=None, labels=None, labelOrientation=None):322try:323# Determine which, if any, parameters are being modified324self.axis = axis if axis is not None else self.axis325self.startPos = startPos if startPos is not None else self.startPos326self.length = length if length is not None else self.length327self.labelText = labels if labels is not None else self.labels328329# Re-do label orientation as well, if it has been set330if labelOrientation == "down":331self.labelShift = vector(0,-0.05*self.length,0)332elif labelOrientation == "up":333self.labelShift = vector(0,0.05*self.length,0)334elif labelOrientation == "left":335self.labelShift = vector(-0.1*self.length,0,0)336elif labelOrientation == "right":337self.labelShift = vector(0.1*self.length,0,0)338339self.__reorient()340except TypeError as err:341print("**********TYPE ERROR**********")342print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")343print("******************************")344print(err)345raise err346347def __reorient(self):348# Actual internal axis setup code... determines first whether we are creating or updating349updating = True if len(self.intervalMarkers) > 0 else False350351# Then determines the endpoint of the axis and the interval352final = self.startPos + (self.length * self.axis)353interval = (self.length / (self.numLabels-1)) * self.axis354355# Loop for each interval marker, setting up or updating the markers and labels356i=0357while i<self.numLabels:358intervalPos = self.startPos+(i*interval)359360# Determine text for this label361if self.labelText is not None:362labelText = self.labelText[i]363elif self.axisType == "y":364labelText = "%.2f" % intervalPos.y365else:366labelText = "%.2f" % intervalPos.x367368if updating:369self.intervalMarkers[i].pos = intervalPos370self.intervalLabels[i].pos = intervalPos+self.labelShift371self.intervalLabels[i].text = str(labelText)372else:373self.intervalMarkers.append(374points(pos=intervalPos,color=self.axisColor,size = 6) )375self.intervalLabels.append(376label(pos=intervalPos+self.labelShift, text=str(labelText),box=False,height = 8, color=self.labelColor) )377i=i+1378379# Finally, create / update the line itself!380if updating:381self.axisCurve.pos = [self.startPos,final]382else:383self.axisCurve = curve(pos=[self.startPos,final],color = self.axisColor)384385class PhysTimer:386"""387This class assists students in creating an onscreen timer display.388"""389390def __init__(self, x, y, fontsize = 13, useScientific=False, timerColor=color.white):391392# PhysTimer393# x,y - world coordinates for the timer location394# fontsize - size of font for timer text395# useScientific - bool to turn off/on scientific notation for time396# timerColor - attribute controlling the color of the text397398try:399self.useScientific = useScientific400self.timerColor = timerColor401if useScientific is False:402self.timerLabel = label(pos=vector(x,y,0), text='00:00:00.00', box=False, height = fontsize)403else:404self.timerLabel = label(pos=vector(x,y,0), text='00E01', box=False, height = fontsize)405except TypeError as err:406print("**********TYPE ERROR**********")407print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")408print("******************************")409print(err)410raise err411412def update(self, t):413try:414# Basically just use sprintf formatting according to either stopwatch or scientific notation415if self.useScientific:416self.timerLabel.text = "%.4E" % t417else:418hours = int(t / 3600)419mins = int((t / 60) % 60)420secs = int(t % 60)421frac = int(round(100 * (t % 1)))422if frac == 100:423frac = 0424secs = secs + 1;425self.timerLabel.text = "%02d:%02d:%02d.%02d" % (hours, mins, secs, frac)426except TypeError as err:427print("**********TYPE ERROR**********")428print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")429print("******************************")430print(err)431raise err432433class PhysGraph:434"""435This class assists students in creating graphs with advanced functionality.436"""437438# Static, pre-determined list of colors from which each line will be generated439graphColors = [color.red, color.green, color.blue, color.yellow,440color.orange, color.cyan, color.magenta, color.white]441442def __init__(self, numPlots=1, title = None, xlabel = None, ylabel = None, backgroundColor = color.white):443444# title - sets window title445# xlabel - sets label on the horizontal axis446# ylabel - sets label on the vertical axis447# backgroundColor - sets background color of graph448449try:450# Create our specific graph window451self.graphDisplay = graph(x = 475, y = 350, title = title, xtitle = xlabel, ytitle = ylabel, background = backgroundColor)452453self.numPlots = numPlots454455# Initialize each plot curve456self.graphs = []457for i in range(numPlots):458self.graphs.append(gcurve(color=PhysGraph.graphColors[i%len(PhysGraph.graphColors)]))459except TypeError as err:460print("**********TYPE ERROR**********")461print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")462print("******************************")463print(err)464raise err465466def plot(self, independent, *dependents):467try:468if len(dependents) != self.numPlots:469raise Exception("ERROR: Number of dependent parameters given does not match numPlots given at initialization!")470471# Plot each line based on its parameter!472for i in range(len(dependents)):473self.graphs[i].plot(pos=(independent,dependents[i]))474except TypeError as err:475print("**********TYPE ERROR**********")476print("Please check that you are not passing in a variable of the wrong type (e.g. a scalar as a vector, or vice-versa)!")477print("******************************")478print(err)479raise err480481#########################################################################################482## CSV Functions readcsv(), writecsv()483def readcsv(filename,cols=1, IgnoreHeader=False, startrow = 0, NumericData=True):484data = [0]*(cols);485for i in range(cols):486data[i]=[];487if sys.version_info.major == 2:488with open(filename,'rb') as csvfile: #open the file, and iterate over its data489csvdata = csv.reader(csvfile); #tell python that the file is a csv490for i in range(0,startrow): #skip to the startrow491csvdata.next();492if IgnoreHeader and startrow!=0:493csvdata.next(); #if ignoring header, advance one row494for row in csvdata: #iterate over the rows in the csv495#Assign the cols of each row to a variable496for c in range(cols): #read in the text values as floats in the array497if NumericData:498data[c].append(float(row[c]));499else:500data[c].append(row[c]);501elif sys.version_info.major == 3:502with open(filename,newline='') as csvfile: #open the file, and iterate over its data503csvdata = csv.reader(csvfile); #tell python that the file is a csv504for i in range(0,startrow): #skip to the startrow505csvdata.next();506if ignoreHeader and startrow!=0:507csvdata.next(); #if ignoring header, advance one row508for row in csvdata: #iterate over the rows in the csv509#Assign the cols of each row to a variable510for c in range(cols): #read in the text values as floats in the array511if NumericData:512data[c].append(float(row[c]));513else:514data[c].append(row[c]);515else:516sys.stderr.write('You need to use python 2* or 3* \n');517exit(1);518return data;519520def writecsv(filename,datalist, header=[]):521csvfile = [];522useheader = False;523#make sure we have the correct versions of python524if sys.version_info.major == 2:525csvfile = open(filename,'wb');526elif sys.version_info.major == 3:527csvfile = open('pythonTest.csv', 'w',newline='');528else:529sys.stderr.write('You need to use python 2* or 3* \n');530exit(1);531532#if user passed a numpy array, convert it533if isinstance(datalist,numpy_ndarray):534datalist = datalist.T;535datalist = datalist.tolist();536#if there is no data, close the file537if len(datalist)<1:538csvfile.close();539return;540#check to see if datalist is a single list or list of lists541isLofL = False;542ListLength = 0;543numLists = 0;544if isinstance(datalist[0],(list,tuple)): #check the first element in datalist545isLofL = True;546ListLength = len(datalist[0]);547numLists = len(datalist);548else:549isLofL = False;550ListLength = len(datalist);551numLists = 1;552#if a list then make sure everything is the same length553if isLofL:554for Lidx in range(1,len(datalist)):555if len(datalist[Lidx])!=ListLength:556sys.stderr.write('All lists in datalist must be the same length \n');557csvfile.close();558return;559#if header is present, make sure it is the same length as the number of cols560if len(header)!=0:561if len(header)!=numLists:562sys.stderr.write('Header length did not match the number of columns, ignoring header.\n');563else:564useheader = True;565566#now that we've checked the inputs, loop and write outputs567DataWriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) # Create writer object568if useheader:569DataWriter.writerow(header);570for row in range(0,ListLength):571thisrow = [];572if numLists > 1:573for col in range(0,numLists):574thisrow.append(datalist[col][row]);575else:576thisrow.append(datalist[row]);577578DataWriter.writerow(thisrow);579580#close the csv file to save581csvfile.close();582## END CSV Functions583#########################################################################################584585"""586#587#588# UNIT TESTING BELOW ----------------------------------------------------------------------589#590#591"""592593class TestMotionMap(unittest.TestCase):594def setUp(self):595self.obj = Mock("obj")596self.obj.pos = vector(0,0,0)597self.tf = 10598self.numMarkers = 5599self.timeOffset = vector(1,1,1)600self.markerScale = 2601self.arrowOffset = vector(1,1,1)602603arrow.reset()604points.reset()605label.reset()606607self.map = MotionMap(self.obj, self.tf, self.numMarkers, markerType="arrow",608markerScale=2, markerColor=color.green,609dropTime=True, timeOffset=self.timeOffset, arrowOffset=self.arrowOffset)610611def test_init(self):612self.assertEqual(self.obj, self.map.obj)613self.assertEqual(self.tf, self.map.tf)614self.assertEqual(self.numMarkers, self.map.numMarkers)615self.assertEqual("arrow", self.map.markerType)616self.assertEqual(self.markerScale, self.map.markerScale)617self.assertEqual(color.green, self.map.markerColor)618self.assertEqual(vector(1,1,1), self.map.timeOffset)619self.assertEqual(True, self.map.dropTime)620self.assertEqual(self.map.interval, self.tf / self.numMarkers)621self.assertEqual(self.map.curMarker, 0)622self.assertEqual(vector(1,1,1), self.map.arrowOffset)623624625def test_update(self):626self.map.curMarker = 1627628self.map.update(0)629self.assertEqual(arrow.called, 0)630self.assertEqual(points.called, 0)631self.assertEqual(label.called, 0)632633self.map.update(3, quantity=2)634self.assertEqual(arrow.called, 1)635self.assertEqual(points.called, 0)636self.assertEqual(label.called, 2)637self.assertEqual(self.map.curMarker, 2)638self.assertEqual(arrow.pos, self.obj.pos+self.arrowOffset)639self.assertEqual(arrow.axis, 4)640self.assertEqual(arrow.color, color.green)641self.assertEqual(label.text, "2")642643class TestMotionMapN(unittest.TestCase):644def setUp(self):645self.obj = Mock("obj")646self.obj.pos = vector(0,0,0)647self.dt = 1648self.numSteps = 5649self.timeOffset = vector(1,1,1)650self.markerScale = 2651self.arrowOffset = vector(1,1,1)652653arrow.reset()654points.reset()655label.reset()656657self.map = MotionMapN(self.obj, self.dt, self.numSteps, markerType="arrow",658markerScale=2, markerColor=color.green,659dropTime=True, timeOffset=self.timeOffset, arrowOffset=self.arrowOffset)660661def test_init(self):662self.assertEqual(self.obj, self.map.obj)663self.assertEqual(self.dt, self.map.dt)664self.assertEqual(self.numSteps, self.map.numSteps)665self.assertEqual("arrow", self.map.markerType)666self.assertEqual(self.markerScale, self.map.markerScale)667self.assertEqual(color.green, self.map.markerColor)668self.assertEqual(vector(1,1,1), self.map.timeOffset)669self.assertEqual(True, self.map.dropTime)670self.assertEqual(self.map.curMarker, 0)671self.assertEqual(vector(1,1,1), self.map.arrowOffset)672673def test_update(self):674self.map.curMarker = 1675676self.map.update(0)677self.assertEqual(arrow.called, 0)678self.assertEqual(points.called, 0)679self.assertEqual(label.called, 0)680681self.map.update(3, quantity=2)682self.assertEqual(arrow.called, 0)683self.assertEqual(points.called, 0)684self.assertEqual(label.called, 0)685self.assertEqual(self.map.curMarker, 1)686self.assertEqual(arrow.pos, self.obj.pos+self.arrowOffset)687self.assertEqual(arrow.axis, 4)688self.assertEqual(arrow.color, color.green)689self.assertEqual(label.text, "2")690691class TestPhysAxis(unittest.TestCase):692def setUp(self):693self.obj = Mock("obj")694self.obj.pos = vector(0,0,0)695self.numLabels = 5696self.axis = vector(1,1,1)697self.startPos = vector(0,1,0)698self.length = 10699self.labels = ["a", "b", "c", "d", "e"]700self.wrongLabels = ["a"]701self.axisType = "arbitrary"702self.labelOrientation="left"703704curve.reset()705706self.physAxis = PhysAxis(self.obj, self.numLabels, axisType=self.axisType, axis=self.axis,707startPos=self.startPos, length=self.length, labels = self.labels,708labelOrientation=self.labelOrientation)709710711def test_init(self):712self.assertEqual(self.physAxis.labelText, self.labels)713self.assertEqual(self.physAxis.obj, self.obj)714self.assertEqual(self.physAxis.lastPos, self.obj.pos)715self.assertEqual(self.physAxis.numLabels, self.numLabels)716self.assertEqual(self.physAxis.axis, self.axis)717self.assertEqual(self.physAxis.length, self.length)718self.assertEqual(self.physAxis.startPos, self.startPos)719self.assertEqual(self.physAxis.axisType, self.axisType)720self.assertEqual(self.physAxis.labelShift, vector(-0.1*self.length, 0, 0))721self.assertEqual(len(self.physAxis.intervalMarkers), self.numLabels)722self.assertEqual(len(self.physAxis.intervalLabels), self.numLabels)723724intervalPos = self.startPos+(self.length * self.axis)725self.assertEqual(self.physAxis.intervalMarkers[-1].pos, intervalPos)726self.assertEqual(self.physAxis.intervalLabels[-1].pos, intervalPos+self.physAxis.labelShift)727self.assertEqual(self.physAxis.intervalLabels[-1].text, "e")728729self.assertEqual(curve.called, 1)730731def test_reorient(self):732newAxis = vector(0,0,1)733startPos = vector(1,0,0)734otherLabels = ["f", "g", "h", "i", "j"]735self.physAxis.reorient(axis=newAxis, startPos=startPos, length=1,736labels=otherLabels, labelOrientation="right")737self.assertEqual(self.physAxis.axis, newAxis)738self.assertEqual(self.physAxis.startPos, startPos)739self.assertEqual(self.physAxis.length, 1)740self.assertEqual(self.physAxis.labelShift, vector(0.1, 0, 0))741742intervalPos = startPos+newAxis743self.assertEqual(self.physAxis.intervalMarkers[-1].pos, intervalPos)744self.assertEqual(self.physAxis.intervalLabels[-1].pos, intervalPos+self.physAxis.labelShift)745self.assertEqual(self.physAxis.intervalLabels[-1].text, "j")746747self.assertEqual(curve.called, 1)748749750def test_update(self):751startMarkerPos = vector(self.physAxis.intervalMarkers[-1].pos.x,752self.physAxis.intervalMarkers[-1].pos.y,753self.physAxis.intervalMarkers[-1].pos.z)754startLabelPos = vector(self.physAxis.intervalLabels[-1].pos.x,755self.physAxis.intervalLabels[-1].pos.y,756self.physAxis.intervalLabels[-1].pos.z)757startCurvePos = vector(self.physAxis.axisCurve.pos[0].x,758self.physAxis.axisCurve.pos[0].y,759self.physAxis.axisCurve.pos[0].z)760761self.physAxis.update()762763self.assertEqual(startMarkerPos, self.physAxis.intervalMarkers[-1].pos)764self.assertEqual(startLabelPos, self.physAxis.intervalLabels[-1].pos)765self.assertEqual(startCurvePos, self.physAxis.axisCurve.pos[0])766767self.physAxis.obj.pos = self.physAxis.obj.pos + vector(1,1,1)768self.physAxis.update()769770self.assertNotEqual(startMarkerPos, self.physAxis.intervalMarkers[-1].pos)771self.assertNotEqual(startLabelPos, self.physAxis.intervalLabels[-1].pos)772self.assertNotEqual(startCurvePos, self.physAxis.axisCurve.pos[0])773774class TestPhysGraph(unittest.TestCase):775def setUp(self):776self.physGraph = PhysGraph(numPlots = 5)777778def test_init(self):779self.assertEqual(self.physGraph.graphDisplay.x, 475)780self.assertEqual(self.physGraph.graphDisplay.y, 350)781self.assertEqual(self.physGraph.numPlots, 5)782self.assertEqual(len(self.physGraph.graphs), 5)783784def test_plot(self):785self.assertRaises(Exception, self.physGraph.plot, vector(0,0,0), vector(1,1,1))786787self.physGraph.plot(vector(0,0,0), vector(1,1,1), vector(2,2,2), vector(3,3,3), vector(4,4,4), vector(5,5,5))788self.assertEqual(len(self.physGraph.graphs[-1].plots), 5)789self.assertEqual(self.physGraph.graphs[-1].plots[0], (vector(0,0,0), vector(1,1,1)))790791class TestPhysTimer(unittest.TestCase):792def setUp(self):793self.timer = PhysTimer(1,1)794795def test_init(self):796self.assertEquals(self.timer.timerLabel.text, "00:00:00.00")797self.assertEquals(self.timer.timerLabel.pos, vector(1,1,0))798799def test_update(self):800self.timer.update(3923.65)801self.assertEquals(self.timer.timerLabel.text, "01:05:23.65")802803self.timer.useScientific=True804self.timer.update(3923.65)805self.assertEquals(self.timer.timerLabel.text, "3.9237E+03")806807808# Now set up unittests to be executed when module is run individually809if __name__ == "__main__":810print("Beginning unit tests!")811unittest.main()812813814