Exemple #1
0
def test_polysplit_hashing():
    """Test that tolerance sets hashing correctly from parent functions."""

    poly = Polygon2D.from_array([[1.0123456789, 3.0123456789],
                                 [3.0123456789, 5.0123456789],
                                 [1.0123456789, 5.0123456789]])

    # 7 digit tolerance w/ rounding
    tol = 1e-7
    g = polysplit._skeleton_as_directed_graph(poly, holes=None, tol=tol)
    k = g.ordered_nodes[0].key
    assert k == '(1.0123457, 3.0123457)', k

    # 10 digit tolerance w/o rounding
    tol = 1e-10
    g = polysplit._skeleton_as_directed_graph(poly, holes=None, tol=tol)
    k = g.ordered_nodes[0].key
    assert k == '(1.0123456789, 3.0123456789)', k

    # Test with number x 100
    poly = Polygon2D.from_array([[100.0123456789, 300.0123456789],
                                 [300.0123456789, 500.0123456789],
                                 [100.0123456789, 500.0123456789]])

    # 10 digit tolerance
    tol = 1e-7
    g = polysplit._skeleton_as_directed_graph(poly, holes=None, tol=tol)
    k = g.ordered_nodes[0].key
    assert k == '(100.0123457, 300.0123457)', k

    # 10 digit tolerance w/o rounding
    tol = 1e-10
    g = polysplit._skeleton_as_directed_graph(poly, holes=None, tol=tol)
    k = g.ordered_nodes[0].key
    assert k == '(100.0123456789, 300.0123456789)', k
Exemple #2
0
def test_perimeter_core_subpolygons_hole_error():
    """Test throwing an exception if hole doesn't get computed in straight skeleton."""
    # Construct a simple rectangle
    poly = Polygon2D.from_array([[0, 0], [6, 0], [6, 8], [0, 8]])
    holes = [Polygon2D.from_array([[2, 2], [4, 2], [4, 6], [2, 6]])]

    # Run method
    with pytest.raises(RuntimeError):
        _ = polysplit.perimeter_core_subpolygons(poly, 1, holes)
Exemple #3
0
def test_polygon_offset_outward():
    """Test the offset method"""

    # Construct a simple rectangle
    poly = [[1, 1], [3, 1], [3, 5], [1, 5]]
    poly = Polygon2D.from_array(poly)

    # Make solution polygon (list of polygons)
    chk_off = Polygon2D.from_array([[0, 0], [4, 0], [4, 6], [0, 6]])

    # Run method
    offset = polyskel.offset(poly, -1, [])
Exemple #4
0
def test_polygon_offset_inward():
    """Test the offset method"""

    # Construct a simple rectangle
    poly = [[0, 0], [4, 0], [4, 6], [0, 6]]
    poly = Polygon2D.from_array(poly)

    # Make solution polygon (list of polygons)
    chk_off = Polygon2D.from_array([[1, 1], [3, 1], [3, 5], [1, 5]])

    # Run method
    offset = polyskel.offset(poly, 1, [])

    assert offset[0].is_equivalent(chk_off, 1e-2)
def test_edge_direction():
    """ Tests the bidirection method """

    # Make the polygon
    poly = Polygon2D.from_array([[0, 0], [6, 0], [6, 6], [3, 4], [0, 6]])
    pt_array = poly.vertices

    # Make unidirect graph
    dg = PolygonDirectedGraph(1e-10)

    for i in range(len(pt_array) - 1):
        k = dg.add_node(pt_array[i], [pt_array[i + 1]])
        if i == 0:
            dg.outer_root_key = k
    dg.add_node(pt_array[-1], [pt_array[0]])

    root = dg.node(dg.outer_root_key)

    # Check
    nodes = dg.ordered_nodes
    for i in range(dg.num_nodes - 1):
        assert not dg.is_edge_bidirect(nodes[i], nodes[i + 1])

    # Check unidirectionality
    next_node = dg.next_unidirect_node(root)
    assert not dg.is_edge_bidirect(root, next_node)

    # Add bidirectional edge
    dg.add_node(Point2D(0, 0), [Point2D(1, 1)])
    bidir_key = dg.add_node(Point2D(1, 1), [Point2D(0, 0)])

    # Check bidirectionality
    assert dg.is_edge_bidirect(dg.node(bidir_key), root)
