예제 #1
0
def test_translation():
    pt = PathCanvas()
    pt.move_to(100, 100)
    pt.translate(1000, 2000)
    pt.line_to(10, 20)
    assert pt.paths == [
        Path([Point(100.0, 100.0), Point(1010.0, 2020.0)]),
    ]
예제 #2
0
def test_rel_line_to():
    pt = PathTiler()
    pt.translate(1000, 2000)
    pt.move_to(0, 0)
    pt.rel_line_to(100, 200)
    assert pt.paths == [
        [Point(1000.0, 2000.0), Point(1100.0, 2200.0)],
    ]
예제 #3
0
def test_reflect_xy():
    pt = PathCanvas()
    pt.move_to(100, 100)
    pt.reflect_xy(1000, 2000)
    pt.line_to(200, 200)
    assert pt.paths == [
        Path([Point(100.0, 100.0), Point(1800, 3800)]),
    ]
예제 #4
0
def test_translation():
    pt = PathTiler()
    pt.move_to(100, 100)
    pt.translate(1000, 2000)
    pt.line_to(10, 20)
    assert pt.paths == [
        [Point(100.0, 100.0), Point(1010.0, 2020.0)],
    ]
예제 #5
0
def test_reflect_xy():
    pt = PathTiler()
    pt.move_to(100, 100)
    pt.reflect_xy(1000, 2000)
    pt.line_to(200, 200)
    assert pt.paths == [
        [Point(100.0, 100.0), Point(1800, 3800)],
    ]
예제 #6
0
def test_rel_line_to():
    pt = PathCanvas()
    pt.translate(1000, 2000)
    pt.move_to(0, 0)
    pt.rel_line_to(100, 200)
    assert pt.paths == [
        Path([Point(1000.0, 2000.0),
              Point(1100.0, 2200.0)]),
    ]
예제 #7
0
def test_reflect_line():
    pt = PathCanvas()
    pt.move_to(100, 100)
    pt.reflect_line(Point(50, -50), Point(150, 50))
    pt.line_to(100, 50)
    pt.line_to(100, 100)
    assert pt.paths == [
        Path([Point(100.0, 100.0),
              Point(150, 0), Point(200, 0)]),
    ]
예제 #8
0
def test_reflect_line():
    pt = PathTiler()
    pt.move_to(100, 100)
    pt.reflect_line(Point(50, -50), Point(150, 50))
    pt.line_to(100, 50)
    pt.line_to(100, 100)
    assert pt.paths == [
        [Point(100.0, 100.0),
         Point(150, 0), Point(200, 0)],
    ]
예제 #9
0
def test_segment_sort_along(p1, p2, tvals):
    # Get rid of pathological cases.
    assume(p1.distance(p2) > 0.001)

    tvals = [t / 100 for t in tvals]
    fuzz = [1e-10, -1e-10]
    points = [along_the_way(p1, p2, t) for t in tvals]
    points = [
        Point(x + f, y + f) for (x, y), f in zip(points, itertools.cycle(fuzz))
    ]

    # Calculate the smallest distance between any pair of points.  If we get
    # the wrong answer from sort_along, then the total distance will be off by
    # at least twice this.
    min_gap = min(q1.distance(q2) for q1, q2 in all_pairs(points + [p1, p2]))

    seg = Segment(p1, p2)
    spoints = seg.sort_along(points)

    assert len(spoints) == len(points)
    assert all(pt in points for pt in spoints)

    original = Point(*p1).distance(Point(*p2))
    total = (Point(*p1).distance(Point(*spoints[0])) + sum(
        Point(*p).distance(Point(*q)) for p, q in adjacent_pairs(spoints)) +
             Point(*spoints[-1]).distance(Point(*p2)))
    # The total distance will be wrong by at least 2*min_gap if it is wrong.
    assert total - original < 2 * min_gap
예제 #10
0
def test_square_to_paralleogram(pt1, pt2):
    pt12 = tuple(v1 + v2 for v1, v2 in zip(pt1, pt2))
    xform = square_to_parallelogram(pt1, pt2)
    ins = [(0, 0), (1, 0), (0, 1), (1, 1)]
    outs = [(0, 0), pt1, pt2, pt12]
    for i, o in zip(ins, outs):
        pti = Point(*i)
        pto = Point(*o)
        actual = Point(*(xform * pti))
        print(pti, pto, actual)
        assert actual.is_close(pto)
