Example #1
0
    def raycast(self, point, direction, *, callback, max_distance=None):
        """ Implementation taken directly from Box2D, as it's quite extensible
        there and optimized.
            This tree does not quite give as an easy way to get the `first`
        hit effitiently, but we can iterate over all objects, that ray MAY
        hit quite fast.
            If we want to get the `first` hit, we can check each object for ray
        intersection in callback and return a modified max_distance version,
        that will limit the search area, with last found element being our
        `first` hit. For example:

            res = {'res': None}

            def cb(obj, point, direction, max_distance, res=res):
                ray_hit = RAY_TEST(obj, point, direction, max_distance)
                if ray_hit is not None:
                    res['res'] = obj
                    _, hit_dist = ray_hit
                    return hit_dist
                return None

            tree.raycast(p, d, max_distance=1000, callback=cb)


        If we need any element, just return 0.0 from callback, it will stop
        traversing the tree.
        Has no return value, use callback for that.

        NOTE: I know, that this interface is very un-pythonic, but a generator
        is even harder to work with, as it requires usage of `send` API for
        shortening the distance.
        """
        assert abs(direction.length2() - 1) < EPSILON

        if self._root is None:
            return None

        v = direction.rotate_deg(90)
        abs_v = Vector(math.fabs(v.x), math.fabs(v.y))

        if max_distance is None:
            max_distance = float("inf")

        p2 = point + direction * max_distance
        segment_aabb = AABB(min_vector(point, p2), max_vector(point, p2))

        node_stack = [self._root]
        last_result = None
        while node_stack:
            node = node_stack.pop()
            # First check AABB
            if not node.aabb.intersects(segment_aabb):
                continue
            # Separating axis for segment (Gino, p80).
            # |dot(v, p1 - c)| > dot(|v|, h)
            c = node.aabb.center
            h = node.aabb.extents
            separation = abs(v.dot(point - c)) - abs_v.dot(h)
            if separation > 0:
                continue
            # Ok, now we know, this AABB intersects the ray
            if node.leaf:
                value = callback(node.obj, point, direction, max_distance)
                if value is None:
                    continue
                last_result = node.obj
                if value == 0:
                    # Client has terminated the raycast
                    return last_result
                if value > 0:
                    # Fixup the bounds of our AABB
                    max_distance = value
                    p2 = point + direction * max_distance
                    segment_aabb = AABB(
                        min_vector(point, p2), max_vector(point, p2))
            else:
                node_stack.append(node.left)
                node_stack.append(node.right)
        return last_result
Example #2
0
 def test_vector_dot(self):
     u = Vector(2, 5)
     v = Vector(3, 7)
     self.assertEqual(u.dot(v), 41)
     self.assertEqual(v.dot(u), 41)