Exemple #6
0
    def frequency_lines(self):
        """Get the frequency lines for windrose as Polygon2D lists."""

        # Reset computed graphics to account for changes to cached viz properties
        self._compass = None
        self._container = None

        ytick_array = []
        ytick_num = self.frequency_intervals_compass
        zero_dist = self._zero_mesh_radius
        ytick_dist = self.frequency_spacing_hypot_distance
        # Add frequency polygon for calmrose, if exists
        if self.show_zeros and self.zero_count > 0:
            _ytick_array = [(vec1[0] * zero_dist, vec1[1] * zero_dist)
                            for (vec1, _) in self.bin_vectors]
            ytick_array.append(_ytick_array)

        # Add the regular frequency polygons
        for i in range(1, ytick_num + 1):
            _ytick_array = []
            for (vec1, _) in self.bin_vectors:
                _x = (vec1[0] * i * ytick_dist) + (vec1[0] * zero_dist)
                _y = (vec1[1] * i * ytick_dist) + (vec1[1] * zero_dist)
                _ytick_array.append((_x, _y))
            ytick_array.append(_ytick_array)

        return [
            self._transform(Polygon2D.from_array(vecs)) for vecs in ytick_array
        ]
def test_dg_skel_concave():
    """Test the dg with skeleton in concave geom"""

    pt_array = [[0, 0], [6, 0], [6, 6], [3, 4], [0, 6]]

    # Make the polygon
    polygon = Polygon2D.from_array(pt_array)

    # Adj matrix to test against
    chk_amtx = [
        [0, 1, 0, 0, 0, 1, 0, 0],  # 0
        [0, 0, 1, 0, 0, 0, 1, 0],  # 1
        [0, 0, 0, 1, 0, 0, 1, 0],  # 2
        [0, 0, 0, 0, 1, 0, 0, 1],  # 3
        [1, 0, 0, 0, 0, 1, 0, 0],  # 4
        [1, 0, 0, 0, 1, 0, 0, 1],  # 5
        [0, 1, 1, 0, 0, 0, 0, 1],  # 6
        [0, 0, 0, 1, 0, 1, 1, 0]
    ]  # 7
    #     0, 1, 2, 3, 4, 5, 6, 7

    chk_lbls = {
        0: '(0.0, 0.0)',
        1: '(6.0, 0.0)',
        2: '(6.0, 6.0)',
        3: '(3.0, 4.0)',
        4: '(0.0, 6.0)',
        5: '(2.09, 2.09)',
        6: '(3.91, 2.09)',
        7: '(3.0, 1.82)'
    }

    dg = polysplit._skeleton_as_directed_graph(polygon, [], 1e-2)

    amtx = dg.adj_matrix()
    lbls = dg.adj_matrix_labels()

    # Test the size
    assert len(amtx) == len(chk_amtx), _cmpstr(len(amtx), len(chk_amtx))
    assert len(amtx[0]) == len(chk_amtx[0]), _cmpstr(len(amtx[0]),
                                                     len(chk_amtx[0]))
    assert len(lbls) == len(chk_lbls)

    # Test the labels
    for key in range(len(lbls)):
        assert lbls[key] == chk_lbls[key], 'key: {} result in '.format(key) + \
            _cmpstr(lbls[key], chk_lbls[key])

    # Test if the adj matrix is correct
    # Flip dict for chk, this ensures if points are deleted or unordered in
    # adj mtx test will still pass
    chk_row_dict = {val: key for key, val in chk_lbls.items()}
    for i in range(len(amtx)):
        key = lbls[i]
        ci = chk_row_dict[key]
        for j in range(len(amtx[0])):
            assert amtx[i][j] == chk_amtx[ci][j], 'at index {},{}: '.format(i, j) + \
                _cmpstr(amtx[i][j], chk_amtx[i][j])
