Esempio n. 1
0
 def from_points(cls, points):
     """Create a line segment from one or more collinear points.  The first
     point is assumed to be the anchor.  The order of the remaining points
     is unimportant, however they must all be collinear.  The furthest
     point from the anchor determines the line segment's vector.
     
     :param points: Iterable of at least 2 distinct points.
     """
     points = iter(points)
     try:
         start = end = planar.Vec2(*points.next())
     except StopIteration:
         raise ValueError("Expected iterable of 1 or more points")
     furthest = 0.0
     pt_vectors = []
     for p in points:
         p = planar.Vec2(*p)
         dist = (p - start).length2
         if dist > furthest:
             furthest = dist
             end = p
         pt_vectors.append(p)
     segment = _LinearGeometry.__new__(cls)
     if end != start:
         segment.vector = end - start
     else:
         # degenerate case
         segment.direction = (1, 0)
         segment.length = 0.0
     segment._anchor = start
     for p in pt_vectors:
         if not segment.contains_point(p):
             raise ValueError("All points provided must be collinear")
     return segment
Esempio n. 2
0
    def from_shapes(cls, shapes):
        """Creating a bounding box that completely encloses all of the
        shapes provided.
        """
        shapes = iter(shapes)
        try:
            shape = next(shapes)
        except StopIteration:
            raise ValueError(
                ("BoundingBox.from_shapes(): requires at least one shape"))
        min_x, min_y = shape.bounding_box.min_point
        max_x, max_y = shape.bounding_box.max_point

        for shape in shapes:
            x, y = shape.bounding_box.min_point
            if x < min_x:
                min_x = x
            if y < min_y:
                min_y = y
            x, y = shape.bounding_box.max_point
            if x > max_x:
                max_x = x
            if y > max_y:
                max_y = y
        box = object.__new__(cls)
        box._min = planar.Vec2(min_x, min_y)
        box._max = planar.Vec2(max_x, max_y)
        return box
Esempio n. 3
0
 def _pt_tangents(self, point):
     """Return the pair of tangent points for the given exterior point.
     This general algorithm works for all polygons in O(n) time.
     """
     px, py = point
     left_tan = right_tan = self[0]
     verts = iter(self)
     v0_x, v0_y = self[-2]
     v1_x, v1_y = self[-1]
     prev_turn = (v1_x - v0_x) * (py - v0_y) - (px - v0_x) * (v1_y - v0_y)
     v0_x = v1_x
     v0_y = v1_y
     for v1_x, v1_y in self:
         next_turn = (v1_x - v0_x) * (py - v0_y) - (px - v0_x) * (v1_y -
                                                                  v0_y)
         if prev_turn <= 0.0 and next_turn > 0.0:
             if ((v0_x - px) * (right_tan.y - py) - (right_tan.x - px) *
                 (v0_y - py) >= 0.0):
                 right_tan = planar.Vec2(v0_x, v0_y)
         elif prev_turn > 0.0 and next_turn <= 0.0:
             if ((v0_x - px) * (left_tan.y - py) - (left_tan.x - px) *
                 (v0_y - py) <= 0.0):
                 left_tan = planar.Vec2(v0_x, v0_y)
         v0_x = v1_x
         v0_y = v1_y
         prev_turn = next_turn
     return left_tan, right_tan
Esempio n. 4
0
def test_set_epsilon():
    import planar
    old_e = planar.EPSILON
    assert not planar.Vec2(0, 0).almost_equals((0.01, 0))
    try:
        planar.set_epsilon(0.02)
        assert_equal(planar.EPSILON, 0.02)
        assert_equal(planar.EPSILON2, 0.0004)
        assert planar.Vec2(0, 0).almost_equals((0.01, 0))
    finally:
        planar.set_epsilon(old_e)
    assert_equal(planar.EPSILON, old_e)
    assert_equal(planar.EPSILON2, old_e**2)
    assert not planar.Vec2(0, 0).almost_equals((0.01, 0))
