def delauney(self): segments = [] combos = list(itertools.combinations(self.graph.nodes, 3)) #print "combinations: ", len(combos) for a, b, c in combos: centre = self.triangle_circumcenter(a, b, c) distance2 = (Vector2(a.x, a.y) - centre).magnitude_squared() smaller_found = False for node in self.graph.nodes: if node in [a, b, c]: continue if (Vector2(node.x, node.y) - centre).magnitude_squared() < distance2: smaller_found = True break if not smaller_found: segments.extend(list(itertools.combinations([a, b, c], 2))) for segment in segments: order = sorted(segment, key=lambda node: node.x + node.y) segment = (order[0], order[1]) segments = set(segments) return segments
def __init__(self): graphics.Scene.__init__(self) self.segments = [] # we should redo the boxes when window gets resized self.proximity_radius = 10 self.proximities = LQProximityStore(Vector2(0, 0), Vector2(600, 400), self.proximity_radius) self.flock = [] self.frame = 0 self.connect("on-click", self.on_mouse_click) self.connect("on-enter-frame", self.on_enter_frame)
def on_enter_frame(self, scene, context): c_graphics = graphics.Graphics(context) if len(self.flock) < 80: for i in range(2): self.flock.append( Boid(Vector2(self.width / 2, self.height / 2), 2.0, 0.05)) # main loop (i should rename this to something more obvious) c_graphics.set_line_style(width=0.8) c_graphics.set_color("#666") for boid in self.flock: neighbours = [] if self.frame % 2 == 0: #recalculate direction every second frame neighbours = self.proximities.find_neighbours(boid, 40) boid.run(neighbours) self.wrap(boid) self.proximities.update_position(boid) self.draw_boid(context, boid) self.frame += 1 context.stroke() self.redraw()
def update(self, context): self.current_frame +=1 if self.current_frame == self.frames: self.current_frame = 0 if self.boids: boid = self.boids.pop(0) boid.location = Vector2(self.location.x, self.location.y) self.move_on(boid)
def update(self, context): if len(self.boids) == self.bucket_size: self.boids_out = list(self.boids) self.boids = [] self.rotation_angle += 0.02 if self.incremental_angle: nodes = len(self.boids) or 1 else: nodes = self.bucket_size - 1 angle_step = math.pi * 2 / nodes current_angle = 0 i = 0 points = [] while i < (math.pi * 2): x = self.location.x + math.cos(self.rotation_angle + i) * self.radius y = self.location.y + math.sin(self.rotation_angle + i) * self.radius points.append(Vector2(x,y)) i += angle_step context.stroke() for boid in self.boids: distance = None closest_point = None for point in points: point_distance = (boid.location - point).magnitude_squared() if not distance or point_distance < distance: closest_point = point distance = point_distance if closest_point: target = boid.seek(closest_point) #if target.magnitude_squared() < 1: # boid.flight_angle = (self.location - boid.location).cross().heading() boid.acceleration *= 8 points.remove(closest_point) # taken else: boid.velocity *= .9 context.stroke() if self.boids_out: for boid in self.boids_out: self.move_on(boid) boid.acceleration = -(self.location - boid.location) * 2 boid.flight_angle = 0 self.boids_out = []
def __init__(self): graphics.Scene.__init__(self) # we should redo the boxes when window gets resized box_size = 10 self.proximities = LQProximityStore(Vector2(0,0), Vector2(600,400), box_size) self.waypoints = [] self.waypoints = [QueueingWaypoint(100, 100, 70), BucketWaypoint(500, 100, 10), GrowWaypoint(500, 500, 10), QueueingWaypoint(300, 500, 70), BucketWaypoint(100, 500, 10), GrowWaypoint(100, 300, 3), ] for waypoint in self.waypoints: self.add_child(waypoint) # link them together for curr, next in zip(self.waypoints, self.waypoints[1:]): curr.next = next next.previous = curr self.waypoints[0].previous = self.waypoints[-1] self.waypoints[-1].next = self.waypoints[0] self.boids = [Boid(Vector2(100,100), 2.0) for i in range(15)] for i, boid in enumerate(self.boids): boid.target(self.waypoints[0]) self.add_child(boid) self.mouse_node = None # some debug variables self.debug_radius = False self.debug_awareness = False self.connect("on-enter-frame", self.on_enter_frame)
def on_click(*args): for centre in self.canvas.centres: if abs(centre) < 2000: point = Vector2(centre.x, centre.y) self.canvas.points.append(point) node = Node(point.x, point.y, point) self.canvas.nodes.append(node) self.canvas.add_child(node) self.canvas.centres = [] self.canvas.redraw()
def __init__(self, x, y): graphics.Sprite.__init__(self, x, y, draggable = True) self.graphics.set_color("#999") self.graphics.rectangle(-4, -4, 8, 8, 2) self.graphics.fill() self.connect("on-drag", self.on_drag) self.location = Vector2(x, y) self.debug = False
def on_mouse_click(self, area, event, target): if not target: point = Vector2(event.x, event.y) self.points.append(point) node = Node(event.x, event.y, point) self.nodes.append(node) self.add_child(node) self.centres = [] self.triangulate() self.redraw()
def voronoi(self): segments = [] centres = {} for a, b, c in itertools.combinations(self.nodes, 3): centre = self.triangle_circumcenter(a, b, c) distance2 = (Vector2(a.x, a.y) - centre).magnitude_squared() smaller_found = False for node in self.nodes: if node in [a, b, c]: continue if (Vector2(node.x, node.y) - centre).magnitude_squared() < distance2: smaller_found = True break if not smaller_found: order = sorted([a, b, c], key=lambda node: node.x + node.y) for a1, b1 in itertools.combinations(order, 2): centres.setdefault((a1, b1), []).append(centre) #return centres for all points that share more than one #centres = set([c[0] for c in centres.values() if len(c) > 1]) res = [] for key in centres: if len(centres[key]) > 1: for node, node2 in zip(centres[key], centres[key][1:]): res.append((node, node2)) if len(centres[key]) > 2: res.append((centres[key][-1], centres[key][0])) res = set(res) return res
def on_enter_frame(self, scene, context): c_graphics = graphics.Graphics(context) c_graphics.set_line_style(width=0.5) c_graphics.set_color("#AA00FF") if len(self.flock) < 40: self.flock.append(Boid(Vector2(100, 100), 2.0, 0.05)) for boid in self.flock: boid.run(self.flock, context) context.stroke() context.fill() self.redraw()
def align(self, boids): sum = Vector2() in_zone = 0.0 for boid, d in boids: if 0 < d < self.neighbour_distance: sum += boid.velocity in_zone += 1 if in_zone: sum = sum / in_zone # weight by neighbour count sum.limit(self.max_force) return sum
def steer(self, target, slow_down): steer = Vector2() desired = target - self.location # A vector pointing from the location to the target d = desired.magnitude() if d > 0: desired.normalize() # Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed) if slow_down and d < 100: desired *= self.max_speed * ( d / 100.0) # This damping is somewhat arbitrary else: desired *= self.max_speed steer = desired - self.velocity # Steering = Desired minus Velocity steer.limit(self.max_force) # Limit to maximum steering force else: steer = Vector2() return steer
def triangle_circumcenter(self, a, b, c): """shockingly, the circumcenter math has been taken from wikipedia we move the triangle to 0,0 coordinates to simplify math""" p_a = Vector2(a.x, a.y) p_b = Vector2(b.x, b.y) - p_a p_c = Vector2(c.x, c.y) - p_a p_b2 = p_b.magnitude_squared() p_c2 = p_c.magnitude_squared() d = 2 * (p_b.x * p_c.y - p_b.y * p_c.x) if d < 0: d = min(d, EPSILON) else: d = max(d, EPSILON) centre_x = (p_c.y * p_b2 - p_b.y * p_c2) / d centre_y = (p_b.x * p_c2 - p_c.x * p_b2) / d centre = p_a + Vector2(centre_x, centre_y) return centre
def __init__(self, location, max_speed = 2.0): graphics.Sprite.__init__(self, snap_to_pixel=False) self.visible = True self.radius = 3 self.acceleration = Vector2() self.brake = Vector2() self.velocity = Vector2(random.random() * 2 - 1, random.random() * 2 - 1) self.location = location self.max_speed = max_speed self.max_force = 0.03 self.positions = [] self.message = None # a message that waypoint has set perhaps self.flight_angle = 0 self.data = {} self.virus = None self.radio = self.radius * 5 self.target_waypoint = None self.connect("on-render", self.on_render)
def align(self, boids): neighbour_distance = 50.0 sum = Vector2() in_zone = 0.0 for boid in boids: d = (self.location - boid.location).magnitude() if 0 < d < neighbour_distance: sum += boid.velocity in_zone += 1 if in_zone: sum = sum / in_zone # weight by neighbour count sum.limit(self.max_force) return sum
def separate(self, boids): sum = Vector2() in_zone = 0.0 for boid, d in boids: if 0 < d < self.desired_separation: diff = self.location - boid.location diff.normalize() diff = diff / math.sqrt(d) # Weight by distance sum += diff in_zone += 1 if in_zone: sum = sum / in_zone return sum
def steer(self, target, slow_down): desired = target - self.location # A vector pointing from the location to the target d = desired.magnitude_squared() if d > 0: # this means that we have a target desired.normalize() # Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed) if slow_down and d > self.braking_distance: desired *= self.max_speed * d / self.braking_distance # This damping is somewhat arbitrary else: desired *= self.max_speed steer = desired - self.velocity # Steering = Desired minus Velocity steer.limit(self.max_force) # Limit to maximum steering force return steer else: return Vector2()
def update_position(self, w, h): distance = 0 if self.target: distance = (self.target - self.location).magnitude_squared() if not self.target or self.prev_distance and distance > self.prev_distance: self.prev_distance = w * w + h * h self.target = Vector2(randint(0, w), randint(0, h)) target = (self.target - self.location) self.acceleration = (target - self.velocity).normalize() * 2 self.velocity += self.acceleration self.velocity.limit(20) self.prev_distance = distance self.location += self.velocity
def separate(self, boids): desired_separation = 25.0 sum = Vector2() in_zone = 0.0 for boid in boids: d = (self.location - boid.location).magnitude() if 0 < d < desired_separation: diff = self.location - boid.location diff.normalize() diff = diff / d # Weight by distance sum += diff in_zone += 1 if in_zone: sum = sum / in_zone return sum
def cohesion(self, boids): """ For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location""" neighbour_distance = 50.0 sum = Vector2() in_zone = 0.0 for boid in boids: d = (self.location - boid.location).magnitude() if 0 < d < neighbour_distance: sum += boid.location in_zone += 1 if in_zone: sum = sum / in_zone return self.steer(sum, False) return sum
def cohesion( self, boids, ): """ For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location""" sum = Vector2() in_zone = 0 for boid, d in boids: if 0 < d < self.neighbour_distance: sum = sum + boid.location in_zone += 1 if in_zone: sum = sum / float(in_zone) return self.steer(sum, True) return sum
def separate(self, boids): sum = Vector2() in_zone = 0.0 for boid, d in boids: if not boid.visible: continue if 0 < d < self.radius * 5 * self.radius * 5: diff = self.location - boid.location diff.normalize() diff = diff / math.sqrt(d) # Weight by distance sum += diff in_zone += 1 if in_zone: sum = sum / in_zone sum.limit(self.max_force) return sum
def draw_tangent(self): tangent = self.tangent tangent.graphics.clear() tangent.graphics.set_line_style(width=0.5) band_radius = 30 v1 = Vector2(self.circle1.x, self.circle1.y) v2 = Vector2(self.circle2.x, self.circle2.y) distance = abs(v1 - v2) tangent.graphics.set_color("#000") #tangent.graphics.move_to(v1.x, v1.y) #tangent.graphics.line_to(v2.x, v2.y) c = distance distance = 100 a = distance + self.circle2.radius b = distance + self.circle1.radius orientation = (v2 - v1).heading() # errrm, well, basically the one is in the other if (b**2 + c**2 - a**2) / (2.0 * b * c) >= 1: tangent.graphics.arc( v1.x, v1.y, max(self.circle1.radius, self.circle2.radius) + band_radius, 0, math.pi * 2) tangent.graphics.stroke() return # we have to figure out the angle for the vector that is pointing # towards the point C (which will help as to draw that tangent) left_angle = math.acos((b**2 + c**2 - a**2) / (2.0 * b * c)) arc_angle = math.acos((a**2 + b**2 - c**2) / (2.0 * a * b)) # arc on the one side a1 = left_angle + orientation x, y = math.cos(a1) * b, math.sin(a1) * b v3_1 = Vector2(v1.x + x, v1.y + y) tangent.graphics.arc(v3_1.x, v3_1.y, distance - band_radius, (v1 - v3_1).heading(), (v2 - v3_1).heading()) tangent.graphics.stroke() # arc on the other side (could as well flip at the orientation axis, too dumb to do that though) a2 = -left_angle + orientation x, y = math.cos(a2) * b, math.sin(a2) * b v3_2 = Vector2(v1.x + x, v1.y + y) tangent.graphics.arc(v3_2.x, v3_2.y, distance - band_radius, (v2 - v3_2).heading(), (v1 - v3_2).heading()) tangent.graphics.stroke() # the rest of the circle tangent.graphics.arc(v1.x, v1.y, self.circle1.radius + band_radius, (v3_1 - v1).heading(), (v3_2 - v1).heading()) tangent.graphics.stroke() tangent.graphics.arc_negative(v2.x, v2.y, self.circle2.radius + band_radius, (v3_1 - v2).heading(), (v3_2 - v2).heading()) tangent.graphics.stroke()
def __init__(self): self.acceleration = Vector2() self.velocity = Vector2() self.location = Vector2(150, 150)
def on_mouse_click(self, widget, event, target): self.flock.append(Boid(Vector2(event.x, event.y), 2.0, 0.05))
def __init__(self, location, max_speed, max_force): self.acceleration = Vector2() self.velocity = Vector2(random() * 2 - 1, random() * 2 - 1) self.location = location self.max_speed = max_speed self.max_force = max_force