예제 #11
0
def test_two_segments():
    pt = PathTiler()
    pt.move_to(100, 100)
    pt.line_to(150, 200)
    pt.move_to(17, 17)
    pt.rel_line_to(100, 200)
    pt.close_path()
    assert pt.paths == [
        [Point(100.0, 100.0), Point(150.0, 200.0)],
        [Point(17.0, 17.0),
         Point(117.0, 217.0),
         Point(17.0, 17.0)],
    ]
예제 #12
0
    def draw_tile(self, dwg):
        self.three_points()
        border_side = Line(self.top, self.bottom)
        border_shoulder = Line(self.top, self.belly)
        border_foot = Line(self.belly, self.bottom)

        offset = self.tilew / 8
        side_line = border_side.offset(-offset)
        shoulder_line = border_shoulder.offset(offset)
        foot_line = border_foot.offset(offset)

        with dwg.saved():
            dwg.translate(*self.belly)
            dwg.rotate(30)
            snip_top = dwg.in_device(0, offset * SQRT2)
            snip_bottom = dwg.in_device(-offset * SQRT2, 0)

        snip_top = dwg.in_user(*snip_top)
        snip_bottom = dwg.in_user(*snip_bottom)
        snip_line = Line(snip_top, snip_bottom)

        far_left = Point(-self.tilew * SQRT3 / 2, -self.tilew / 2)
        shoulder_limit = Line(far_left, self.top)
        shoulder_limit = shoulder_limit.offset(offset)

        far_right = Point(self.tilew * SQRT3 / 2, 0)
        side_limit = Line(self.top, far_right)
        side_limit = side_limit.offset(offset)

        side_top = side_line.intersect(side_limit)
        side_bottom = side_line.intersect(border_foot)
        shoulder_top = shoulder_line.intersect(shoulder_limit)
        shoulder_bottom = shoulder_line.intersect(snip_line)
        foot_top = foot_line.intersect(snip_line)
        foot_bottom = foot_line.intersect(border_side)

        dwg.move_to(*side_top)
        dwg.line_to(*side_bottom)

        dwg.move_to(*shoulder_top)
        dwg.line_to(*shoulder_bottom)
        dwg.line_to(*snip_bottom)

        dwg.move_to(*snip_top)
        dwg.line_to(*foot_top)
        dwg.line_to(*foot_bottom)
예제 #13
0
    def draw_tile(self, dwg):
        west = Point(0, self.tilew)
        south = Point(self.tilew, 0)
        sqw = west.distance(south)
        southwest = Point(self.tilew-sqw/2, self.tilew-sqw/2)
        diagonal = Line(west, south)
        vert = Line(Point(self.tilew-sqw/2, 0), southwest)
        horz = Line(southwest, Point(0, self.tilew-sqw/2))

        wsw = diagonal.intersect(vert)
        ssw = diagonal.intersect(horz)

        dwg.move_to(*west)
        dwg.line_to(*wsw)
        dwg.line_to(*southwest)
        dwg.line_to(*ssw)
        dwg.line_to(*south)
예제 #14
0
def path_pieces(path, segs_to_points):
    """Produce a new series of paths, split at intersection points.

    Yields a series of pieces (paths).  The pieces trace the same line as the
    original path.  The endpoints of the pieces are all intersection points
    in `segs_to_points`, or the endpoints of the original path, if it isn't
    circular.  The pieces are in order along `path`, so consecutive pieces
    end and begin at the same point. If `path` is closed, then the first piece
    returned will begin at the first cut, not at the path's first point.

    """
    # If path is circular, then the first piece we collect has to be added to
    # the last piece, so save it for later.
    collecting_head = path.closed
    head = None

    piece = []
    for pt in path:
        if not piece:
            piece.append(pt)
        else:
            seg = Segment(piece[-1], pt)
            cuts = segs_to_points.get(seg)
            if cuts is not None:
                cuts = seg.sort_along(cuts)
                for cut in cuts:
                    ptcut = Point(*cut)
                    piece.append(ptcut)
                    if collecting_head:
                        head = piece
                        collecting_head = False
                    else:
                        yield Path(piece)
                    piece = [ptcut]
            piece.append(pt)

    piece = Path(piece)
    if head:
        piece = piece.join(Path(head))
    yield piece
