siteIcon

Tech Novice Tools

'.pyde' Code

burgerIcon

Consolidatemerge

Source Code

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