Example #1
0
    def move(self, magnitude, direction=None, min_angle=0, max_angle=360, phasing=False):
        assert min_angle <= max_angle, "The minimum angle must be smaller than maximum angle."
        angle = math.degrees(math.acos(direction[0] / 0)) if direction \
            else math.radians(random() * (max_angle - min_angle) + min_angle)
        x = magnitude * math.cos(angle)
        y = magnitude * math.sin(angle)
        displacement = np.array([x, y])
        squared_distance = np.dot(displacement, displacement)
        departure = self.get_center()
        destination = departure + displacement
        self.set_center(destination[0], destination[1])

        # Check if the particle is still inside the zone.
        if not self.world.shape.confines_circle(self):
            destination = self.world.shape.confine_circle_coord(self, departure, destination)
            displacement = destination - departure
            squared_distance = np.dot(displacement, displacement)
            self.set_center(destination[0], destination[1])
            if not self.world.shape.confines_circle(self):
                canvas.create_oval(destination[0] - self.get_radius(), destination[1] - self.get_radius(),
                                   destination[0] + self.get_radius(), destination[1] + self.get_radius(), fill="red")

        # Check if the particle collides with other particles along its path.
        if not math.isclose(squared_distance, 0, rel_tol=1e-09):
            self.set_center(departure[0], departure[1])
            quadrants = self.world.grid.rectangle_overlap(departure, destination, self.get_radius(), canvas)
            trajectory = formula.Segment(departure, destination)
            particles = set()
            obstacles = set()
            for quadrant in quadrants:
                contents = quadrant.contents()
                for content in contents:
                    if content != self:
                        particles.add(content)
                        point = content.get_center()
                        vector = point - departure
                        theta = formula.angle_between(displacement, vector)
                        if theta < 90 and not math.isclose(theta, 90):
                            distance_from_trajectory = trajectory.squared_distance_from_point(point)
                            distance_from_obstacle = self.squared_distance_from_point(point)
                            rectangle_width = self.get_radius() + content.get_radius()
                            if distance_from_trajectory < math.pow(rectangle_width, 2):
                                distance_along_trajectory = distance_from_obstacle - distance_from_trajectory \
                                    if not math.isclose(distance_from_trajectory, 0, rel_tol=1e-09) \
                                    else distance_from_obstacle
                                rectangle_length = math.sqrt(
                                    squared_distance) + self.get_radius() + content.get_radius()
                                if distance_along_trajectory < math.pow(rectangle_length, 2):
                                    obstacles.add(content)
                                    # content.redraw(canvas, fill="red")
            obstacles = list(obstacles)
            obstacles.sort(key=lambda particle: self.distance_from_circle(particle))
            non_obstacles = list(particles.difference(obstacles))
            non_obstacles.sort(key=lambda particle: self.distance_from_circle(particle))

            # Movement stops at the nearest obstacle.
            j = 0
            while j < len(obstacles):
                point = obstacles[j].get_center()
                distance_from_trajectory = trajectory.squared_distance_from_point(point)
                distance_from_obstacle = math.pow(obstacles[j].get_radius() + self.get_radius(), 2)
                projection = formula.project_vector(point - departure, displacement)
                distance_from_position = math.sqrt(distance_from_obstacle - distance_from_trajectory)
                delta = formula.resize_vector(displacement, distance_from_position)
                destination = departure + projection - delta
                vector = destination - departure
                squared_magnitude = np.dot(vector, vector)

                # Check if the magnitude of the corrected displacement is less than or equal to the original.
                if squared_magnitude > squared_distance and not math.isclose(squared_magnitude, squared_distance):
                    displacement = formula.resize_vector(vector, math.sqrt(squared_distance))
                    destination = departure + displacement
                    squared_distance = np.dot(displacement, displacement)

                # Check if the particle collides with other obstacles.
                self.set_center(destination[0], destination[1])
                j += 1
                while j < len(obstacles):
                    if self.overlaps_circle(obstacles[j]):
                        break
                    j += 1

            # Check if the particle overlaps other particles that were outside the path.
            for j in range(len(non_obstacles)):
                if self.overlaps_circle(non_obstacles[j]):
                    destination = departure
                    break

            # Update the coordinate of the particle.
            displacement = destination - departure
            squared_distance = np.dot(displacement, displacement)
            self.set_center(destination[0], destination[1])

            # Update the quadtree.
            if not math.isclose(squared_distance, 0, rel_tol=1e-09):
                for quadrant in quadrants:
                    if self in quadrant.contents():
                        quadrant.contents().remove(self)
                quadrants = self.world.grid.overlapped_by_circle(self)
                for quadrant in quadrants:
                    if self not in quadrant.contents() and len(quadrant.leaves()) == 0:
                        quadrant.contents().append(self)
                self.redraw(canvas)
