Exemple #1
0
def test_unique():
    tree = AABBTree()
    aabb1 = AABB([(0, 1)])
    aabb2 = AABB([(0, 1)])
    aabb3 = AABB([(0, 1)])
    tree.add(aabb1, 'box 1')
    tree.add(aabb2, 'box 2')
    vals = tree.overlap_values(aabb3, unique=True)
    assert len(vals) == 1

    vals = tree.overlap_values(aabb3, unique=False)
    assert len(vals) == 2
    assert 'box 1' in vals
    assert 'box 2' in vals
class WayContainerAABBTree:
    def __init__(self, d_max=0):
        self.d_max = d_max
        self.data = AABBTree()

    def __del__(self):
        pass

    def insert(self, element):
        a, b = element.get_axis_aligned_bounding_box()
        aabb = AABB([(a[0], b[0]), (a[1], b[1])])
        self.data.add(aabb, element)

    def find_near_candidates(self, lat_lon, d_max):
        if not math.isfinite(lat_lon[0]) or not math.isfinite(lat_lon[1]):
            return []

        # transfer bounding box of +/- d_max (in meter) to a +/- d_lon and d_lat
        # (approximate, but very good for d_max << circumference earth)
        d_lat, d_lon = LocalMap.get_scale_at(lat_lon[0], lat_lon[1])
        d_lat *= d_max
        d_lon *= d_max

        # define an axis-aligned bounding box (in lat/lon) around the queried point lat_lon
        bb = AABB([(lat_lon[0] - d_lat, lat_lon[0] + d_lat),
                   (lat_lon[1] - d_lon, lat_lon[1] + d_lon)])

        # and query all overlapping bounding boxes of ways
        candidates = self.data.overlap_values(bb)

        return candidates

    @staticmethod
    def axis_aligned_bounding_boxes_overlap(a1, b1, a2, b2):
        return np.all(a1 < b2) and np.all(a2 < b1)
Exemple #3
0
def test_overlap_values():
    aabbs = standard_aabbs()
    values = ['value 1', 3.14, None, None]

    aabb5 = AABB([(-3, 3.1), (-3, 3)])
    aabb6 = AABB([(0, 1), (5, 6)])
    aabb7 = AABB([(6.5, 6.5), (5.5, 5.5)])

    for indices in itertools.permutations(range(4)):
        tree = AABBTree()
        for i in indices:
            tree.add(aabbs[i], values[i])

        vals5 = tree.overlap_values(aabb5)
        assert len(vals5) == 2
        for val in ('value 1', 3.14):
            assert val in vals5

        assert tree.overlap_values(aabb6) == []
        assert tree.overlap_values(aabb7) == []

    assert AABBTree(aabb5).overlap_values(aabb7) == []
Exemple #4
0
def test_return_the_origin_pass_in_value():
    class Foo:
        pass

    tree = AABBTree()
    value_set = {Foo() for _ in range(10)}

    for value in value_set:
        tree.add(AABB([(0, 1), (0, 1)]), value=value)

    retrieved_value_set = set(
        tree.overlap_values(AABB([(0, 2), (0, 2)]), unique=False))
    assert retrieved_value_set == value_set
Exemple #5
0
class Collision_detection_AABB(Collision_Detection):
	def CheckCollisions(self , rects):
		self.tree = AABBTree()
		d = {i:rects[i] for i in range(len(rects))}
		j = 0
		for i in rects:
			aabb = AABB([(i.x, i.x+i.w),(i.y, i.y+i.h)])
			self.tree.add(aabb, j)
			j += 1
		
		s = set()
		for i in rects:
			a = self.tree.overlap_values(AABB([(i.x, i.x+i.w), (i.y, i.y+i.h)]))
			s = s.union({frozenset([i,d[j]]) for j in a if i != d[j]})
		return s
class RoadContainerAABBtree:
    def __init__(self, d_max=0):
        self.d_max = d_max
        self.data = AABBTree()

    def __del__(self):
        pass

    def insert(self, element):
        a, b = element.get_axis_aligned_bounding_box()

        e = AABB([(a[0], b[0]), (a[1], b[1])])
        self.data.add(e, element)

    def find_near(self, x, direction):
        # find candidates, exclude only those which are safe to exclude
        candidates = self.find_near_candidates(x, self.d_max)
        # then enumerate all candidates an do precise search
        dist_x_list = []
        dist_dir_list = []
        x_projected_list = []
        way_direction_list = []
        for r in candidates:
            dist_x, x_projected, dist_dir, way_direction = r.distance_of_point(
                x, direction)
            dist_x_list.append(dist_x)
            dist_dir_list.append(dist_dir)
            x_projected_list.append(x_projected)
            way_direction_list.append(way_direction)
        return candidates, dist_x_list, x_projected_list, dist_dir_list, way_direction_list

    def find_near_candidates(self, x, d_max):
        if not math.isfinite(x[0]) or not math.isfinite(x[1]):
            return []

        # the point x and a square environment
        bb = AABB([(x[0] - d_max, x[0] + d_max), (x[1] - d_max, x[1] + d_max)])

        candidates = self.data.overlap_values(bb)

        return candidates

    def axis_aligned_bounding_boxes_overlap(self, a1, b1, a2, b2):
        return np.all(a1 < b2) and np.all(a2 < b1)