Exemple #8
0
def test_complex_perimeter_core_subpolygons():
    """Test splitting perimeter and core subpolygons from polygon."""

    poly = Polygon2D.from_array([[0.7, 0.2], [2, 0], [2, 2], [1, 1], [0, 2],
                                 [0, 0]])
    holes = [
        Polygon2D.from_array([[0.6, 0.6], [1.5, 0.6], [1, 0.8], [0.6, 1.2]]),
        Polygon2D.from_array([[1.1, 0.25], [1.5, 0.25], [1.3, 0.5]])
    ]

    p, c = polysplit.perimeter_core_subpolygons(poly,
                                                .1,
                                                holes=holes,
                                                tol=1e-10)

    # Some very simple tests
    assert len(p) == 13
    assert len(c) == 1
def test_dg_noskel():
    """Test the dg with no skeleton"""

    # Points
    pt_array = [[0, 0], [6, 0], [6, 6], [3, 9], [0, 6]]

    # Make the polygon
    polygon = Polygon2D.from_array(pt_array)

    # Make the check cases
    chk_pt_lst = [Point2D.from_array(p) for p in pt_array]

    # Inititalize a dg object
    d = PolygonDirectedGraph(1e-10)
    vertices = polygon.vertices

    # Add edges to dg
    for i in range(len(vertices) - 1):
        k = d.add_node(vertices[i], [vertices[i + 1]])
        if i == 0:
            d.outer_root_key = k
    d.add_node(vertices[-1], [vertices[0]])

    # Test number
    assert len(chk_pt_lst) == d.num_nodes, _cmpstr(len(chk_pt_lst),
                                                   d.num_nodes)

    # Test root
    assert d.node(d.outer_root_key)._order == 0

    # Test adjacencies are correct
    curr_node = d.node(d.outer_root_key)
    for chk_pt in chk_pt_lst:
        assert chk_pt.is_equivalent(curr_node.pt,
                                    TOL), _cmpstr(chk_pt, curr_node.pt)

        # Increment
        curr_node = curr_node.adj_lst[0]

    # Test the adj matrix
    amtx = d.adj_matrix()

    # Adj matrix to test against
    chk_amtx = [
        [0, 1, 0, 0, 0],  # 0
        [0, 0, 1, 0, 0],  # 1
        [0, 0, 0, 1, 0],  # 2
        [0, 0, 0, 0, 1],  # 3
        [1, 0, 0, 0, 0]
    ]  # 4
    #     0, 1, 2, 3, 4

    # Test if the adj matrix is correct
    for i in range(len(chk_amtx)):
        for j in range(len(chk_amtx[0])):
            assert amtx[i][j] == chk_amtx[i][j], _cmpstr(
                amtx[i][j], chk_amtx[i][j])
def test_exterior_cycle():
    """ Tests the exterior cycles method """

    # Make the polygon
    polygon = Polygon2D.from_array([[0, 0], [6, 0], [6, 4], [0, 4]])
    dg = polysplit._skeleton_as_directed_graph(polygon, [], 1e-10)

    root = dg.node(dg.outer_root_key)
    exterior = dg.exterior_cycle(root)

    for pt, node in zip(polygon.vertices, exterior):
        assert node.pt.is_equivalent(pt, 1e-10)