Esempio n. 5
0
    def __mul__(self, other):
        """Apply the transform using matrix multiplication, creating a
        resulting object of the same type.  A transform may be applied to
        another transform, a vector, vector array, or shape.

        :param other: The object to transform.
        :type other: Affine, :class:`~planar.Vec2`, 
            :class:`~planar.Vec2Array`, :class:`~planar.Shape`
        :rtype: Same as ``other``
        """
        sa, sb, sc, sd, se, sf, _, _, _ = self
        if isinstance(other, Affine):
            oa, ob, oc, od, oe, of, _, _, _ = other
            return tuple.__new__(
                Affine,
                (sa * oa + sb * od, sa * ob + sb * oe, sa * oc + sb * of + sc,
                 sd * oa + se * od, sd * ob + se * oe, sd * oc + se * of + sf,
                 0.0, 0.0, 1.0))
        elif hasattr(other, 'from_points'):
            # Point/vector array
            Point = planar.Point
            return other.from_points(
                Point(px * sa + py * sd + sc, px * sb + py * se + sf)
                for px, py in other)
        else:
            try:
                vx, vy = other
            except Exception:
                return NotImplemented
            return planar.Vec2(vx * sa + vy * sd + sc, vx * sb + vy * se + sf)
Esempio n. 6
0
    def centroid(self):
        """The geometric center point of the polygon. This point only exists 
        for simple polygons. For non-simple polygons it is ``None``. Note
        in concave polygons, this point may lie outside of the polygon itself.

        If the centroid is unknown, it is calculated from the vertices and
        cached. If the polygon is known to be simple, this takes O(n) time. If
        not, then the simple polygon check is also performed, which has an
        expected complexity of O(n log n).
        """
        if self._centroid is _unknown:
            if self.is_simple:
                # Compute the centroid using by summing the centroids
                # of triangles made from each edge with vertex[0] weighted
                # (positively or negatively) by each triangle's area
                a = self[0]
                b = self[1]
                total_area = 0.0
                centroid = planar.Vec2(0, 0)
                for i in range(2, len(self)):
                    c = self[i]
                    area = ((b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) *
                            (b[1] - a[1]))
                    centroid += (a + b + c) * area
                    total_area += area
                    b = c
                self._centroid = centroid / (3.0 * total_area)
            else:
                self._centroid = None
        return self._centroid
Esempio n. 7
0
 def point_right(self, point):
     """Return True if the specified point is in the space
     to the right of, but not behind the ray.
     """
     to_point = planar.Vec2(*point) - self._anchor
     return (self._direction.dot(to_point) > -planar.EPSILON
         and self._normal.dot(to_point) >= planar.EPSILON)
Esempio n. 8
0
    def regular(cls, vertex_count, radius, center=(0, 0), angle=0):
        """Create a regular polygon with the specified number of vertices
        radius distance from the center point. Regular polygons are
        always convex.

        :param vertex_count: The number of vertices in the polygon.
            Must be >= 3.
        :type vertex_count: int
        :param radius: distance from vertices to center point.
        :type radius: float
        :param center: The center point of the polygon. If omitted,
            the polygon will be centered on the origin.
        :type center: Vec2
        :param angle: The starting angle for the vertices, in degrees.
        :type angle: float
        """
        cx, cy = center
        angle_step = 360.0 / vertex_count
        verts = []
        for i in range(vertex_count):
            x, y = cos_sin_deg(angle)
            verts.append((x * radius + cx, y * radius + cy))
            angle += angle_step
        poly = cls(verts, is_convex=True)
        poly._centroid = planar.Vec2(*center)
        poly._max_r = radius
        poly._max_r2 = radius * radius
        poly._min_r = min_r = ((poly[0] + poly[1]) * 0.5 - center).length
        poly._min_r2 = min_r * min_r
        poly._dupe_verts = False
        return poly
Esempio n. 9
0
 def point_right(self, point):
     """Return True if the specified point is in the space
     to the right of, but not behind the line segment.
     """
     to_point = planar.Vec2(*point) - self._anchor
     along = self._direction.dot(to_point)
     return (self.length + planar.EPSILON > along > -planar.EPSILON
         and self._normal.dot(to_point) >= planar.EPSILON)
Esempio n. 10
0
 def _init_min_max(self, points):
     points = iter(points)
     try:
         min_x, min_y = max_x, max_y = next(points)
     except StopIteration:
         raise ValueError("BoundingBox() requires at least one point")
     for x, y in points:
         if x < min_x:
             min_x = x * 1.0
         elif x > max_x:
             max_x = x * 1.0
         if y < min_y:
             min_y = y * 1.0
         elif y > max_y:
             max_y = y * 1.0
     self._min = planar.Vec2(min_x, min_y)
     self._max = planar.Vec2(max_x, max_y)