Exemple #7
0
def endpoint_statistics(path_to_svg):
    '''
    Given:
        path_to_svg: A path to an SVG file.
    
    Normalizes by the svg's long edge as defined by its viewBox.
    
    Ignores <svg> width or height attributes.

    '''

    global_scale = 1.0

    try:
        doc = Document(path_to_svg)
        flatpaths = doc.flatten_all_paths()
        paths = [path for (path, _, _) in flatpaths]
    except:
        global_scale = get_global_scale(doc.tree)
        ## Let's truly fail if there are transform nodes we can't handle.
        # try: global_scale = get_global_scale( doc.tree )
        # except: print( "WARNING: There are transforms, but flatten_all_paths() failed. Falling back to unflattened paths and ignoring transforms.", file = sys.stderr )

        paths, _ = svg2paths(path_to_svg)

    ## First pass: Gather all endpoints, path index, segment index, t value
    endpoints = []  # a copy of point coordinations
    endpoints_p = [
    ]  # real points, we will do the snapping by changing points in this list
    endpoint_addresses = []

    for path_index, path in enumerate(paths):
        for seg_index, seg in enumerate(path):
            for t in (0, 1):
                pt = seg.point(t)
                endpoints.append((pt.real, pt.imag))
                endpoint_addresses.append((path_index, seg_index, t))

    print("Creating spatial data structures:")
    ## Point-point queries.
    dist_finder = scipy.spatial.cKDTree(endpoints)
    ## Build an axis-aligned bounding box tree for the segments.
    bbtree = AABBTree()  # but, why?
    # for path_index, path in tqdm( enumerate( paths ), total = len( paths ), ncols = 50 ):
    for path_index, path in enumerate(paths):
        for seg_index, seg in enumerate(path):
            xmin, xmax, ymin, ymax = seg.bbox(
            )  # record bbox of each segmentation?
            bbtree.add(AABB([(xmin, xmax), (ymin, ymax)]),
                       (path_index, seg_index, seg))

    # Second pass: Gather all minimum distances
    print("Finding minimum distances:")

    minimum_distances = []
    for i, (pt, (path_index, seg_index,
                 t)) in enumerate(zip(endpoints, endpoint_addresses)):
        ## 1. Find the minimum distance to any other endpoints

        ## Find two closest points, since the point itself is in dist_finder with distance 0.
        mindist, closest_pt_indices = dist_finder.query([pt], k=2)

        ## These come back as 1-by-2 matrices.
        mindist = mindist[0]
        closest_pt_indices = closest_pt_indices[0]
        ## If we didn't find 2 points, then pt is the only point in this file.
        ## There is no point element in SVG, so that should never happen.

        assert len(closest_pt_indices) == 2
        ## If there are two or more other points identical to pt,
        ## then pt might not actually be one of the two returned, but both distances
        ## should be zero.
        assert i in closest_pt_indices or (mindist < eps).all()
        assert min(mindist) <= eps

        ## The larger distance corresponds to the point that is not pt.
        mindist = max(mindist)

        ## If we already found the minimum distance is 0, then there's no point also
        ## searching for T-junctions.
        if mindist < eps:
            minimum_distances.append(mindist)
            continue

        ## 2. Find the closest point on any other paths (T-junction).
        ## We are looking for any segments closer than mindist to pt.
        # why? why mindist?
        query = AABB([(pt[0] - mindist, pt[0] + mindist),
                      (pt[1] - mindist, pt[1] + mindist)])

        for other_path_index, other_seg_index, seg in bbtree.overlap_values(
                query):
            ## Don't compare the point with its own segment.
            if other_path_index == path_index and other_seg_index == seg_index:
                continue

            ## Optimization: If the distance to the bounding box is larger
            ## than mindist, skip it.
            ## This is still relevant, because mindist will shrink as we iterate over
            ## the results of our AABB tree query.
            # why? this is also not reasonable to me
            xmin, xmax, ymin, ymax = seg.bbox()
            if (pt[0] < xmin - mindist or pt[0] > xmax + mindist
                    or pt[1] < ymin - mindist or pt[1] > ymin + mindist):
                continue

            ## Get the point to segment distance.
            dist_to_other_path = distance_point_to_segment(pt, seg)
            ## Keep it if it's smaller.
            if mindist is None or dist_to_other_path < mindist:
                mindist = dist_to_other_path

            ## Terminate early if the minimum distance found already is 0
            if mindist < eps: break

        ## Accumulate the minimum distance
        minimum_distances.append(mindist)

    minimum_distances = global_scale * asfarray(minimum_distances)

    ## Divide by long edge.
    if 'viewBox' in doc.root.attrib:
        import re
        _, _, width, height = [
            float(v)
            for v in re.split('[ ,]+', doc.root.attrib['viewBox'].strip())
        ]
        long_edge = max(width, height)
        print("Normalizing by long edge:", long_edge)
        minimum_distances /= long_edge
    elif "width" in doc.root.attrib and "height" in doc.root.attrib:
        width = doc.root.attrib["width"].strip().strip("px")
        height = doc.root.attrib["height"].strip().strip("px")
        long_edge = max(float(width), float(height))
        print("Normalizing by long edge:", long_edge)
        minimum_distances /= long_edge
    else:
        print(
            "WARNING: No viewBox found in <svg>. Not normalizing by long edge."
        )
    print("Done")
    return minimum_distances
