Last active
July 17, 2019 18:34
-
-
Save SoMuchForSubtlety/c2284b2ebbb5f48b580471403bc4b0a0 to your computer and use it in GitHub Desktop.
simple swarm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| from threading import Thread | |
| from random import seed | |
| from random import random | |
| from datetime import datetime | |
| from PyQt5.QtCore import QCoreApplication, Qt, QLineF, QRectF, QTimer | |
| from PyQt5.QtGui import QPainter, QColor, QFont, QKeySequence, QMouseEvent, QCursor | |
| from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QAction, QMessageBox | |
| from PyQt5.QtWidgets import QCheckBox, QProgressBar, QShortcut | |
| import numpy as np | |
| import math | |
| import operator | |
| # how much of the speed is retained after every calculation | |
| slowdown = 1 | |
| # how much of the movement vector is executed before recalculating | |
| sim_speed = 0.1 | |
| # the distance where we switch from attraction to repulsion | |
| min_desired_distance = 10 | |
| # attraction vector multiplier | |
| attraction_force = 0.5 | |
| # max allowed speed | |
| max_speed = 20 | |
| # max allowed change to speed for each calculation | |
| max_speed_change = 1 | |
| # how far cells can see | |
| max_sight = 200 | |
| # mow many cells to simulate | |
| population = 100 | |
| # how much cells react to the mouse pointer | |
| mouse_attraction = -100000 | |
| mouse_location = np.array([500, 500]) | |
| def vector(start_point, end_point): | |
| return end_point - start_point | |
| class Flyer(): | |
| swarm = [] | |
| def __init__(self, position, gui): | |
| self.gui = gui | |
| self.position = position | |
| self.future_position = position | |
| self.heading = np.array([0.0, 0.0]) | |
| Flyer.swarm.append(self) | |
| def act(self): | |
| self.heading *= slowdown | |
| new_movement = np.array([0.0, 0.0]) | |
| v = vector(self.position, mouse_location) | |
| d = math.hypot(v[0], v[1]) | |
| if d < max_sight: | |
| v /= d | |
| new_movement += (v * mouse_attraction) | |
| closest = [] | |
| closest_distance = 0 | |
| for flyer in Flyer.swarm: | |
| if flyer != self: | |
| v = vector(self.position, flyer.position) | |
| d = math.hypot(v[0], v[1]) | |
| if d < max_sight: | |
| if (len(closest) == 0 or d < closest_distance) and d > min_desired_distance: | |
| closest_distance = d | |
| closest.append(flyer) | |
| if len(closest) > 5: | |
| closest.pop(0) | |
| if d < min_desired_distance and d != 0: | |
| v /= d | |
| v *= (5 - d) | |
| new_movement += v | |
| bounds_vector = np.array([0.0, 0.0]) | |
| if self.position[0] < 0: | |
| self.position[0] += 1000 | |
| #bounds_vector += np.array([100000, 0]) | |
| elif self.position[0] > 1000: | |
| self.position[0] -= 1000 | |
| #bounds_vector += np.array([-100000, 0]) | |
| if self.position[1] < 0: | |
| self.position[1] += 1000 | |
| #bounds_vector += np.array([0, 100000]) | |
| elif self.position[1] > 1000: | |
| self.position[1] -= 1000 | |
| #bounds_vector += np.array([0, -100000]) | |
| #new_movement += bounds_vector | |
| # move to closest flyer if none are under 5 away | |
| for flyer in closest: | |
| v = vector(self.position, flyer.position) | |
| v *= attraction_force | |
| new_movement += v | |
| # limit movement | |
| length = math.hypot(new_movement[0], new_movement[1]) | |
| if length > max_speed_change: | |
| new_movement /= length | |
| new_movement *= max_speed_change | |
| self.heading += new_movement | |
| length = math.hypot(self.heading[0], self.heading[1]) | |
| if length > max_speed: | |
| self.heading /= length | |
| self.heading *= max_speed | |
| self.future_position = self.position + (self.heading * sim_speed) | |
| def execute_move(self): | |
| self.position = self.future_position | |
| class window(QMainWindow): | |
| def __init__(self): | |
| seed(datetime.now()) | |
| super(window, self).__init__() | |
| self.setStyleSheet("QMainWindow {background: 'white';}") | |
| self.clock = 500 | |
| self.wSize = [1000, 1000] | |
| self.timer = QTimer() | |
| self.timer.timeout.connect(self.updateWorld) | |
| self.timer.start(10) | |
| self.setGeometry(50, 50, self.wSize[0], self.wSize[1]) | |
| self.setWindowTitle('AL') | |
| for i in range(0, population): | |
| Flyer(np.array([((random() * 1000)), (random() * 1000)]), self) | |
| self.setHotkeys() | |
| self.home() | |
| def setHotkeys(self): | |
| self.shortcutact = QShortcut(QKeySequence("Right"), self) | |
| self.shortcutact.activated.connect(self.updateWorld) | |
| self.shortcutpr = QShortcut(QKeySequence("Space"), self) | |
| self.shortcutpr.activated.connect(self.PauseResume) | |
| def PauseResume(self): | |
| if self.timer.isActive(): | |
| self.timer.stop() | |
| else: | |
| self.timer.start(self.clock) | |
| def home(self): | |
| self.show() | |
| def paintEvent(self, event): | |
| qp = QPainter() | |
| qp.begin(self) | |
| self.paintStuff(event, qp) | |
| qp.end() | |
| def enlarge_window(self, state): | |
| if state == Qt.Checked: | |
| self.setGeometry(50, 50, 1000, 600) | |
| else: | |
| self.setGeometry(50, 50, 500, 300) | |
| def close_application(self): | |
| sys.exit() | |
| def paintStuff(self, event, qp): | |
| qp = QPainter() | |
| qp.begin(self) | |
| self.paintFlyers(event, qp) | |
| qp.end() | |
| def paintFlyers(self, event, qp): | |
| qp.setPen(QColor(0, 0, 0)) | |
| pen = qp.pen() | |
| for flyer in Flyer.swarm: | |
| pen.setWidth(1) | |
| pen.setColor(QColor(200, 200, 200)) | |
| qp.setPen(pen) | |
| qp.drawLine(flyer.position[0], flyer.position[1], flyer.position[0] + | |
| flyer.heading[0], flyer.position[1]+flyer.heading[1]) | |
| pen.setWidth(3) | |
| pen.setColor(QColor(0, 0, 0)) | |
| qp.setPen(pen) | |
| qp.drawPoint(flyer.position[0], flyer.position[1]) | |
| def updateWorld(self): | |
| point = QCursor.pos() | |
| mouse_location[0] = point.x() | |
| mouse_location[1] = point.y() | |
| length = len(Flyer.swarm) | |
| t1 = Thread(target=act_range(0, int(length/4))) | |
| t2 = Thread(target=act_range(int(length/4), int(length/2))) | |
| t3 = Thread(target=act_range(int(length/2), int((length/4)*3))) | |
| t4 = Thread(target=act_range(int((length/4)*3), length)) | |
| t1.start() | |
| t2.start() | |
| t3.start() | |
| t4.start() | |
| t1.join() | |
| t2.join() | |
| t3.join() | |
| t4.join() | |
| for flyer in Flyer.swarm: | |
| flyer.execute_move() | |
| self.repaint() | |
| def act_range(start, end): | |
| for i in range(start, end): | |
| Flyer.swarm[i].act() | |
| if __name__ == "__main__": | |
| def run(): | |
| app = QApplication(sys.argv) | |
| Gui = window() | |
| sys.exit(app.exec_()) | |
| run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment