def branches_splitting_boundary_kinks(self):
        """Add new branches to fix the problem of boundary kinks not marked by the skeleton
		Due to a low density that did not spot the change of curvature at the kink.
		Does not modify the singularites on the contrarty to increasing the density.

		Returns
		-------
		new_branches : list
			List of polylines as list of point XYZ-coordinates.

		"""

        new_branches = []

        compas_singular_faces = set(self.compas_singular_faces())
        for boundary in self.boundaries():
            angles = {(u, v, w): angle_vectors(
                subtract_vectors(self.vertex_coordinates(v),
                                 self.vertex_coordinates(u)),
                subtract_vectors(self.vertex_coordinates(w),
                                 self.vertex_coordinates(v)))
                      for u, v, w in window(boundary + boundary[:2], n=3)}
            for u, v, w, x, y in list(window(boundary + boundary[:4], n=5)):

                # check if not a corner
                if self.vertex_degree(w) == 2:
                    continue

                angle = angles[(v, w, x)]
                adjacent_angles = (angles[(u, v, w)] + angles[(w, x, y)]) / 2

                if angle - adjacent_angles > self.relative_kink_angle_limit:
                    # check if not already marked via an adjacent compas_singular face
                    if all([
                            fkey not in compas_singular_faces
                            for fkey in self.vertex_faces(w)
                    ]):
                        fkeys = list(self.vertex_faces(w, ordered=True))
                        fkey = fkeys[int(floor(len(fkeys) / 2))]
                        for edge in self.face_halfedges(fkey):
                            if w in edge and not self.is_edge_on_boundary(
                                    *edge):
                                new_branches += [[
                                    trimesh_face_circle(self, fkey)[0],
                                    self.vertex_coordinates(vkey)
                                ] for vkey in edge]
                                break

        return new_branches
Esempio n. 2
0
    def face_skewness(self, fkey):
        """Face skewness as the maximum absolute angular deviation from the ideal polygon angle.

        Parameters
        ----------
        fkey : Key
            The face key.

        Returns
        -------
        float
            The skewness.

        References
        ----------
        .. [1] Wikipedia. *Types of mesh*.
               Available at: https://en.wikipedia.org/wiki/Types_of_mesh.
        """
        ideal_angle = 180 * (1 - 2 / float(len(self.face_vertices(fkey))))
        angles = []
        vertices = self.face_vertices(fkey)
        for u, v, w in window(vertices + vertices[:2], n=3):
            o = self.vertex_coordinates(v)
            a = self.vertex_coordinates(u)
            b = self.vertex_coordinates(w)
            angle = angle_points(o, a, b, deg=True)
            angles.append(angle)
        return max((max(angles) - ideal_angle) / (180 - ideal_angle),
                   (ideal_angle - min(angles)) / ideal_angle)
Esempio n. 3
0
    def kagome_polyedge_weaving(self):

        mesh = self.kagome

        edge_to_polyedge_index = {}
        for i, polyedge in enumerate(self.kagome_polyedge_data):
            for u, v in pairwise(polyedge):
                edge_to_polyedge_index[(u, v)] = i
                edge_to_polyedge_index[(v, u)] = i

        vertex_to_polyege_offset = {vkey: {} for vkey in mesh.vertices()}
        for fkey in mesh.faces():
            if len(mesh.face_vertices(fkey)) == 3:
                for u, v, w in window(mesh.face_vertices(fkey) +
                                      mesh.face_vertices(fkey)[:2],
                                      n=3):
                    vertex_to_polyege_offset[v].update({
                        edge_to_polyedge_index[(u, v)]:
                        +1,
                        edge_to_polyedge_index[(v, w)]:
                        -1
                    })
            else:
                for u, v, w in window(mesh.face_vertices(fkey) +
                                      mesh.face_vertices(fkey)[:2],
                                      n=3):
                    vertex_to_polyege_offset[v].update({
                        edge_to_polyedge_index[(u, v)]:
                        -1,
                        edge_to_polyedge_index[(v, w)]:
                        +1
                    })

        polyedge_weave = []
        for i, polyedge in enumerate(self.kagome_polyedge_data):
            polyedge_weave.append(
                [vertex_to_polyege_offset[vkey][i] for vkey in polyedge])

        return polyedge_weave
    def branches_splitting_flipped_faces(self):
        """Add new branches to fix the problem of polyline patches that would form flipped faces in the decomposition mesh.

		Returns
		-------
		new_branches : list
			List of polylines as list of point XYZ-coordinates.

		"""

        new_branches = []
        centre_to_fkey = {
            geometric_key(trimesh_face_circle(self, fkey)[0]): fkey
            for fkey in self.faces()
        }

        # compute total rotation of polyline
        for polyline in self.branches_singularity_to_singularity():
            angles = [
                angle_vectors_signed(subtract_vectors(v, u),
                                     subtract_vectors(w, v), [0., 0., 1.])
                for u, v, w in window(polyline, n=3)
            ]
            # subdivide once per angle limit in rotation
            if abs(sum(angles)) > self.flip_angle_limit:
                # the step between subdivision points in polylines (+ 2 for the extremities, which will be discarded)
                alone = len(self.compas_singular_faces()) == 0
                n = floor(abs(sum(angles)) / self.flip_angle_limit) + 1
                step = int(floor(len(polyline) / n))
                # add new branches from corresponding face in Delaunay mesh
                seams = polyline[::step]
                if polyline[-1] != seams[-1]:
                    if len(seams) == n + 1:
                        del seams[-1]
                    seams.append(polyline[-1])
                if alone:
                    seams = seams[0:-1]
                else:
                    seams = seams[1:-1]
                for point in seams:
                    fkey = centre_to_fkey[geometric_key(point)]
                    for edge in self.face_halfedges(fkey):
                        if not self.is_edge_on_boundary(*edge):
                            new_branches += [[
                                trimesh_face_circle(self, fkey)[0],
                                self.vertex_coordinates(vkey)
                            ] for vkey in edge]
                            break

        return new_branches