Esempio n. 11
0
 def vector(self, value):
     vector = planar.Vec2(*value)
     length = vector.length
     if length:
         self.direction = vector
     else:
         self.direction = (1, 0)
     self.length = vector.length
Esempio n. 12
0
 def point_behind(self, point):
     """Return True if the specified point is behind the anchor point with
     respect to the direction of the ray.  In other words, the angle
     between the ray direction and the vector pointing from the ray's
     anchor to the given point is greater than 90 degrees.
     """
     to_point = planar.Vec2(*point) - self._anchor
     return self.direction.dot(to_point) <= -planar.EPSILON
Esempio n. 13
0
 def distance_to(self, point):
     """Return the distance between the given point and the ray."""
     to_point = planar.Vec2(*point) - self._anchor
     if self.direction.dot(to_point) >= 0.0:
         # Point "beside" ray
         return abs(to_point.dot(self._normal))
     else:
         # Point "behind" ray
         return to_point.length
Esempio n. 14
0
    def reflect(self, point):
        """Reflect a point across the line.

        :param point: The point to reflect.
        :type point: Vec2
        """
        point = planar.Vec2(*point)
        offset_distance = point.dot(self._normal) - self.offset
        return point - 2.0 * self._normal * offset_distance
Esempio n. 15
0
 def from_points(cls, points):
     """Create a line from two or more collinear points.  The direction of
     the line is derived from the first two distinct points, the order of
     the remaining points is unimportant.
     
     :param points: Iterable of at least 2 distinct points.
     """
     points = iter(points)
     try:
         start = end = planar.Vec2(*points.next())
         while end == start:
             end = planar.Vec2(*points.next())
     except StopIteration:
         raise ValueError("Expected iterable of 2 or more distinct points")
     line = _LinearGeometry.__new__(cls)
     line.direction = end - start
     line.offset = start.dot(line.normal)
     for p in points:
         if not line.contains_point(p):
             raise ValueError("All points provided must be collinear")
     return line
Esempio n. 16
0
    def distance_to(self, point):
        """Return the signed distance from the line to the specified point.
        The sign indicates which half-plane contains the point. If the
        distance is negative, the point is in the "left" half plane with
        respect to the line, if it is positive, the point is in the "right"
        half plane.

        :param point: The point to measure the distance to.
        :type point: Vec2
        """
        point = planar.Vec2(*point)
        return point.dot(self._normal) - self.offset
Esempio n. 17
0
def wallsPrediction(data):
    speed_vector = planar.Vec2(estimation.ball_x_pred - data.ball_x,
                               estimation.ball_y_pred - data.ball_y)
    speed_vector = planar.Vec2.normalized(speed_vector)
    ball_position = planar.Vec2(data.ball_x, data.ball_y)

    if (speed_vector.x != 0) and (speed_vector.y != 0):
        t_x_pos = (0.65 - ball_position.x) / speed_vector.x
        t_y_pos = (0.75 - ball_position.y) / speed_vector.y
        t_x_neg = (-0.65 - ball_position.x) / speed_vector.x
        t_y_neg = (-0.75 - ball_position.y) / speed_vector.y

        time_list = [(t_x_pos), (t_y_pos), (t_x_neg), (t_y_neg)]
        time = min([i for i in time_list if i > 0])

        walls_y = speed_vector.y * time + ball_position.y
        walls_x = speed_vector.x * time + ball_position.x

    elif speed_vector.x != 0:
        t_x_pos = (0.65 - ball_position.x) / speed_vector.x
        t_x_neg = (-0.65 - ball_position.x) / speed_vector.x

        time_list = [(t_x_pos), (t_x_neg)]
        time = min([i for i in time_list if i > 0])
        walls_y = ball_position.y
        walls_x = ball_position.x + speed_vector.y * time

    elif speed_vector.y != 0:
        t_y_pos = (0.75 - ball_position.x) / speed_vector.x
        t_y_neg = (-0.75 - ball_position.x) / speed_vector.x

        time_list = [(t_y_pos), (t_y_neg)]
        time = min([i for i in time_list if i > 0])
        walls_x = ball_position.x
        walls_y = ball_position.y + speed_vector.y * time
    else:
        walls_x = 0
        walls_y = 0

    return walls_x, walls_y
