def __init__(self, cameraOverlay, radius=20, mouseVisible=True): self.cameraOverlay = cameraOverlay Cursor.__init__(self, Vertex(0, 0)) Dimensioned.__init__(self, Vertex(radius * 2, radius * 2)) self.radius = radius self.color = (191, 191, 191) setMouseVisibility(mouseVisible)
def __init__(self, timer, length, height, algorithm, data): PyGameScene.__init__(self, timer, length, height) resolution = Milli() Intervaled.__init__(self, 1 / (resolution.scalor * 120), resolution) self.origin = Vertex(length / 2, height / 2) self.radius = min(*self.origin.components) * 0.80 self.algorithm = algorithm self.reset(data)
def __init__(self, timer, length, height): PyGameScene.__init__(self, timer, length, height) Gravitational.__init__( self, 10, Vertex(DEMO_WINDOW_LENGTH / 2, DEMO_WINDOW_HEIGHT << 4)) self.ball = Ball( 32, Vertex(DEMO_WINDOW_LENGTH / 3, DEMO_WINDOW_HEIGHT / 3), Vector(BALL_VELOCITY, BALL_VELOCITY)) self.wind = Wind(0.125) self.addEntity(self.ball)
class ClockingScene(PyGameScene): def __init__(self, timer, length, height, continuous=False): PyGameScene.__init__(self, timer, length, height) # Start a new clock at the current local time. instant = datetime.now() self.clock = Clock(instant.hour, instant.minute, instant.second) self.addEntity(self.clock) # Tap into the Temporal entity updating. # Use the center of the view as the origin of the clock face. self.origin = Vertex(length / 2, height / 2) # Track request for continuous mode or not. self.continuous = continuous def update(self, **kwargs): PyGameScene.update(self, **kwargs) self.scene.fill((0, 0, 0)) # Draw a clock face. radius = min(self.origin.components) * 0.75 pygame.draw.circle(self.scene, (127, 127, 127), self.origin.tupled(), radius, 2) # Adjust reference angle from 0 tau (3 o'clock) to 1/4 tau (midnight). referenceAngle = -0.25 * tau referencePosition = self.origin + Vertex( cos(referenceAngle) * radius, sin(referenceAngle) * radius) # pygame.draw.line(self.scene, (127, 127, 0), self.origin.tupled(), referencePosition.tupled(), 2) # Draw the hour hand. hoursAngle = referenceAngle + (self.clock.hours % 12) / 12 * tau hourHandPosition = self.origin + Vertex( cos(hoursAngle) * radius, sin(hoursAngle) * radius) pygame.draw.line(self.scene, (127, 0, 0), self.origin.tupled(), hourHandPosition.tupled(), 2) # Draw the minute hand. minutesAngle = referenceAngle + self.clock.minutes / 60 * tau minuteHandPosition = self.origin + Vertex( cos(minutesAngle) * radius, sin(minutesAngle) * radius) pygame.draw.line(self.scene, (0, 127, 0), self.origin.tupled(), minuteHandPosition.tupled(), 2) # Draw the second hand. secondsAngle = referenceAngle + (self.clock.seconds if self.continuous else floor( self.clock.seconds)) / 60 * tau secondHandPosition = self.origin + Vertex( cos(secondsAngle) * radius, sin(secondsAngle) * radius) pygame.draw.line(self.scene, (0, 0, 127), self.origin.tupled(), secondHandPosition.tupled(), 2)
def __init__(self, timer, length, height, continuous=False): PyGameScene.__init__(self, timer, length, height) # Start a new clock at the current local time. instant = datetime.now() self.clock = Clock(instant.hour, instant.minute, instant.second) self.addEntity(self.clock) # Tap into the Temporal entity updating. # Use the center of the view as the origin of the clock face. self.origin = Vertex(length / 2, height / 2) # Track request for continuous mode or not. self.continuous = continuous
def __init__(self, timer, length, height): PyGameScene.__init__(self, timer, length, height) self.borders = [] self.borders.append( Segment(Vertex(BORDER_SIZE, BORDER_SIZE), Vertex(length - BORDER_SIZE, BORDER_SIZE))) # Top self.borders.append( Segment(Vertex(length - BORDER_SIZE, BORDER_SIZE), Vertex(length - BORDER_SIZE, height - BORDER_SIZE))) # Right self.borders.append( Segment(Vertex(BORDER_SIZE, height - BORDER_SIZE), Vertex(length - BORDER_SIZE, height - BORDER_SIZE))) # Bottom self.borders.append( Segment(Vertex(BORDER_SIZE, BORDER_SIZE), Vertex(BORDER_SIZE, height - BORDER_SIZE))) # Left self.previewColor = (127, 127, 127) self.segmentColor = (0, 0, 127) # Track the four vertices/vectors comprising of the line segments AC and BD. self.capturePosition = False self.A = None self.AC = None # Indicate a 'mode' for checking for intersection between the line segments. self.checking = False self.derivativesColor = (127, 127, 0) self.intersectionColor = (255, 127, 127)
def __init__(self, columns, rows): if columns * rows <= 0: raise ValueError( f"The pixel dimensions of the sensor array are invalid: [{columns}, {rows}]" ) Dimensioned.__init__(self, Vertex(columns, rows))
def live(self): # Breathe! Enjoy the moment(s) that have elapsed! velocity = self.velocity * self.accumulatedTime self.accumulatedTime = 0 lastPosition = self.position.copy() # TODO: Use perception (do a perception check! lol) to determine # nearby entities that might interfere with the projection. nextPosition = self.projectBy(velocity) # Uh oh, there might be a barrier..! if self.environment: length, height = self.environment.dimensions.tupled() # Reflect the ray if necessary. px, vx = reflect(0, nextPosition.x, velocity.x, length) py, vy = reflect(0, nextPosition.y, velocity.y, height) if velocity.x != vx: self.onReflection() self.velocity.x *= -1 if velocity.y != vy: self.onReflection() self.velocity.y *= -1 nextPosition = Vertex(px, py) self.position = nextPosition # Use a line segment to indicate change in position. self.segment = Segment(lastPosition, self.position)
def __init__(self, timer, length, height, continuous=False): PyGameScene.__init__(self, timer, length, height) self.origin = Vertex(length / 2, height / 2) self.borders = list() self.borders.append(Segment(Vertex(BORDER_SIZE, BORDER_SIZE), Vertex(length - BORDER_SIZE, BORDER_SIZE))) # Top self.borders.append(Segment(Vertex(length - BORDER_SIZE, BORDER_SIZE), Vertex(length - BORDER_SIZE, height - BORDER_SIZE))) # Right self.borders.append(Segment(Vertex(BORDER_SIZE, height - BORDER_SIZE), Vertex(length - BORDER_SIZE, height - BORDER_SIZE))) # Bottom self.borders.append(Segment(Vertex(BORDER_SIZE, BORDER_SIZE), Vertex(BORDER_SIZE, height - BORDER_SIZE))) # Left # Ensure the radar beam can reach the corners of the screen. self.radar = Radar(self.origin) self.addEntity(self.radar) # Tap into the Temporal entity updating.
def update(self, **kwargs): PyGameScene.update(self, **kwargs) self.scene.fill((0, 0, 0)) # Draw a clock face. radius = min(self.origin.components) * 0.75 pygame.draw.circle(self.scene, (127, 127, 127), self.origin.tupled(), radius, 2) # Adjust reference angle from 0 tau (3 o'clock) to 1/4 tau (midnight). referenceAngle = -0.25 * tau referencePosition = self.origin + Vertex( cos(referenceAngle) * radius, sin(referenceAngle) * radius) # pygame.draw.line(self.scene, (127, 127, 0), self.origin.tupled(), referencePosition.tupled(), 2) # Draw the hour hand. hoursAngle = referenceAngle + (self.clock.hours % 12) / 12 * tau hourHandPosition = self.origin + Vertex( cos(hoursAngle) * radius, sin(hoursAngle) * radius) pygame.draw.line(self.scene, (127, 0, 0), self.origin.tupled(), hourHandPosition.tupled(), 2) # Draw the minute hand. minutesAngle = referenceAngle + self.clock.minutes / 60 * tau minuteHandPosition = self.origin + Vertex( cos(minutesAngle) * radius, sin(minutesAngle) * radius) pygame.draw.line(self.scene, (0, 127, 0), self.origin.tupled(), minuteHandPosition.tupled(), 2) # Draw the second hand. secondsAngle = referenceAngle + (self.clock.seconds if self.continuous else floor( self.clock.seconds)) / 60 * tau secondHandPosition = self.origin + Vertex( cos(secondsAngle) * radius, sin(secondsAngle) * radius) pygame.draw.line(self.scene, (0, 0, 127), self.origin.tupled(), secondHandPosition.tupled(), 2)
def handle(self, event): handled = False if event.type == pygame.QUIT: handled = self.onQuit() elif event.type == pygame.MOUSEMOTION: handled = self.onMousePositionChanged(Vertex(*event.pos)) elif event.type == pygame.MOUSEBUTTONDOWN: handled = self.onMouseButtonPressed(event.button) elif event.type == pygame.MOUSEBUTTONUP: handled = self.onMouseButtonReleased(event.button) return handled
def __init__(self, length, height, sensorArray, frameRate=FRAME_RATE): if length * height <= 0: raise ValueError( f"The physical dimensions of the camera sensor are invalid: [{length}, {height}]" ) if not isinstance(sensorArray, CameraSensorArray): raise ValueError( "Expected 'sensorArray' to be a CameraSensorArray instance.") Dimensioned.__init__(self, Vertex(length, height)) self.sensorArray = sensorArray interval = Milli() Intervaled.__init__(self, millisecondsPerFrame(frameRate), interval) self.capturing = False
def update(self, **kwargs): self.scene.fill((0, 0, 0)) length, height = self.dimensions.tupled() # Draw a line for each data point. for index in range(len(self.data)): point = self.data[index] rads = self.radialize(index) outer = self.origin + Vertex( cos(rads) * length * .45, sin(rads) * height * .45) color = self.colorize(point) try: smoothLine(self.scene, color, self.origin.tupled(), outer.tupled()) # jaggedLine(self.scene, color, self.origin.tupled(), outer.tupled()) except TypeError as oops: print(f"Failed at {origin} -> {outer}: {oops}!") break
def __init__(self, timer, dimensions): Dimensioned.__init__(self, Vertex(*dimensions)) Environment.__init__(self, timer)
def __init__(self, length, height): CameraMount.__init__(self, Vertex(length, height))
def intersection(AC, BD): if not isinstance(AC, Segment): raise TypeError("Expected 'a' to be an instance of Segment!") if not isinstance(BD, Segment): raise TypeError("Expected 'b' to be an instance of Segment!") # Goal: Determine the intersection of line segments AC and BD, if it exists. I, delta = None, None try: # Objective: Find the angle of intersection from A to I to B, or angle AIB. # ================================ # The method used will be to find the angles of ABD and BAC, and subtract # their summation from the sum of angles in any triangle, 180* or π. We'll # be using radians, so as to find a "slice of π". ^_^ # Task: Calculate angle ABD. # -------------------------------- vAB = AC.debut - BD.debut vBD = BD.arret - BD.debut ABD = Vertex.angleBetween(vAB, vBD) # Task: Calculate angle BAC. # -------------------------------- vBA = BD.debut - AC.debut vAC = AC.arret - AC.debut BAC = Vertex.angleBetween(vBA, vAC) # Task: Derive the angle AIB. # -------------------------------- AIB = pi - (ABD + BAC) # Objective: Find the lengths of line segments AB, AI, and BI. # ================================ # We'll be using the rule of sines, as we already know the length of # line segment AB, which also happens to correspond to angle AIB, so # we'll call it.. i. ai = vAB.magnitude() bi = vBA.magnitude() # Task: Calculate the lengths of line segments AI and BI. # -------------------------------- af = abs( ai * sin(ABD) / sin(AIB) ) # The length of line segment AI along line segment AC. It can't be negative. bf = abs( bi * sin(BAC) / sin(AIB) ) # The length of line segment BI along line segment BD. It can't be negative. # Objective: Find the intersection vertex I. # ================================ # If an intersection exists, the lengths of AI or BI must be less than or equal to # AC or BD, respectively. if af <= vAC.magnitude() and bf <= vBD.magnitude(): # Task: Calculate the intersection vertices Ia and Ib. # -------------------------------- # Since we know the lengths of AI and BI, we can scale the unit vectors of the # respective line segments to derive "their I"s. Ia = AC.debut + (vAC.unit() * af) Ib = BD.debut + (vBD.unit() * bf) # print(f"Ia: {Ia}; Ib: {Ib}") # Task: Derive the intersection vertex I. # -------------------------------- # But only if the values are close enough; otherwise, no deal! if isclose(Ia[0], Ib[0]) and isclose(Ia[1], Ib[1]): I = Ia # Due to potential rounding issues, we'll provide the delta of the calculated # intersection vectors. delta = Ia - Ib except ZeroDivisionError: # If we get a division by zero error... either there was a line length of 0, or # something... interesting happened. pass # TODO: What to do in this case? As one test outputs: # No intersection between [130, 351] --- [521, 154] and [521, 154] --- [521, 154]. # Technically, the intersection point is [521, 154], but because the second line # has a length of zero, the algorithm above cannot find one of the angles, given # that the vector has zero length (in this case, vBD). except ValueError: # This can happen when exceeding the domain of math.acos(numerator/divisor). # I didn't know .. math had a domain that could error out. pass return (I, delta)
class SortingScene(PyGameScene, Intervaled): def __init__(self, timer, length, height, algorithm, data): PyGameScene.__init__(self, timer, length, height) resolution = Milli() Intervaled.__init__(self, 1 / (resolution.scalor * 120), resolution) self.origin = Vertex(length / 2, height / 2) self.radius = min(*self.origin.components) * 0.80 self.algorithm = algorithm self.reset(data) def shuffle(self, shuffles=3000): count = 0 while count < shuffles: a = randint(0, len(self.data) - 1) b = randint(0, len(self.data) - 1) if a != b: self.data[a], self.data[b] = self.data[b], self.data[a] count += 1 def reset(self, data): self.data = data self.shuffle() self.algorithm.reset() self.delayed = False def onMouseButtonPressed(self, button): handled = False if button == pygame.BUTTON_LEFT: # Only allow a shuffle if the algorithm is finished. if self.algorithm.finished(self.data): if not self.delayed: self.delayed = True else: self.reset(self.data) handled = True return handled def radialize(self, value): return radians((value - 0) / (len(self.data) - 0) * 360) def colorize(self, value): # Based on https://www.particleincell.com/2014/colormap/ (the "Long Rainbow") f = (value - 0) / (len(self.data) - 0) a = (1 - f) / 0.2 X = floor(a) Y = floor(255 * (a - X)) r, g, b = 0, 0, 0 if X == 0: r, g, b = 255, Y, 0 elif X == 1: r, g, b = 255 - Y, 255, 0 elif X == 2: r, g, b = 0, 255, Y elif X == 3: r, g, b = 0, 255 - Y, 255 elif X == 4: r, g, b = Y, 0, 255 else: r, g, b = 255, 0, 255 return (r, g, b) def onInterval(self): self.data = self.algorithm.step(self.data) def onElapsedTime(self, elapsedTime): Intervaled.onElapsedTime(self, elapsedTime) PyGameScene.onElapsedTime(self, elapsedTime) def update(self, **kwargs): self.scene.fill((0, 0, 0)) length, height = self.dimensions.tupled() # Draw a line for each data point. for index in range(len(self.data)): point = self.data[index] rads = self.radialize(index) outer = self.origin + Vertex( cos(rads) * length * .45, sin(rads) * height * .45) color = self.colorize(point) try: smoothLine(self.scene, color, self.origin.tupled(), outer.tupled()) # jaggedLine(self.scene, color, self.origin.tupled(), outer.tupled()) except TypeError as oops: print(f"Failed at {origin} -> {outer}: {oops}!") break
def resize(self, length, height): if self.display: self.display.resize(length, height) self.dimensions = Vertex( length, height) # TODO: Make this a Dimensioned object..?
def __init__(self, radius): CameraMount.__init__(self, Vertex(radius, radius))
def initialize(self, length, height): pygame.display.init() self.output = pygame.display.set_mode((length, height)) self.frame = None self.overlay = None self.dimensions = Vertex(*self.output.get_size())
def emissionHeading(self): # Pick a random heading for the ray. heading = random() * 2 * pi vx = cos(heading) * MAXIMUM_VELOCITY * INITIAL_VELOCITY_MODIFIER vy = sin(heading) * MAXIMUM_VELOCITY * INITIAL_VELOCITY_MODIFIER return Vertex(vx, vy)
def update(self, **kwargs): # Render unto the overlay thine .. rendering..? self.cameraOverlay.displayRendering( self.render(), self.position - Vertex(self.radius, self.radius))
def live(self): # As long as the radar lives, it shall emit a beam! self.environment.addEntity(RadarBeam(self.position, Vertex(cos(self.angle), sin(self.angle))))