Esempio n. 5
0
def flatness(vertices, faces, maxdev=0.02):
    """Compute mesh flatness per face.

    Parameters
    ----------
    vertices : list
        The vertex coordinates.
    faces : list
        The face vertices.
    maxdev : float, optional
        A maximum value for the allowed deviation from flatness.
        Default is ``0.02``.

    Returns
    -------
    dict
        For each face, a deviation from *flatness*.

    Notes
    -----
    The "flatness" of a face is expressed as the ratio of the distance between
    the diagonals to the average edge length. For the fabrication of glass panels,
    for example, ``0.02`` could be a reasonable maximum value.

    Warning
    -------
    This function only works as expected for quadrilateral faces.

    See Also
    --------
    * :func:`compas.geometry.mesh_flatness`

    """
    dev = []
    for face in faces:
        points = [vertices[index] for index in face]
        lengths = [
            distance_point_point(a, b)
            for a, b in window(points + points[0:1], 2)
        ]
        l = sum(lengths) / len(lengths)
        d = distance_line_line((points[0], points[2]), (points[1], points[3]))
        dev.append((d / l) / maxdev)
    return dev
Esempio n. 6
0
def is_polygon_convex(polygon):
    """Determine if a polygon is convex.

    Parameters
    ----------
    polygon : sequence of sequence of floats
        The XYZ coordinates of the corners of the polygon.

    Notes
    -----
    Use this function for *spatial* polygons.
    If the polygon is in a horizontal plane, use :func:`is_polygon_convex_xy` instead.

    Returns
    -------
    bool
        ``True`` if the polygon is convex.
        ``False`` otherwise.

    See Also
    --------
    is_polygon_convex_xy

    Examples
    --------
    >>> polygon = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.4, 0.4, 0.0], [0.0, 1.0, 0.0]]
    >>> is_polygon_convex(polygon)
    False
    """
    a = polygon[0]
    o = polygon[1]
    b = polygon[2]
    oa = subtract_vectors(a, o)
    ob = subtract_vectors(b, o)
    n0 = cross_vectors(oa, ob)
    for a, o, b in window(polygon + polygon[:2], 3):
        oa = subtract_vectors(a, o)
        ob = subtract_vectors(b, o)
        n = cross_vectors(oa, ob)
        if dot_vectors(n, n0) >= 0:
            continue
        else:
            return False
    return True
Esempio n. 7
0
def mesh_flatness(mesh, maxdev=1.0):
    """Compute mesh flatness per face.

    Parameters
    ----------
    mesh : Mesh
        A mesh object.
    maxdev : float, optional
        A maximum value for the allowed deviation from flatness.
        Default is ``1.0``.

    Returns
    -------
    dict
        For each face, a deviation from *flatness*.

    Notes
    -----
    The "flatness" of a face is expressed as the ratio of the distance between
    the diagonals to the average edge length. For the fabrication of glass panels,
    for example, ``0.02`` could be a reasonable maximum value.

    Warnings
    --------
    This function only works as expected for quadrilateral faces.

    """
    dev = []
    for fkey in mesh.faces():
        points = mesh.face_coordinates(fkey)
        if len(points) == 3:
            dev.append(0.0)
        else:
            lengths = [
                distance_point_point(a, b)
                for a, b in window(points + points[0:1], 2)
            ]
            length = sum(lengths) / len(lengths)
            d = distance_line_line((points[0], points[2]),
                                   (points[1], points[3]))
            dev.append((d / length) / maxdev)
    return dev