def test_min_ccw_cycle():
    """ Find a closed loop from a PolygonDirectedGraph """

    # Make the polygon
    poly = Polygon2D.from_array([[0, 0], [6, 0], [6, 6], [3, 4], [0, 6]])

    # Make the test cases
    chk_poly = [[0, 0], [6, 0], [3.91, 2.09], [3, 1.82], [2.09, 2.09]]
    chk_poly = Polygon2D.from_array(chk_poly)

    # Skeletonize
    dg = polysplit._skeleton_as_directed_graph(poly, [], 1e-10)

    ref_node = dg.node(dg.outer_root_key)

    next_node = dg.next_unidirect_node(ref_node)
    cycle = dg.min_ccw_cycle(ref_node, next_node)

    cycle_poly = Polygon2D.from_array([n.pt for n in cycle])

    assert cycle_poly.is_equivalent(chk_poly, 1e-2)
def test_polygon_is_equivalent():
    """ Test if polygons are equivalent based on point equivalence"""

    tol = 1e-10
    p1 = Polygon2D.from_array([[0, 0], [6, 0], [7, 3], [0, 4]])

    # Test no points are the same
    p2 = Polygon2D.from_array([[0, 1], [6, 1], [7, 4]])
    assert not p1.is_equivalent(p2, tol)

    # Test when length is not same
    p2 = Polygon2D.from_array([[0, 0], [6, 0], [7, 3]])
    assert not p1.is_equivalent(p2, tol)

    # Test equal condition same order
    p2 = Polygon2D.from_array([[0, 0], [6, 0], [7, 3], [0, 4]])
    assert p1.is_equivalent(p2, tol)

    # Test equal condition different order 1
    p2 = Polygon2D.from_array([[7, 3], [0, 4], [0, 0], [6, 0]])
    assert p1.is_equivalent(p2, tol)

    # Test equal condition different order 2
    p2 = Polygon2D.from_array([[0, 4], [0, 0], [6, 0], [7, 3]])
    assert p1.is_equivalent(p2, tol)
Exemple #13
0
def test_concave_angles():
    """ Test simple concave angles"""
    poly = Polygon2D.from_array(
        [[0, 0], [4, 0], [4, 6], [2, 4], [0, 6]])

    conv_theta = math.acos(2 / math.sqrt(8)) / math.pi * 180
    conc_theta = 180. + (2 * conv_theta)
    chk_angles = [90, conv_theta, conc_theta, conv_theta, 90]

    angles = polyskel.interior_angles(poly, radian=False)
    angles = list(angles)

    for i in range(len(angles)):
        assert abs(chk_angles[i] - angles[i]) < 1e-10
Exemple #14
0
def test_convex_angles():
    """ Test simple rectangle angles"""
    poly = Polygon2D.from_array([[0, 0], [4, 0], [4, 6], [0, 6]])
    chk_angles = [90, 90, 90, 90]

    angles = polyskel.interior_angles(poly, radian=False)
    angles = list(angles)

    for i in range(len(angles)):
        assert abs(chk_angles[i] - angles[i]) < 1e-10

    # Test pentagon
    poly = Polygon2D.from_array(
        [[0, 0], [4, 0], [4, 6], [2, 8], [0, 6]])

    chk_angles = [90, 135, 90, 135, 90]

    angles = polyskel.interior_angles(poly, radian=False)
    angles = list(angles)

    pp(angles)
    for i in range(len(angles)):
        assert abs(chk_angles[i] - angles[i]) < 1e-10
Exemple #15
0
    def _ytick_radial_lines(bin_vecs, ytick_num):
        """Y-axis lines for radial histogram as List of lineSegment2Ds.

        Args:
            bin_vecs: Array of histogram bin edge vectors.
            ytick_num: Number of Y-axis intervals.
        Returns:
            List of LineSegment2Ds.
        """
        max_bar_radius = 1.0

        # Compute y-axis in radial coordinates, vector multiplication with max_bar_radius
        bar_edge_loop = bin_vecs + [bin_vecs[0]]
        ytick_dist = max_bar_radius / ytick_num  # Length of 1 y-tick
        ytick_array = (((vec1[0] * i * ytick_dist, vec1[1] * i * ytick_dist)
                        for (vec1, _) in bar_edge_loop)
                       for i in range(1, ytick_num + 1))

        return [Polygon2D.from_array((v for v in vecs)) for vecs in ytick_array]