Example #2
0
    def rectangle_overlap(self, start, end, margin, canvas):
        queue = [self._root]
        quadrants = []

        # Vectors used to find the four corners of the rectangle.
        v1 = formula.resize_vector(end - start, margin)
        v2 = -v1
        v3 = formula.rotate_vector(v1, 90)
        v4 = -v3

        # The four corners of the rectangle.
        p1 = start + v2 + v3
        p2 = start + v2 + v4
        p3 = end + v1 + v3
        p4 = end + v1 + v4

        # The borders of the rectangle
        border_12 = formula.Segment(p1, p2)
        border_13 = formula.Segment(p1, p3)
        border_24 = formula.Segment(p2, p4)
        border_34 = formula.Segment(p3, p4)

        # Visual representation of the rectangle.
        # canvas.create_line(p1[0], p1[1], p2[0], p2[1], fill="blue", width=2)
        # canvas.create_line(p3[0], p3[1], p4[0], p4[1], fill="blue", width=2)
        # canvas.create_line(p1[0], p1[1], p3[0], p3[1], fill="blue", width=2)
        # canvas.create_line(p2[0], p2[1], p4[0], p4[1], fill="blue", width=2)

        # Iterate through a list of quadrants overlapped by the rectangle.
        while len(queue) > 0:
            quadrant = queue.pop(0)
            center = quadrant.get_center()
            width = quadrant.get_width()
            height = quadrant.get_height()
            centerline = formula.Segment(start, end)
            distance_from_centerline = centerline.distance_from_point(center)

            # The boundary of the current quadrant.
            x1 = center[0] - width / 2
            x2 = center[0] + width / 2
            y1 = center[1] - height / 2
            y2 = center[1] + height / 2

            # The four corners of the current quadrant.
            north_west = np.array([x1, y1])
            north_east = np.array([x2, y1])
            south_west = np.array([x1, y2])
            south_east = np.array([x2, y2])

            # The borders of the current quadrant.
            north_border = formula.Segment(north_west, north_east)
            south_border = formula.Segment(south_west, south_east)
            west_border = formula.Segment(north_west, south_west)
            east_border = formula.Segment(north_east, south_east)

            # Check if the quadrant is inside the rectangle.
            if distance_from_centerline < margin or math.isclose(
                    distance_from_centerline, margin):
                quadrants.append(quadrant)
                queue += quadrant.leaves()

            # Check if one of the borders of the rectangle goes through the center of the current quadrant.
            elif border_12.intersects_point(center)\
                    or border_13.intersects_point(center)\
                    or border_24.intersects_point(center)\
                    or border_34.intersects_point(center):
                quadrants.append(quadrant)
                queue += quadrant.leaves()

            # Check if one of the corners of the rectangle is inside the current quadrant.
            elif quadrant.contains_point(p1):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif quadrant.contains_point(p2):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif quadrant.contains_point(p3):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif quadrant.contains_point(p4):
                quadrants.append(quadrant)
                queue += quadrant.leaves()

            # Check if one of the segments of the rectangle intersects a quadrant.
            elif west_border.intersects_segment(border_12)\
                    or east_border.intersects_segment(border_12)\
                    or north_border.intersects_segment(border_12)\
                    or south_border.intersects_segment(border_12):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif west_border.intersects_segment(border_13)\
                    or east_border.intersects_segment(border_13)\
                    or north_border.intersects_segment(border_13)\
                    or south_border.intersects_segment(border_13):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif west_border.intersects_segment(border_24)\
                    or east_border.intersects_segment(border_24)\
                    or north_border.intersects_segment(border_24)\
                    or south_border.intersects_segment(border_24):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
            elif west_border.intersects_segment(border_34)\
                    or east_border.intersects_segment(border_34)\
                    or north_border.intersects_segment(border_34)\
                    or south_border.intersects_segment(border_34):
                quadrants.append(quadrant)
                queue += quadrant.leaves()
        return quadrants