Exemple #8
0
class Collision:
    def __init__(self, objects=None):
        self.tree = AABBTree()
        if objects:
            for item in objects:
                if item["type"] == "CUBE":
                    self.add_object(item["pos"],
                                    (item["scale"][0] / 2, item["scale"][1] /
                                     2, item["scale"][2] / 2), item["goal"])
                elif item["type"] == "SPHERE":
                    self.add_object(item["pos"], item["scale"], item["goal"])

    # Returns AABB object with coorect values based on a point and offset
    def get_aabb(self, point, bound):
        return AABB([(point.x - bound[0], point.x + bound[0]),
                     (point.y - bound[1], point.y + bound[1]),
                     (point.z - bound[2], point.z + bound[2])])

    # Add object with position and scale to the tree
    # Sets the position as the value returned in case of collision
    def add_object(self, position, scale, goal):
        self.tree.add(self.get_aabb(position, scale), {
            "pos": position,
            "scale": scale,
            "goal": goal
        })

    # Makes checking for collision on point easier
    def point_collision(self, point, bound):
        return self.tree.does_overlap(self.get_aabb(point, bound))

    # Returns the point object of collided objects
    def collision_objects(self, point, bound):
        return self.tree.overlap_values(self.get_aabb(point, bound))

    # checks if player is between bounds of object on an axis with respect to size
    def is_between(self, player, obj, axis):
        return obj["pos"][axis] - obj["scale"][axis] - player["scale"][
            axis] < player["pos"][axis] < obj["pos"][axis] + obj["scale"][
                axis] + player["scale"][axis]

    # Finds which side of a cube the player is touching
    def get_colliding_face(self, player, obj):
        # Zeroes the axis of the plane on the object the player is touching
        return (1 if self.is_between(player, obj, 0) else 0,
                1 if self.is_between(player, obj, 1) else 0,
                1 if self.is_between(player, obj, 2) else 0)

    # Returns surface vector based on player direction
    def get_surface_vector(self, player, obj):
        directions = self.get_colliding_face(player, obj)
        # Returns a normalized vector that represents the direction of the surface
        # Direction on the non zeroed axes based on player movement
        return Vector(player["direction"].x * directions[0],
                      player["direction"].y * directions[1],
                      player["direction"].z * directions[2]).normalize()

    # Returns slide vector for player on collided object
    def get_slide_vector(self, player, obj):
        surface_vector = self.get_surface_vector(player, obj)
        return surface_vector * player["direction"].dot(surface_vector)

    # Returns motion vector of player
    def move(self, player):
        # collision member set to advoid key error
        player["collision"] = []
        # If no collision return player directly
        if not self.point_collision(player["newpos"], player["scale"]):
            return player
        else:
            # If collision, get slide vector for each object collided with
            for item in self.collision_objects(player["newpos"],
                                               player["scale"]):
                player["direction"] = self.get_slide_vector(player, item)
                player["collision"].append(
                    self.get_colliding_face(player, item))
                if item["goal"]:
                    player["collision"].append((0, 0, 0))
            return player