def test_sub_polygon_traversal():
    # Graph methods to retrieve subpolygons

    tol = 1e-10
    pts = [[0, 0], [6, 0], [6, 8], [0, 8]]
    poly = Polygon2D.from_array(pts)

    # Test retrieval of exterior cycle form root node
    g = polysplit._skeleton_as_directed_graph(poly, None, tol)
    chk_nodes = [[0, 0], [6, 0], [6, 8], [0, 8], [3, 5], [3, 3]]
    assert len(chk_nodes) == len(g.ordered_nodes)
    for i, n in enumerate(g.ordered_nodes):
        assert n.pt.is_equivalent(Point2D.from_array(chk_nodes[i]), tol)

    # Check root
    assert g.outer_root_key == _vector2hash(Vector2D(0, 0), 1e-5)

    # Get exterior cycle
    exterior = g.exterior_cycle(g.node(g.outer_root_key))
    chk_nodes = [[0, 0], [6, 0], [6, 8], [0, 8]]
    assert len(chk_nodes) == len(exterior)
    for i, n in enumerate(exterior):
        assert n.pt.is_equivalent(Point2D.from_array(chk_nodes[i]), tol)
Exemple #17
0
def test_perimeter_core_subpolygons():
    """Test splitting perimeter and core subpolygons from polygon."""
    # Construct a simple rectangle
    poly = Polygon2D.from_array([[0, 0], [6, 0], [6, 8], [0, 8]])

    # Make solution polygons (list of polygons)
    test_perims = [
        Polygon2D.from_array(((0.0, 0.0), (6.0, 0.0), (5.0, 1.0), (1.0, 1.0))),
        Polygon2D.from_array(((6.0, 0.0), (6.0, 8.0), (5.0, 7.0), (5.0, 1.0))),
        Polygon2D.from_array(((6.0, 8.0), (0.0, 8.0), (1.0, 7.0), (5.0, 7.0))),
        Polygon2D.from_array(((0.0, 8.0), (0.0, 0.0), (1.0, 1.0), (1.0, 7.0)))
    ]

    test_core = Polygon2D.from_array([[1, 1], [5, 1], [5, 7], [1, 7]])

    # Run method
    perims, cores = polysplit.perimeter_core_subpolygons(poly, 1)

    # Check equality
    for perim, test_perim in zip(perims, test_perims):
        assert perim.is_equivalent(test_perim, 1e-10)

    assert test_core.is_equivalent(cores[0], 1e-10)
Exemple #18
0
def test_skeleton_subpolygons():
    """Test splitting polygon into skeleton polygons"""

    # Make polygon
    poly = Polygon2D.from_array([[2, 0], [2, 2], [1, 1], [0, 2], [0, 0]])

    # Tested Polygons
    test_subpolys = [
        Polygon2D.from_array([[2., 0.], [2., 2.], [1.41421356, 0.58578644]]),
        Polygon2D.from_array([[2., 2.], [1., 1.], [1., 0.41421356],
                              [1.41421356, 0.58578644]]),
        Polygon2D.from_array([[1., 1.], [0., 2.], [0.58578644, 0.58578644],
                              [1., 0.41421356]]),
        Polygon2D.from_array([[0., 2.], [0., 0.], [0.58578644, 0.58578644]]),
        Polygon2D.from_array([[0., 0.], [2., 0.], [1.41421356, 0.58578644],
                              [1., 0.41421356], [0.58578644, 0.58578644]])
    ]

    # Split
    subpolys = polysplit.skeleton_subpolygons(poly)

    # Assert
    for subpoly, test_subpoly in zip(subpolys, test_subpolys):
        assert subpoly.is_equivalent(test_subpoly, 1e-7)
