Consolidate: Driver
'''
Template: Pr_Py_Grid_Template_10_24_19
File: Narnia_Driver_St9_10_31_19
Site:
Path:
Date: 10/31/19
Author: klp
Comments:
Calculate distance and angle between 2 cools
and orbit work?
'''
import random
from Grid import *
from Sinusoid import *
from Molecool import *
#wide-scoped variables go here
#adjust your canvas size if desired, here
canvas_width = 600
canvas_height = 600
#boolean variables
didBeginSketch = False
isSessionRunning = False
showFrameAndTime = False
allowAggression = True
shouldThrottleSpeeds = True
shouldAllowCollisions = True
showOutput = False
showDemo = False
consolidateByColor = True
#time and theta variables
t = 0 #time
tinc = 2
defaultTinc = 2
theta = 0
deltaTheta = 30
consolidationValue = 30
def setup():
global shouldRefreshBackground, showGridLines, shouldShowAxes
global numSquares, inc
global white, my_red, my_blue, my_green, yellow, black
global lightGray, purple, gray, cyan, paleGreen
global bgColr, myRate
global tinc, tincDefault
global grid, wave, myCools, minSpacing, maxCools
global maxSpeed
size(canvas_width, canvas_height)
#color mode and basic colors
colorMode(HSB, 360, 100, 100, 100) #hue, saturation, brightness, alpha
white = color(0, 0, 100)
#red, green and blue are reserved words in Processing
my_red = color(0, 100, 100)
my_blue = color(240, 80, 60)
my_green = color(120, 60, 40)
yellow = color(60, 100, 100)
black = color(0, 0, 0)
lightGray = color(0, 0, 95)
purple = color(290, 100, 100)
gray = color(0, 0, 50)
cyan = "#1ED0FA" #using hex code instead of color(h, s, b)
paleGreen = "#BEF7BE"
#sketch settings
myRate = 60 # default is 60 frames/second
bgColr = lightGray
frameRate(myRate)
background(bgColr)
#adjust for background refresh and grid here
shouldRefreshBackground = True
showGridLines = True
shouldShowAxes = True
#additional setup variables/functions here
msg = "===Narnia Driver St7==="
print(msg)
tinc = 0
tincDefault = tinc
noStroke()
#call below lets you customize features
#grid = Grid(lineColor=my_green, numSquares=20.0, bg=paleGreen)
#grid = Grid(numSquares=14)
grid = Grid() #all default values used
print(grid.toString())
#TODO: shown here as example; can delete if not needed
wave = Sinusoid(type="c", period=2.0*frameRate, minVal = 1, maxVal = 1.5)
print(wave.toString())
myCools = []
numCools = 300
minSpacing = .5*grid.inc
maxLoopCount = 250
maxCools = 10
maxSpeed = 4.0
for i in range(0, numCools):
#make sure future cools are not gonna overlap within reason
loopCount = 0
while True:
x = random.uniform(.33*grid.inc, width - .33*grid.inc)
y = random.uniform(.33*grid.inc, height - .33*grid.inc)
aHue = random.uniform(0, 360)
aColr = createColor(aHue)
randomAngle = random.uniform(0, 360)
m = Molecool(xc=x, yc=y, thetaDeg=randomAngle, colr=aColr)
m.showDirection = False
isClear = compareCoolLocations(m)
loopCount += 1
if isClear or loopCount > maxLoopCount:
break;
myCools.append(m)
#end createCools loop
#end function setup
def draw():
global t, tinc
#adjust background color if desired
if shouldRefreshBackground:
background(bgColr);
grid.drawGraphGrid(showGridLines)
t += tinc
if showFrameAndTime:
print("Frame ", frameCount, "t = " , t)
for m in myCools:
m.update(tinc)
m.display()
if shouldThrottleSpeeds:
throttleSpeed(m)
if showOutput:
println(m.toString())
if len(myCools) == 2 and showOutput:
d = myCools[0].computeDistanceFrom(myCools[1])
incDist = d/grid.inc
angBetween = myCools[1].calculateAngleRadBetween(myCools[0])
angBetweenDeg = degrees(angBetween)
print("distance between = " + str(incDist))
print("angle between = " + str(angBetweenDeg))
#end for loop
if consolidateByColor and didBeginSketch:
for m0 in myCools:
ndx0 = myCools.index(m0)
for m1 in myCools:
ndx1 = myCools.index(m1)
if ndx0 == ndx1:
continue
else:
d = m0.computeDistanceFrom(m1)
if d < 1*grid.inc:
hue0 = hue(m0.colr)
hue1 = hue(m1.colr)
if abs(hue0 - hue1) < consolidationValue:
avgHue = ceil((hue0 + hue1)/2.0)
m0.colr = color(avgHue, 100, 100)
m0.diam = 1.10*max(m0.diam, m1.diam)
myCools.remove(m1)
#prevent glitching on borders when/if diam is increased
m0.xc = max(m0.xc, .5*m0.diam)
m0.xc = min(m0.xc, width-.5*m0.diam)
m0.yc = max(m0.yc, .5*m0.diam)
m0.yc = min(m0.yc, height-.5*m0.diam)
m0.update(tinc)
m0.display()
print("Size of myCools: " + str(len(myCools)))
#TODO: shown here as example: can delete if not needed
if showDemo:
demo()
#end function draw
def throttleSpeed(m):
if m.speed > maxSpeed:
m.setSpeed(.75*maxSpeed)
#end function throttleSpeed
def compareCoolLocations(m):
farEnoughAway = True
for myM in myCools:
distance = myM.computeDistanceFrom(m)
if distance < minSpacing:
farEnoughAway = False
break
return farEnoughAway
#end function compareCoolLocations
def createColor(h):
if h >=60 and h <= 200:
aSat = random.randint(50, 100)
aBri = random.randint(70, 100)
else:
aSat = 100
aBri = 100
aColr = color(h, aSat, aBri)
return aColr
#end function createColor
def demo():
fill(yellow)
diamX = 300*wave.getY(t)
diamY = 200*wave.getY(1.1*t)
ellipse(width/2, height/2, diamX, diamY)
#printing distances in terms of grid scale
println("diamX, diamY = " + str(diamX/grid.inc) + ", " + str(diamY/grid.inc))
#end function demo
#no touch code other than TODO shown
def mousePressed():
if mouseButton == LEFT:
startStop()
if mouseButton == RIGHT:
rmcAction()
#end function mousePressed
def rmcAction():
#add only when paused
if not isSessionRunning and len(myCools) < maxCools:
addACool()
#end function rmcAction
def addACool():
x = mouseX
y = mouseY
anAngle = deltaTheta * len(myCools)
aHue = (deltaTheta * len(myCools)) % 360
aColor = createColor(aHue)
aCool = Molecool(xc = x, yc = y, colr=aColor, thetaDeg=anAngle)
aCool.setCoordinates(x,y)
#tuple
adjustment = canvasBoundaryAdjustments(aCool)
if adjustment:
aCool.setCoordinates(adjustment[0], adjustment[1])
myCools.append(aCool)
#end function addACool
def canvasBoundaryAdjustments(m):
#if a cool is created too close to canvas boundary move it a bit away using a tuple
myX = m.xc
myY = m.yc
adjustmentMade = False
if m.xc - .5*m.diam < .5*grid.inc:
myX = .25*grid.inc + .5*m.diam
adjustmentMade = True
if m.xc + .5*m.diam > width - .5*grid.inc:
myX = width - (.25*grid.inc + .5*m.diam)
adjustmentMade = True
if m.yc - .5*m.diam < .5*grid.inc:
myY = .25*grid.inc + .5*m.diam
adjustmentMade = True
if m.yc + .5*m.diam > height - .5*grid.inc:
myY = height - (.25*grid.inc + .5*m.diam)
adjustmentMade = True
#tuple
if adjustmentMade:
return (myX, myY)
else:
return None
#end function canvasBoundaryAdjustments
def setAllDormantStates(b):
for m in myCools:
m.setIsDormant(b)
#no touch code other than TODO items as shown
def startStop():
global didBeginSketch, isSessionRunning, tinc
#pause/restart action
#only when sim is paused can you add cools with a RMC if globally allowed
if not didBeginSketch:
didBeginSketch = True
setAllDormantStates(False)
tinc = defaultTinc
if isSessionRunning:
isSessionRunning = False
tinc = 0
setAllDormantStates(True)
else:
isSessionRunning = True
tinc = defaultTinc
setAllDormantStates(False)
#end function startStop
Last update: 11/03/19
Consolidate: Molecool
import itertools
from Sinusoid import *
my_green = "#296729"
black = "#000000"
white = "#FFFFFF"
class Molecool:
#establish properties for initialization
def __init__(self, xc=100, yc=100, colr=my_green, diam=20,
speed=3.0, thetaDeg=45.0):
self.xc = xc
self.yc = yc
self.dirDeg = thetaDeg
self.currentDirDeg = thetaDeg
self.speed = speed
self.colr = colr
self.origColor = self.colr
self.diam = diam
self.travelTime = 0.0
self.currentSpeed = self.speed
self.distanceTraveled = 0.0
self.vx = self.speed * cos(radians(self.dirDeg))
self.vy = self.speed * sin(radians(self.dirDeg))
self.showDirection = True
self.showBorder = True
self.isDormant = True
self.shouldWobble = False
#TODO: aggressive hits, passive hits
#end __init__
def toString(self):
temp = self.__class__.__name__
temp += "\nCoordinates = (" + str(self.xc) + ", " + str(self.yc) + ")"
temp += "\nVelocities = (" + str(self.vx) + ", " + str(self.vy) + ")"
temp += "\nSpeed = " + str(self.speed)
temp += "\nCurrent Bearing = " + str(self.currentDirDeg)
temp += "\nTravel time = " + str(self.travelTime)
temp += "\nDistance Traveled = " + str(self.distanceTraveled)
temp += "\n"
return temp
#end function toString
#--- setters ----
def setDirDeg(self, thetaDeg):
self.dirDeg = thetaDeg
self.currentDirDeg = self.dirDeg
self.vx = self.speed * cos(radians(self.dirDeg))
self.vy = self.speed * sin(radians(self.dirDeg))
#end function setDirDeg
def setCurrentDirDeg(self, thetaDeg):
self.currentDirDeg = self.dirDeg
self.vx = self.speed * cos(radians(self.currentDirDeg))
self.vy = self.speed * sin(radians(self.currentDirDeg))
#end function setCurrentDirDeg
def setCoordinates(self, x, y):
#precondition: x and y are appropriate for canvas boundaries
self.xc = x
self.yc = y
#end function setCoordinates
def setSpeed(self, s):
#dwr
self.speed = s
self.vx = self.speed * cos(radians(self.currentDirDeg))
self.vy = self.speed * sin(radians(self.currentDirDeg))
self.computeCurrentAngleDeg()
#end function setSpeed
def setIsDormant(self, b):
self.isDormant = b
if not self.isDormant:
self.colr = self.origColor
else:
self.dimColor(30, 80)
#end function setIsDormant
def dimColor(self, s, b):
aHue = hue(self.origColor)
self.colr = color(aHue, s, b)
#end function dimColor
def update(self, t):
if self.isDormant:
println("Taking a nap")
else:
self.travelTime += t
#wobble adjustment
if self.shouldWobble:
waveX = Sinusoid(type="c", period=2*frameRate,
minVal = -.1, maxVal = .1)
waveY = Sinusoid(type="s", period=2*frameRate,
minVal = -.1, maxVal = .1)
self.vx = self.vx + waveX.getY(self.travelTime)
self.vy = self.vy + waveY.getY(self.travelTime)
#dwr
self.speed = self.calculateCurrentSpeed()
self.computeCurrentAngleDeg()
delx = self.vx * t
dely = self.vy * t
self.distanceTraveled += sqrt(pow(delx,2) + pow(dely,2))
self.xc += delx
self.yc += dely
self.currentSpeed = self.calculateCurrentSpeed()
self.checkBoundaries()
self.computeCurrentAngleDeg()
#end function update
def checkBoundaries(self):
if self.xc + .5*self.diam > width or self.xc - .5*self.diam < 0:
self.vx *= -1
if self.yc + .5*self.diam > height or self.yc - .5*self.diam < 0:
self.vy *= -1
#end function checkBoundaries
def calculateCurrentSpeed(self):
return sqrt(pow(self.vx, 2) + pow(self.vy, 2))
#end function calculateCurrentSpeed
def display(self):
if self.showBorder:
stroke(black)
else:
noStroke()
if self.isDormant:
self.dimColor(30, 80)
fill(self.colr)
circle(self.xc, self.yc, self.diam)
if self.showDirection:
self.showDirection()
#end function display
def showDirection(self):
if self.currentDirDeg == 0 and self.vx < 0:
currentAngle = PI
else:
currentAngle = radians(self.currentDirDeg)
littleR = 1.0*self.diam
x1 = self.xc + littleR*cos(currentAngle)
x2 = self.yc + littleR*sin(currentAngle)
strokeWeight(3)
stroke(black)
line(self.xc, self.yc, x1, x2)
if self.showBorder:
stroke(black)
else:
noStroke()
fill(white)
circle(self.xc, self.yc, .3*self.diam)
#end function showDirection
def computeCurrentAngleDeg(self):
ratio = 0
currentAngleRad = 0
if self.vx > 0 or self.vx < 0:
ratio = self.vy/self.vx;
currentAngleRad = atan(ratio)
else:
currentAngleRad = HALF_PI
if self.vx < 0 and self.vy >= 0:
currentAngleRad += PI;
if self.vy <= 0 and self.vx <= 0:
currentAngleRad += PI;
if self.vy <= 0 and self.vx > 0:
currentAngleRad += TWO_PI;
self.currentDirDeg = currentAngleRad * 180.0/PI;
#end function computeCurrentAngleDeg
def computeDistanceFrom(self, m):
delx = self.xc - m.xc
dely = self.yc - m.yc
distance = sqrt(pow(delx,2) + pow(dely,2))
return distance
#end computeDistanceFrom
def calculateAngleRadBetween(self, m1):
delx = abs(m1.xc - self.xc);
dely = abs(m1.yc - self.yc);
thetaRef = -1; #dummy initial value
myThetaRad = 0;
if delx != 0:
thetaRef = atan2(dely,delx);
else:
thetaRef = HALF_PI;
#adjust for quadrants relative to 'this' as origin,
#positive angles clockwise from horizontal
#m1 in Q4
if m1.xc > self.xc and m1.yc >= self.yc:
myThetaRad = thetaRef;
#m1 directly below
if m1.xc == self.xc and m1.yc > self.yc:
myThetaRad = HALF_PI
#m1 in Q3
if m1.xc < self.xc and m1.yc >= self.yc:
myThetaRad = PI - thetaRef
#m1 directly to left
if m1.yc == self.yc and m1.xc < self.xc:
myThetaRad = PI
#m1 in Q2
if m1.xc < self.xc and m1.yc <= self.yc:
myThetaRad = PI + thetaRef;
#m1 directly above
if m1.xc == self.xc and m1.yc < self.yc:
myThetaRad = 3.0*PI/2.0;
#m1 in Q1
if m1.xc > self.xc and m1.yc <= self.yc:
myThetaRad = 2*PI - thetaRef;
return myThetaRad;
#end function calculateAngleRadBetween
Last update: 11/03/19
Consolidate: Grid
#dwr: use hex codes here: color mode was squirrely
lightGray = "#F5F5F5" #same as in driver
gray = "#818181"
black = "#000000"
class Grid:
def __init__(self, numSquares=12.0, bg=lightGray,
lineColor=gray, axesColor=black, wt=1, showAxes=True):
self.bg = bg
self.numSquares = numSquares
#dwr: must cast as float to avoid truncating
self.inc = width/float(self.numSquares)
self.shouldShowAxes = showAxes
self.lineColor = lineColor
self.axesColor = axesColor
self.lineWeight = wt
#end __init__
#a toString method is a traditional way to report
#information about an object: like a summary
def toString(self):
temp = "Canvas is: " + str(width) + " wide and " + str(height) + " tall"
temp += "\nGrid is " + str(self.numSquares) + " by " + str(self.numSquares)
temp += "\nThe increment is: " + str(self.inc)
temp += "\n"
return temp
#end function toString
#no touch code
def drawHorizLines(self):
stroke(self.lineColor)
strokeWeight(self.lineWeight)
y = 0
while(y < height):
line(0, y, width, y)
y += self.inc
#end while loop
#end function drawHorizLines
#no touch code
def drawVertLines(self):
stroke(self.lineColor)
strokeWeight(self.lineWeight)
x = 0
while(x < width):
line(x, 0, x, height)
x += self.inc
#end while loop
#end function drawVertLines
#no touch code
def drawAxes(self):
stroke(self.axesColor)
strokeWeight(2*self.lineWeight)
#x-axis
line(0, height/2, width, height/2)
#y-axis
line(width/2, 0, width/2, height)
#end function drawAxes
#no touch code
def drawGraphGrid(self, showLines):
fill(self.bg)
square(0, 0, width)
if showLines:
self.drawHorizLines()
self.drawVertLines()
if self.shouldShowAxes:
self.drawAxes()
#end function drawGraphGrid
#end class Grid
Last update: 11/03/19
Consolidate: Sinusoid
class Sinusoid:
def __init__(self, type="s", minVal=-1.0, maxVal=1.0, period=TWO_PI, hShift=0.0):
self.type = type;
self.minVal = minVal
self.maxVal = maxVal
self.period = period
self.hShift = hShift
self.axis = (minVal + maxVal)/2.0
self.amplitude = maxVal - self.axis
self.b = 2*PI/period
#end __init__ Sinusoid
def toString(self):
temp = "y = " + str(self.amplitude) + " * "
if self.type == "s":
temp += "sin["
else:
temp += "cos["
temp += str(self.b) + "(x - " + str(self.hShift) + ")] + " + str(self.axis)
temp += "\nPeriod = " + str(self.period)
temp += "\n"
return temp
#end function toString
def getY(self, x):
if type == "s":
return self.amplitude*sin(self.b*(x - self.hShift)) + self.axis
else:
return self.amplitude*cos(self.b*(x - self.hShift)) + self.axis
#end function getY
#end class Sinusoid
Last update: 11/03/19