Esempio n. 18
0
 def from_points(cls, points):
     """Create a ray from two or more collinear points.  The direction of
     the ray is derived from the first two distinct points, with the first
     point assumed to be the anchor. The order of the remaining points is
     unimportant, however they must all be on the ray.
     
     :param points: Iterable of at least 2 distinct points.
     """
     points = iter(points)
     try:
         start = end = planar.Vec2(*points.next())
         while end == start:
             end = planar.Vec2(*points.next())
     except StopIteration:
         raise ValueError("Expected iterable of 2 or more distinct points")
     ray = _LinearGeometry.__new__(cls)
     ray.direction = end - start
     ray.anchor = start
     for p in points:
         if not ray.contains_point(p):
             raise ValueError("All points provided must be collinear")
     return ray
Esempio n. 19
0
 def distance_to(self, point):
     """Return the distance between the given point and the line segment."""
     point = planar.Vec2(*point)
     to_point = point - self._anchor
     along = self.direction.dot(to_point)
     if along < 0.0:
         # Point "behind"
         return to_point.length
     if along > self.length:
         # Point "ahead"
         return (point - self.end).length
     else:
         # Point "beside"
         return abs(to_point.dot(self._normal))
Esempio n. 20
0
    def star(cls, peak_count, radius1, radius2, center=(0, 0), angle=0):
        """Create a radial pointed star polygon with the specified number
        of peaks.

        :param peak_count: The number of peaks. The resulting polygon will
            have twice this number of vertices. Must be >= 2.
        :type peak_count: int
        :param radius1: The peak or valley vertex radius. A vertex
            is aligned on ``angle`` with this radius.
        :type radius1: float
        :param radius2: The alternating vertex radius.
        :type radius2: float
        :param center: The center point of the polygon. If omitted,
            the polygon will be centered on the origin.
        :type center: Vec2
        :param angle: The starting angle for the vertices, in degrees.
        :type angle: float
        """
        if peak_count < 2:
            raise ValueError("star polygon must have a minimum of 2 peaks")
        cx, cy = center
        angle_step = 180.0 / peak_count
        verts = []
        for i in range(peak_count):
            x, y = cos_sin_deg(angle)
            verts.append((x * radius1 + cx, y * radius1 + cy))
            angle += angle_step
            x, y = cos_sin_deg(angle)
            verts.append((x * radius2 + cx, y * radius2 + cy))
            angle += angle_step
        is_simple = (radius1 > 0.0) == (radius2 > 0.0)
        poly = cls(verts,
                   is_convex=(radius1 == radius2),
                   is_simple=is_simple or None)
        if is_simple:
            poly._centroid = planar.Vec2(*center)
        poly._max_r = max_r = max(abs(radius1), abs(radius2))
        poly._max_r2 = max_r * max_r
        if (radius1 >= 0.0) == (radius2 >= 0.0):
            if not poly.is_convex:
                poly._min_r = min_r = min(abs(radius1), abs(radius2))
                poly._min_r2 = min_r * min_r
            else:
                poly._min_r = min_r = ((poly[0] + poly[1]) * 0.5 -
                                       center).length
                poly._min_r2 = min_r * min_r
        if radius1 > 0.0 and radius2 > 0.0:
            poly._dupe_verts = False
        return poly
Esempio n. 21
0
    def project(self, point):
        """Compute the projection of a point onto the ray. This
        is the closest point on the ray to the specified point.

        :param point: The point to project.
        :type point: Vec2
        """
        to_point = planar.Vec2(*point) - self._anchor
        parallel = self.direction.project(to_point)
        if parallel.dot(self.direction) > -planar.EPSILON:
            # Point "beside" ray
            return parallel + self._anchor
        else:
            # Point "behind" ray
            return self._anchor