Example #3
0
    def search(self):
        if self.field_of_view and len(self.field_of_view) == 2:
            # Get the central vision.
            facing_direction_vector = self.direction()
            # central_vision_extent = self._center + facing_direction_vector
            # self._center = self._center + formula.resize_vector(facing_direction_vector, self._radius)

            # Get the left outer boundary of the peripheral vision.
            left_outer_boundary_vector = formula.rotate_vector(facing_direction_vector, self.field_of_view[1] / 2)
            left_outer_boundary_extent = self._center + left_outer_boundary_vector
            left_outer_boundary = formula.Segment(self._center, left_outer_boundary_extent)

            # Get the right outer boundary of the peripheral vision.
            right_outer_boundary_vector = formula.rotate_vector(facing_direction_vector, -self.field_of_view[1] / 2)
            right_outer_boundary_extent = self._center + right_outer_boundary_vector
            right_outer_boundary = formula.Segment(self._center, right_outer_boundary_extent)

            # Draw the field of view.
            canvas.create_line(self._center[0], self._center[1], left_outer_boundary_extent[0],
                               left_outer_boundary_extent[1], fill="blue", width=2)
            canvas.create_line(self._center[0], self._center[1], right_outer_boundary_extent[0],
                               right_outer_boundary_extent[1], fill="blue", width=2)
            # circle_zone.canvas.create_line(self._center[0], self._center[1], central_vision_extent[0],
            # central_vision_extent[1], fill="blue", width=2)
            canvas.create_arc(self._center[0] - self.field_of_view[0],
                              self._center[1] - self.field_of_view[0],
                              self._center[0] + self.field_of_view[0],
                              self._center[1] + self.field_of_view[0],
                              outline="blue", width=2, style=tk.ARC,
                              start=360 - (self._rotation + self.field_of_view[1] / 2),
                              extent=self.field_of_view[1])

            # Find the quadrants of the world that are inside the field of view.
            quadrants = []
            queue = [self.world.grid.get_root()]
            particles_searched = 0
            particles_selected = 0
            quadrants_searched = 0
            quadrants_selected = 0
            while len(queue) > 0:
                quadrants_searched += 1
                quadrant = queue.pop(0)
                center = quadrant.get_center()
                width = quadrant.get_width()
                height = quadrant.get_height()

                # The boundaries of the current quadrant.
                x1 = center[0] - width / 2
                x2 = center[0] + width / 2
                y1 = center[1] - height / 2
                y2 = center[1] + height / 2

                # The four corners of the current quadrant.
                north_west = np.array([x1, y1])
                north_east = np.array([x2, y1])
                south_west = np.array([x1, y2])
                south_east = np.array([x2, y2])

                # Vectors obtained by joining the center of the particle to each corner of the quadrant.
                cnw = north_west - self._center
                cne = north_east - self._center
                csw = south_west - self._center
                cse = south_east - self._center

                # Angles between the central vision vector and the previously calculated vectors.
                angle_cnw = formula.angle_between(facing_direction_vector, cnw)
                angle_cne = formula.angle_between(facing_direction_vector, cne)
                angle_csw = formula.angle_between(facing_direction_vector, csw)
                angle_cse = formula.angle_between(facing_direction_vector, cse)
                angle_threshold = self.field_of_view[1] / 2

                # Squared distances obtained from previously calculated vectors.
                sqrd_cnw = np.dot(cnw, cnw)
                sqrd_cne = np.dot(cne, cne)
                sqrd_csw = np.dot(csw, csw)
                sqrd_cse = np.dot(cse, cse)
                squared_distance_threshold = math.pow(self.field_of_view[0], 2)

                # The borders of the current quadrant.
                north_border = formula.Segment(north_west, north_east)
                south_border = formula.Segment(south_west, south_east)
                west_border = formula.Segment(north_west, south_west)
                east_border = formula.Segment(north_east, south_east)

                # Check if the quadrant contains the particle's coordinate or the furthest points of the outer boundaries.
                if quadrant.contains_point(self._center) \
                        or quadrant.contains_point(left_outer_boundary_extent) \
                        or quadrant.contains_point(right_outer_boundary_extent):
                    quadrants.append(quadrant)
                    queue += quadrant.leaves()
                    quadrants_selected += 1

                # Check if the left outer boundary intersects with the quadrant.
                elif north_border.intersects_segment(left_outer_boundary) \
                        or south_border.intersects_segment(left_outer_boundary) \
                        or west_border.intersects_segment(left_outer_boundary) \
                        or east_border.intersects_segment(left_outer_boundary):
                    quadrants.append(quadrant)
                    queue += quadrant.leaves()
                    quadrants_selected += 1

                # Check if the right outer boundary intersects with the quadrant.
                elif north_border.intersects_segment(right_outer_boundary) \
                        or south_border.intersects_segment(right_outer_boundary) \
                        or west_border.intersects_segment(right_outer_boundary) \
                        or east_border.intersects_segment(right_outer_boundary):
                    quadrants.append(quadrant)
                    queue += quadrant.leaves()
                    quadrants_selected += 1

                # Check if quadrant is inside the field of view.
                elif (angle_cnw < angle_threshold and sqrd_cnw < squared_distance_threshold) \
                        or (angle_cne < angle_threshold and sqrd_cne < squared_distance_threshold) \
                        or (angle_csw < angle_threshold and sqrd_csw < squared_distance_threshold) \
                        or (angle_cse < angle_threshold and sqrd_cse < squared_distance_threshold):
                    quadrants.append(quadrant)
                    queue += quadrant.leaves()
                    quadrants_selected += 1

            # Get the particles that are inside the field of view.
            particles = set()
            targets = []
            for quadrant in quadrants:
                quadrant.redraw(canvas, outline="red")
                particles.update(quadrant.contents())
            for particle in particles:
                particles_searched += 1
                if particle != self:
                    vector = particle.get_center() - self._center
                    squared_distance = math.sqrt(np.dot(vector, vector))
                    angle = formula.angle_between(facing_direction_vector, vector)
                    epsilon = (2 * np.dot(vector, vector) - math.pow(particle.get_radius(), 2)) / (
                            2 * np.dot(vector, vector))
                    epsilon = math.degrees(math.acos(epsilon))
                    if (angle - epsilon < self.field_of_view[1] / 2
                        or math.isclose(angle - epsilon, self.field_of_view[1] / 2)) \
                            and (squared_distance < self.field_of_view[0] + particle.get_radius()
                                 or math.isclose(squared_distance, self.field_of_view[0] + particle.get_radius())):
                        # print(str(min_angle) + " <= " + str(a) + " <= " + str(max_angle))
                        targets.append(particle)
                        particle.redraw(canvas, fill="red")
                        particles_selected += 1
            self.redraw(canvas, fill="blue")
            targets.sort(key=lambda target: self.distance_from_circle(target), reverse=True)
            print()
            print("SEARCH")
            print("Quadrants (selected/searched): {}/{}".format(quadrants_selected, quadrants_searched))
            print("Particles (selected/searched): {}/{}".format(particles_selected, particles_searched))
            return targets