예제 #15
0
def test_save_restore():
    pt = PathTiler()
    pt.move_to(100, 100)
    pt.translate(1000, 2000)
    pt.line_to(10, 20)
    pt.save()
    pt.translate(1, 2)
    pt.move_to(1, 2)
    pt.line_to(2, 4)
    pt.restore()
    pt.move_to(1, 2)
    pt.line_to(2, 4)
    assert pt.paths == [
        [Point(100.0, 100.0), Point(1010.0, 2020.0)],
        [Point(1002.0, 2004.0), Point(1003.0, 2006.0)],
        [Point(1001.0, 2002.0), Point(1002.0, 2004.0)],
    ]
예제 #16
0
def debug_world(paths, width, height):
    dwg = Drawing(paths=paths, name="debug_world", bg=None)

    # Gray rectangle: the desired visible canvas.
    with dwg.style(rgb=(.95, .95, .95)):
        dwg.rectangle(0, 0, width, height)
        dwg.fill()

    # Reference grid.
    llx, lly = dwg.llx, dwg.lly
    urx = llx + dwg.width
    ury = lly + dwg.height
    with dwg.style(rgb=(.5, 1, 1), width=1, dash=[5, 5], dash_offset=7.5):
        for xmin in tick_range(llx, urx, 20):
            dwg.move_to(xmin, lly)
            dwg.line_to(xmin, ury)
            dwg.stroke()
        for ymin in tick_range(lly, ury, 20):
            dwg.move_to(llx, ymin)
            dwg.line_to(urx, ymin)
            dwg.stroke()

    with dwg.style(rgb=(.5, 1, 1), width=1):
        dwg.circle_points([Point(0, 0)], radius=10)
        for xmaj in tick_range(llx, urx, 100):
            dwg.move_to(xmaj, lly)
            dwg.line_to(xmaj, ury)
            dwg.stroke()
        for ymaj in tick_range(lly, ury, 100):
            dwg.move_to(llx, ymaj)
            dwg.line_to(urx, ymaj)
            dwg.stroke()

    # The paths themselves.
    dwg.draw_paths(paths, width=1, rgb=(1, 0, 0))

    dwg.finish()
    print("Wrote debug_world.png")
예제 #17
0
 def three_points(self):
     self.top = Point(0, 0)
     self.bottom = Point(0, -self.tilew)
     self.belly = Point(self.tilew * SQRT3 / 4, -self.tilew * .75)
예제 #18
0
def debug_world(dwg0, paths_styles):
    """Draw a picture of the entire world.

    `dwg0` is the Drawing we're really making.

    `paths_styles` is a list of tuples: (paths, style) for drawing.
    """

    # Get the perimeter of the real drawing.
    dwg0_path = dwg0.perimeter()

    # Get the bounds of everything we're going to draw.
    bounds = EmptyBounds()
    for paths, styles in paths_styles:
        bounds |= paths_bounds(paths)
    bounds |= dwg0_path.bounds()
    bounds = bounds.expand(percent=2)

    dwg = Drawing(bounds=bounds, name="debug_world", bg=(.95, .95, .95))

    # White rectangle: the desired visible canvas.
    with dwg.style(rgb=(1, 1, 1)):
        dwg0_path.draw(dwg)
        dwg.fill()

    # Reference grid.
    llx, lly, urx, ury = dwg.bounds
    with dwg.style(rgb=(.5, 1, 1), width=1, dash=[5, 5], dash_offset=7.5):
        for xmin in tick_range(llx, urx, 20):
            dwg.move_to(xmin, lly)
            dwg.line_to(xmin, ury)
            dwg.stroke()
        for ymin in tick_range(lly, ury, 20):
            dwg.move_to(llx, ymin)
            dwg.line_to(urx, ymin)
            dwg.stroke()

    with dwg.style(rgb=(.5, 1, 1), width=1):
        for xmaj in tick_range(llx, urx, 100):
            dwg.move_to(xmaj, lly)
            dwg.line_to(xmaj, ury)
            dwg.stroke()
        for ymaj in tick_range(lly, ury, 100):
            dwg.move_to(llx, ymaj)
            dwg.line_to(urx, ymaj)
            dwg.stroke()

    # The origin.
    with dwg.style(rgb=(0, .75, .75), width=1):
        dwg.circle_points([Point(0, 0)], radius=10)
        dwg.move_to(-10, 0)
        dwg.line_to(10, 0)
        dwg.move_to(0, -10)
        dwg.line_to(0, 10)
        dwg.stroke()

    # The paths themselves.
    for paths, styles in paths_styles:
        dwg.draw_paths(paths, **styles)

    dwg.finish()
    print("Wrote debug_world.png")