Exemple #19
0
def _skeleton_as_directed_graph(_polygon, holes, tol):
    """Compute the straight skeleton of a polygon as a PolygonDirectedGraph.

    Args:
        polygon: polygon as Polygon2D.
        holes: holes as list of Polygon2Ds.
        tol: Tolerance for point equivalence.

    Returns:
        A PolygonDirectedGraph object.
    """

    if _polygon.is_clockwise:
        # Exterior polygon must be in counter-clockwise order.
        _polygon = _polygon.reverse()
    if holes is not None:
        # Interior polygon must be in counter-clockwise order.
        for i, hole in enumerate(holes):
            if hole.is_clockwise:
                holes[i] = hole.reverse()

    # Make directed graph
    dg = PolygonDirectedGraph(tol=tol)

    # Reverse order to ensure cw order for input
    holes_array = [] if holes is None else [hole.to_array() for hole in holes]
    polygon = _polygon.reverse()  # flip to cw order
    slav = polyskel._SLAV(polygon.to_array(), holes_array, tol)

    # Get the exterior polygon coordinates making sure to flip back to ccw
    for lav in slav._lavs:
        vertices = list(reversed(list(lav)))

        # Get order, account for the fact we flip outer polygons back to ccw order
        is_lav_cw_order = not Polygon2D.from_array([v.point for v in vertices
                                                    ]).is_clockwise

        # Start with last point to be consistent with order of point input, and then
        # add rest of vertices in order.
        for j in range(len(vertices) - 1):
            curr_v = vertices[j]
            next_v = vertices[j + 1]
            k = dg.add_node(curr_v.point, [next_v.point], exterior=True)

            # Add roots
            if j == 0:
                if is_lav_cw_order:  # Outer polygon
                    if dg.outer_root_key is None:
                        dg.outer_root_key = k
                    else:
                        raise RuntimeError(
                            'Outer root key is already assigned. '
                            'Cannot reassign outer root key.')
                else:
                    dg.hole_root_keys.append(k)

        # Close loop
        dg.add_node(vertices[-1].point, [vertices[0].point], exterior=True)

    # Compute the skeleton
    subtree_list = polyskel._skeletonize(slav)

    for subtree in subtree_list:
        event_pt = subtree.source
        for sink_pt in subtree.sinks:
            # Add a bidirectional edge to represent skeleton edges
            dg.add_node(sink_pt, [event_pt])
            dg.add_node(event_pt, [sink_pt], exterior=False)

    return dg
Exemple #20
0
    if isinstance(polygon, list):  # face with holes
        verts = []
        for poly in polygon:
            verts.append([Point3D(pt.x, pt.y, z_height) for pt in poly])
        return from_face3d(Face3D(verts[0], holes=verts[1:]))
    else:
        verts = [Point3D(pt.x, pt.y, z_height) for pt in polygon]
        return from_face3d(Face3D(verts))


if all_required_inputs(ghenv.Component):
    # first extract the straight skeleton from the geometry
    polyskel, boundaries, hole_polygons = [], [], []
    for face in to_face3d(_floor_geo):
        # convert the input geometry into Polygon2D for straight skeleton analysis
        boundary = Polygon2D.from_array([(pt.x, pt.y) for pt in face.boundary])
        if boundary.is_clockwise:
            boundary = boundary.reverse()
        holes, z_height = None, face[0].z
        if face.has_holes:
            holes = []
            for hole in face.holes:
                h_poly = Polygon2D.from_array([(pt.x, pt.y) for pt in hole])
                if not h_poly.is_clockwise:
                    h_poly = h_poly.reverse()
                holes.append(h_poly)
        boundaries.append(boundary)
        hole_polygons.append(holes)
        # compute the skeleton and convert to line segments
        skel_lines = skeleton_as_edge_list(boundary, holes, tolerance)
        skel_lines_rh = [