Esempio n. 22
0
 def get_laser_pts(self, scan):
     pts = list()
     for i, r in enumerate(scan.ranges):
         if r < scan.range_max and r > scan.range_min:
             angle = scan.angle_min + i * scan.angle_increment
             x = r * math.cos(angle)
             y = r * math.sin(angle)
             pt = self.make_point_stamped(x, y, scan.header.frame_id)
             try:
                 pt_tf = self.tf_buffer.transform(pt, self.map_frame)
             except tf2_ros.LookupException:
                 rospy.logerr("TF tree is not ready yet")
                 return None
             cur_pt = planar.Vec2(pt_tf.point.x, pt_tf.point.y)
             pts.append(cur_pt)
     return pts
Esempio n. 23
0
    def inflate(self, amount):
        """Return a new box resized from this one. The new
        box has its size changed by the specified amount,
        but remains centered on the same point.

        :param amount: The quantity to add to the width and
            height of the box. A scalar value changes
            both the width and height equally. A vector
            will change the width and height independently.
            Negative values reduce the size accordingly.
        :type amount: float or :class:`~planar.Vec2`
        """
        try:
            dx, dy = amount
        except (TypeError, ValueError):
            dx = dy = amount * 1.0
        dv = planar.Vec2(dx, dy) / 2.0
        return self.from_points((self._min - dv, self._max + dv))
Esempio n. 24
0
    def project(self, point):
        """Compute the projection of a point onto the line segment. This
        is the closest point on the segment to the specified point.

        :param point: The point to project.
        :type point: Vec2
        """
        to_point = planar.Vec2(*point) - self._anchor
        parallel = self.direction.project(to_point)
        along = parallel.dot(self.direction)
        if along <= -planar.EPSILON:
            # Point "behind"
            return self._anchor
        elif along >= self.length + planar.EPSILON:
            # Point "ahead"
            return self.end
        else:
            # Point "beside"
            return parallel + self._anchor
Esempio n. 25
0
def ellipse_1(mean, covar, confidence):
    '''Return an ellipse of the given confidence for the given Gaussian.'''
    # Draw a circle of radius 1 around the origin.
    cir = My_Polygon.regular(POLYGON_EDGES, radius=1)
    # Compute ellipse parameters.
    (eivals, eivecs) = np.linalg.eigh(covar)
    crit = chisq_crit2(confidence)
    scale0 = math.sqrt(crit * eivals[0])
    scale1 = math.sqrt(crit * eivals[1])
    angle = planar.Vec2(eivecs[0][0], eivecs[0][1]).angle
    # Transform the circle into an ellipse. NOTE: Originally, I set this up to
    # build a full transformation matrix and then multiply the polygon by that
    # composite matrix. However, the order of operations was really wierd --
    # e.g., t * s * r works, even though the transformation order is s, r, t. I
    # think this is due to some mismatch between the associativity of the
    # multiplication operator and calls to the __mult__() special method.
    # Anyway, this way is a lot easier to follow.
    return (planar.Affine.translation(mean) *
            (planar.Affine.rotation(angle) * (planar.Affine.scale(
                (scale0, scale1)) * cir))).as_geos
Esempio n. 26
0
def load_graph(nyc_dir, fn_edge_times, hour):
    # print "Making it a graph..."
    sts = np.loadtxt(nyc_dir + "points.csv", delimiter=",")
    edges = np.loadtxt(nyc_dir + "edges.csv", delimiter=",")
    times = np.loadtxt(nyc_dir + fn_edge_times, delimiter=",")
    G = nx.DiGraph()
    for i in xrange(sts.shape[0]):
        G.add_node(i, lat=sts[i][1], lon=sts[i][2])
    for i in xrange(edges.shape[0]):
        t = times[i][hour + 1]
        G.add_edge(edges[i][1] - 1, edges[i][2] - 1, weight=t)
    poly = planar.Polygon.from_points(common.nyc_poly)
    for i in G.nodes():
        data = G.node[i]
        contains = poly.contains_point(planar.Vec2(data["lon"], data["lat"]))
        if not contains:
            G.remove_node(i)
    # print "Finding the largest connected component subgraph"
    G_final = max(nx.strongly_connected_component_subgraphs(G), key=len)
    return G_final, np.fliplr(sts[:, 1:])