예제 #19
0
from zellij.path import Path, combine_paths, equal_path, equal_paths, paths_length

from hypothesis import given
from hypothesis.strategies import lists, randoms, composite, one_of
import pytest

from .hypo_helpers import ipoints


def P(twodigits):
    """Helper for compact points: P(34) --> Point(3, 4)"""
    return Point(*divmod(twodigits, 10))


@pytest.mark.parametrize("compact, result", [
    (P(34), Point(3, 4)),
    (P(20), Point(2, 0)),
])
def test_p(compact, result):
    assert compact == result


@pytest.mark.parametrize(
    "p1, p2, result",
    [
        # Identical paths.
        ([P(11), P(22)], [P(11), P(22)], True),
        # Completely different.
        ([P(11), P(22)], [P(11), P(33)], False),
        # Same, but reversed.
        ([P(11), P(22)], [P(22), P(11)], True),
예제 #20
0
def test_line_angle(p1, p2, angle):
    l = Line(Point(*p1), Point(*p2))
    assert math.isclose(l.angle(), angle)
예제 #21
0
def test_intersect(p1, p2, p3, p4, pi):
    l1 = Line(Point(*p1), Point(*p2))
    l2 = Line(Point(*p3), Point(*p4))
    assert l1.intersect(l2).is_close(Point(*pi))
예제 #22
0
def P(twodigits):
    """Helper for compact points: P(34) --> Point(3, 4)"""
    return Point(*divmod(twodigits, 10))
예제 #23
0
        [Point(1001.0, 2002.0), Point(1002.0, 2004.0)],
    ]


def test_rel_line_to():
    pt = PathTiler()
    pt.translate(1000, 2000)
    pt.move_to(0, 0)
    pt.rel_line_to(100, 200)
    assert pt.paths == [
        [Point(1000.0, 2000.0), Point(1100.0, 2200.0)],
    ]


@pytest.mark.parametrize("p1, p2, result", [
    ([Point(0, 0), Point(1, 1)], [
        Point(1, 1), Point(2, 0), Point(3, 0)
    ], [Point(0, 0), Point(1, 1),
        Point(2, 0), Point(3, 0)]),
    ([Point(0, 0), Point(1, 1)], [Point(2, 2), Point(3, 3)], None),
    ([Point(0, 0), Point(1, 1)], [
        Point(1, 1), Point(2, 2), Point(3, 0)
    ], [Point(0, 0), Point(2, 2), Point(3, 0)]),
])
def test_join_paths(p1, p2, result):
    def same(p1, p2):
        if p1 is None and p2 is None:
            return True
        elif p1 is None or p2 is None:
            return False
        else:
예제 #24
0
def test_no_intersection(p1, p2, p3, p4, err):
    l1 = Line(Point(*p1), Point(*p2))
    l2 = Line(Point(*p3), Point(*p4))
    with pytest.raises(err):
        l1.intersect(l2)
예제 #25
0
def test_offset():
    l1 = Line(Point(10, 10), Point(13, 14))
    l2 = l1.offset(10)
    assert l2.p1 == Point(18, 4)
    assert l2.p2 == Point(21, 8)
예제 #26
0
def test_point_equality(p1, p2, result):
    assert (Point(*p1) == Point(*p2)) == result
예제 #27
0
def test_point_is_close(p1, p2, result):
    assert Point(*p1).is_close(Point(*p2)) == result
예제 #28
0
def test_point_distance(p1, p2, result):
    assert math.isclose(Point(*p1).distance(Point(*p2)), result)
예제 #29
0
def test_points_collinear(p1, p2, p3, result):
    assert collinear(Point(*p1), Point(*p2), Point(*p3)) == result