Example #4
0
    def confine_circle_coord(self, circle, start, end):
        center = self.get_center()
        radius = circle.get_radius()
        width = self._width
        height = self._height
        point = circle.get_center()

        # canvas.create_oval(point[0] - radius, point[1] - radius, point[0] + radius, point[1] + radius, fill="blue")

        # The boundary of the current quadrant.
        x1 = center[0] - width / 2
        x2 = center[0] + width / 2
        y1 = center[1] - height / 2
        y2 = center[1] + height / 2

        # The four corners of the current quadrant.
        north_west = np.array([x1, y1])
        north_east = np.array([x2, y1])
        south_west = np.array([x1, y2])
        south_east = np.array([x2, y2])

        # The borders of the current quadrant.
        north_border = formula.Segment(north_west, north_east)
        south_border = formula.Segment(south_west, south_east)
        west_border = formula.Segment(north_west, south_west)
        east_border = formula.Segment(north_east, south_east)

        segment = formula.Segment(start, end)

        if not self.contains_point(point):
            if segment.intersects_segment(north_border):
                point = segment.intersection_point(north_border)
                y = point[1] + radius
                x = segment.x(y)
                point[0], point[1] = x, y
            elif segment.intersects_segment(south_border):
                point = segment.intersection_point(south_border)
                y = point[1] - radius
                x = segment.x(y)
                point[0], point[1] = x, y
            elif segment.intersects_segment(west_border):
                point = segment.intersection_point(west_border)
                x = point[0] + radius
                y = segment.y(x)
                point[0], point[1] = x, y
            elif segment.intersects_segment(east_border):
                point = segment.intersection_point(east_border)
                x = point[0] - radius
                y = segment.y(x)
                point[0], point[1] = x, y
        # canvas.create_oval(point[0] - radius, point[1] - radius, point[0] + radius, point[1] + radius, fill="red")
        if np.sign(segment.vector[0]) < 0:
            distance_from_west = west_border.distance_from_point(point)
            if np.sign(segment.vector[1]) < 0:
                distance_from_north = north_border.distance_from_point(point)
                if distance_from_west < distance_from_north:
                    x = point[0] - distance_from_west + radius
                    y = segment.y(x)
                    point[0], point[1] = x, y
                else:
                    y = point[1] - distance_from_north + radius
                    x = segment.x(y)
                    point[0], point[1] = x, y
            elif np.sign(segment.vector[1]) > 0:
                distance_from_south = south_border.distance_from_point(point)
                if distance_from_west < distance_from_south:
                    x = point[0] - distance_from_west + radius
                    y = segment.y(x)
                    point[0], point[1] = x, y
                else:
                    y = point[1] + distance_from_south - radius
                    x = segment.x(y)
                    point[0], point[1] = x, y
        elif np.sign(segment.vector[0]) > 0:
            distance_from_east = east_border.distance_from_point(point)
            if np.sign(segment.vector[1]) < 0:
                distance_from_north = north_border.distance_from_point(point)
                if distance_from_east < distance_from_north:
                    x = point[0] + distance_from_east - radius
                    y = segment.y(x)
                    point[0], point[1] = x, y
                else:
                    y = point[1] - distance_from_north + radius
                    x = segment.x(y)
                    point[0], point[1] = x, y
            elif np.sign(segment.vector[1]) > 0:
                distance_from_south = south_border.distance_from_point(point)
                if distance_from_east < distance_from_south:
                    x = point[0] + distance_from_east - radius
                    y = segment.y(x)
                    point[0], point[1] = x, y
                else:
                    y = point[1] + distance_from_south - radius
                    x = segment.x(y)
                    point[0], point[1] = x, y


        # canvas.create_oval(point[0] - radius, point[1] - radius, point[0] + radius, point[1] + radius, fill="green")

        """distance_from_north = north_border.distance_from_point(point)
            if distance_from_north < radius and not math.isclose(distance_from_north, radius):
                y = point[1] - distance_from_north + radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_south = south_border.distance_from_point(point)
            if distance_from_south < radius and not math.isclose(distance_from_south, radius):
                y = point[1] + distance_from_south - radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_west = west_border.distance_from_point(point)
            if distance_from_west < radius and not math.isclose(distance_from_west, radius):
                x = point[0] - distance_from_west + radius
                y = segment.y(x)
                point[0], point[1] = x, y

            distance_from_east = east_border.distance_from_point(point)
            if distance_from_east < radius and not math.isclose(distance_from_east, radius):
                x = point[0] + distance_from_east - radius
                y = segment.y(x)
                point[0], point[1] = x, y
        else:
            distance_from_north = north_border.distance_from_point(point)
            if point[1] < center[1] - height / 2 + radius and not math.isclose(point[1], center[1] - height / 2 + radius):
                y = point[1] + distance_from_north + radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_south = south_border.distance_from_point(point)
            if point[1] > center[1] + height / 2 - radius and not math.isclose(point[1], center[1] + height / 2 - radius):
                y = point[1] - distance_from_south - radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_west = west_border.distance_from_point(point)
            if point[0] < center[0] - width / 2 + radius and not math.isclose(point[0], center[0] - width / 2 + radius):
                x = point[0] + distance_from_west + radius
                y = segment.y(x)
                point[0], point[1] = x, y

            distance_from_east = east_border.distance_from_point(point)
            if point[0] > center[0] + width / 2 - radius and not math.isclose(point[0], center[0] + width / 2 - radius):
                x = point[0] - distance_from_east - radius
                y = segment.y(x)
                point[0], point[1] = x, y

            distance_from_north = north_border.distance_from_point(point)
            if distance_from_north < radius and not math.isclose(distance_from_north, radius):
                y = point[1] - distance_from_north + radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_south = south_border.distance_from_point(point)
            if distance_from_south < radius and not math.isclose(distance_from_south, radius):
                y = point[1] + distance_from_south - radius
                x = segment.x(y)
                point[0], point[1] = x, y

            distance_from_west = west_border.distance_from_point(point)
            if distance_from_west < radius and not math.isclose(distance_from_west, radius):
                x = point[0] - distance_from_west + radius
                y = segment.y(x)
                point[0], point[1] = x, y

            distance_from_east = east_border.distance_from_point(point)
            if distance_from_east < radius and not math.isclose(distance_from_east, radius):
                x = point[0] + distance_from_east - radius
                y = segment.y(x)
                point[0], point[1] = x, y"""
        return point