Esempio n. 27
0
def simplify_by_degree(G, max_distance):
    G_simple = G
    poly = planar.Polygon.from_points(common.nyc_poly)
    for n in G_simple.nodes():
        vec = planar.Vec2(G.node[n]["data"].lon, G.node[n]["data"].lat)
        if not poly.contains_point(vec):
            G_simple.remove_node(n)
    for n in G_simple.nodes():
        dead_node = G_simple.out_degree(n) == 0 or G_simple.in_degree(n) == 0
        if G_simple.in_degree(n) == 1 and G_simple.out_degree(n) == 1:
            weight_in_n = G[G.predecessors(n)[0]][n]['weight']
            weight_out_n = G[n][G.successors(n)[0]]['weight']
            weight_new = weight_in_n + weight_out_n
            G_simple.add_edge(G.predecessors(n)[0],
                              G.successors(n)[0],
                              weight=weight_new)
            G_simple.remove_node(n)
        elif G_simple.has_node(n) and dead_node:
            G_simple.remove_node(n)
    G_final = max(nx.strongly_connected_component_subgraphs(G_simple), key=len)
    return G_final
Esempio n. 28
0
 def test_center(self):
     import planar
     box = self.BoundingBox([(-3, -1), (1, 4)])
     assert isinstance(box.center, planar.Vec2)
     assert_equal(box.center, planar.Vec2(-1, 1.5))
     assert_equal(self.BoundingBox([(8, 12)]).center, planar.Vec2(8, 12))
Esempio n. 29
0
 def column_vectors(self):
     """The values of the transform as three 2D column vectors"""
     a, b, c, d, e, f, _, _, _ = self
     return planar.Vec2(a, d), planar.Vec2(b, e), planar.Vec2(c, f)
Esempio n. 30
0
def _ahull_partition_points(hull, points, p0, p1):
    """Partition the points 'above' p0->p1 to compute the sub-hull"""

    # Find point furthest from line p0->p1 as partition point
    furthest = -1.0
    p0_x, p0_y = p0
    pline_dx = p1[0] - p0[0]
    pline_dy = p1[1] - p0[1]
    for p in points:
        dist = pline_dx * (p[1] - p0_y) - (p[0] - p0_x) * pline_dy
        if dist > furthest:
            furthest = dist
            partition_point = p
    partition_point = planar.Vec2(*partition_point)

    # Compute the triangle partition_point->p0->p1
    # in barycentric coordinates
    # All points inside this triangle are not in the hull
    # divide the remaining points into left and right sets
    left_points = []
    right_points = []
    add_left = left_points.append
    add_right = right_points.append
    v0 = p0 - partition_point
    v1 = p1 - partition_point
    dot00 = v0.length2
    dot01 = v0.dot(v1)
    dot11 = v1.length2
    denom = (dot00 * dot11 - dot01 * dot01)
    # If denom is zero, the triangle has no area and
    # all points lie on the partition line
    # and thus can be culled
    if denom:
        inv_denom = 1.0 / denom
        for p in points:
            v2 = p - partition_point
            dot02 = v0.dot(v2)
            dot12 = v1.dot(v2)
            u = (dot11 * dot02 - dot01 * dot12) * inv_denom
            v = (dot00 * dot12 - dot01 * dot02) * inv_denom
            # Since the partition point is the furthest from p0->p1
            # u and v cannot both be negative
            # Note the partition point is discarded here
            if v < 0.0:
                add_left(p)
            elif u < 0.0:
                add_right(p)

    left_count = len(left_points)
    right_count = len(right_points)
    # Heuristic to determine if we should continue to partition
    # recursively, or complete the sub-hull via a sorted scan.
    # The more points culled by this partition, the greater
    # the chance we will partition further. If paritioning
    # culled few points, it is likely that a sorted scan
    # will be the more efficient algorithm. Note the scaling
    # factor here is not particularly sensitive.
    max_partition = (len(points) - left_count - right_count) * 4

    if left_count <= 1:
        # Trivial partition
        hull.append(p0)
        hull.extend(left_points)
    elif left_count <= max_partition:
        _ahull_partition_points(hull, left_points, p0, partition_point)
    else:
        _ahull_sort_points(hull, left_points, p0, partition_point)

    if right_count <= 1:
        # Trivial partition
        hull.append(partition_point)
        hull.extend(right_points)
    elif right_count <= max_partition:
        _ahull_partition_points(hull, right_points, partition_point, p1)
    else:
        _ahull_sort_points(hull, right_points, partition_point, p1)