Esempio n. 8
0
def is_polygon_convex(polygon):
    """Determine if a polygon is convex.

    Parameters
    ----------
    polygon : sequence[point] | :class:`compas.geometry.Polygon`
        A polygon.

    Returns
    -------
    bool
        True if the polygon is convex.
        False otherwise.

    Notes
    -----
    Use this function for *spatial* polygons.
    If the polygon is in a horizontal plane, use :func:`is_polygon_convex_xy` instead.

    Examples
    --------
    >>> polygon = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.4, 0.4, 0.0], [0.0, 1.0, 0.0]]
    >>> is_polygon_convex(polygon)
    False

    """
    a = polygon[0]
    o = polygon[1]
    b = polygon[2]
    oa = subtract_vectors(a, o)
    ob = subtract_vectors(b, o)
    n0 = cross_vectors(oa, ob)
    for a, o, b in window(polygon + polygon[:2], 3):
        oa = subtract_vectors(a, o)
        ob = subtract_vectors(b, o)
        n = cross_vectors(oa, ob)
        if dot_vectors(n, n0) >= 0:
            continue
        else:
            return False
    return True
Esempio n. 9
0
def mesh_flatness(mesh):
    """Compute mesh flatness per face.

    The "flatness" of a face is expressed as the average of the angles between
    the normal at each face corner and the normal of the best-fit plane.
    """
    dev = {}
    for fkey in mesh.faces():
        points = mesh.face_coordinates(fkey)
        base, normal = bestfit_plane_from_points(points)
        angles = []
        for a, b, c in window(points + points[0:2], 3):
            u = subtract_vectors(a, b)
            v = subtract_vectors(c, b)
            n = cross_vectors(u, v)
            if dot_vectors(n, normal) > 0:
                angle = angle_smallest_vectors(n, normal)
            else:
                angle = angle_smallest_vectors(n, scale_vector(normal, -1))
            angles.append(angle)
        dev[fkey] = sum(angles) / len(angles)
    return dev
Esempio n. 10
0
 def face_corners(self, fkey):
     vertices = self.face_vertices(fkey)
     return window(vertices + vertices[0:2], 3)
Esempio n. 11
0
controlpoints = [
    Point(0, 0, 0),
    Point(4, 2.5, 0),
    Point(6, -2.5, 0),
    Point(10, 0, 0)
]
controlpoly = Polyline(controlpoints)

curve = Bezier(controlpoints)
poly = Polyline(curve.locus())
poly1 = Polyline(offset_polyline(poly, +0.15))
poly2 = Polyline(offset_polyline(poly, -0.15))

points = [poly.point(t) for t in linspace(0, 1, 20)]
tangents = [(c - a).unitized() for a, b, c in window(points, 3) if a and c]
normals = [Vector(0, 0, 1).cross(t) for t in tangents]
lines = [[point, point + normal]
         for point, normal in zip(points[1:-1], normals)]

points1 = [intersection_line_polyline(line, poly1) for line in lines]
points2 = [intersection_line_polyline(line, poly2) for line in lines]

frames = []
for a, b in pairwise(points[1:-1]):
    p = (a + b) * 0.5
    t = (b - a).unitized()
    n = Vector(0, 0, 1).cross(t)
    frame = Frame(p, t, n)
    frames.append(frame)
Esempio n. 12
0
def center_of_mass_polyhedron(polyhedron):
    """Compute the center of mass of a polyhedron.

    Parameters
    ----------
    polyhedron : tuple
        The coordinates of the vertices,
        and the indices of the vertices forming the faces.

    Returns
    -------
    tuple
        XYZ coordinates of the center of mass.

    Examples
    --------
    >>> from compas.geometry import Polyhedron
    >>> p = Polyhedron.generate(6)
    >>> center_of_mass_polyhedron((p.vertices, p.faces))
    (-4.206480876464043e-17, -4.206480876464043e-17, -4.206480876464043e-17)

    """
    vertices, faces = polyhedron

    V = 0
    x = 0.0
    y = 0.0
    z = 0.0
    ex = [1.0, 0.0, 0.0]
    ey = [0.0, 1.0, 0.0]
    ez = [0.0, 0.0, 1.0]

    for face in faces:
        if len(face) == 3:
            triangles = [face]
        else:
            centroid = centroid_points([vertices[index] for index in face])
            vertices.append(centroid)
            w = len(vertices) - 1
            triangles = [[u, v, w] for u, v in window(face + face[0:1], 2)]

        for triangle in triangles:
            a = vertices[triangle[0]]
            b = vertices[triangle[1]]
            c = vertices[triangle[2]]
            ab = subtract_vectors(b, a)
            ac = subtract_vectors(c, a)
            n = cross_vectors(ab, ac)
            V += dot_vectors(a, n)
            nx = dot_vectors(n, ex)
            ny = dot_vectors(n, ey)
            nz = dot_vectors(n, ez)

            for j in (-1, 0, 1):
                ab = add_vectors(vertices[triangle[j]],
                                 vertices[triangle[j + 1]])
                x += nx * dot_vectors(ab, ex)**2
                y += ny * dot_vectors(ab, ey)**2
                z += nz * dot_vectors(ab, ez)**2

    if V < 1e-9:
        V = 0.0
        d = 1.0 / 48.0
    else:
        V = V / 6.0
        d = 1.0 / 48.0 / V

    x *= d
    y *= d
    z *= d

    return x, y, z