Exemple #1
0
    def face_flatness(self, face, maxdev=0.02):
        """Compute the flatness of a face.

        Parameters
        ----------
        face : int
            The identifier of the face.

        Returns
        -------
        float
            The flatness.

        Note
        ----
        compas.geometry.mesh_flatness function currently only works for quadrilateral faces.
        This function uses the distance between each face vertex and its projected point
        on the best-fit plane of the face as the flatness metric.

        """
        deviation = 0
        polygon = self.face_coordinates(face)
        plane = bestfit_plane(polygon)
        for pt in polygon:
            pt_proj = project_point_plane(pt, plane)
            dev = distance_point_point(pt, pt_proj)
            if dev > deviation:
                deviation = dev
        return deviation
Exemple #2
0
def calc_correction_vector_tip(pt_new, base_pts):
    """Computing correction vector to meet the distance threshold.
        return vector P-P_c (figure below).

    .. image:: ../images/vertex_correction_to_top_connection.png
        :scale: 80 %
        :align: center

    Parameters
    ----------
    pt_new : [type]
        [description]
    base_pts : list of three points
        contact base points for the base bars
    """
    assert len(base_pts) == 3
    vec_x   = normalize_vector(vector_from_points(base_pts[0], base_pts[1]))
    vec_y   = normalize_vector(vector_from_points(base_pts[0], base_pts[2]))
    vec_z   = normalize_vector(cross_vectors(vec_x, vec_y))
    pl_test = (base_pts[0], vec_z)
    dist_p  = distance_point_plane(pt_new, pl_test)
    pt_proj = project_point_plane(pt_new, pl_test)

    if dist_p < NODE_CORRECTION_TOP_DISTANCE:
        vec_m = scale_vector(normalize_vector(vector_from_points(pt_proj, pt_new)), NODE_CORRECTION_TOP_DISTANCE)
        pt_n = add_vectors(pt_proj, vec_m)
    else:
        pt_n = None
    return pt_n
Exemple #3
0
def polygon_flatness(polygon):
    """Comput the flatness of a polygon.

    Parameters
    ----------
    polygon : list of lists
        A list of polygon point coordinates.

    Returns
    -------
    float
        The flatness.

    Note
    ----
    compas.geometry.mesh_flatness function currently only works for quadrilateral faces.
    This function uses the distance between each face vertex and its projected point on the best-fit plane of the face as the flatness metric.

    """
    deviation = 0

    plane     = bestfit_plane(polygon)

    for pt in polygon:
        pt_proj = project_point_plane(pt, plane)
        dev     = distance_point_point(pt, pt_proj)
        if dev > deviation:
            deviation = dev

    return deviation
Exemple #4
0
def correct_angle(pt_new, pt_int, pl_test):
    """Computing correction vector to meet the angle threshold.
        return vector P-P_c (figure below).

    .. image:: ../images/vertex_correction_to_base.png
        :scale: 80 %
        :align: center

    Parameters
    ----------
    pt_new : point
        new point P
    pt_int : point
        contact point between two bars, i.e. point N in the image above
    pl_test : tuple (point, vector)
        the grey plane shown in the image above

    Returns
    -------
    tuple of two points
        return None if feasible (bigger than the angle threshold), otherwise return the line connecting pt_int and modified pt
    """

    pt_proj = project_point_plane(pt_new, pl_test)
    sin_ang = distance_point_point(pt_new, pt_proj)/distance_point_point(pt_new, pt_int)
    if sin_ang < NODE_CORRECTION_SINE_ANGLE:
        # ? why 0.3 here?
        # length of the triangle's hypotenuse
        # dist_n = 0.3 * distance_point_point(pt_new, pt_int)
        dist_n = NODE_CORRECTION_SINE_ANGLE * distance_point_point(pt_new, pt_int)
        # modified point Pc
        pt_m = add_vectors(pt_proj, scale_vector(normalize_vector(vector_from_points(pt_proj, pt_new)), dist_n))
        lin_c = (pt_int, pt_m)
        return lin_c
    else:
        return None
Exemple #5
0
    mesh.add_vertex(current_key)
    mesh.vertex[current_key].update({'x': pt[0], 'y': pt[1], 'z': pt[2]})

    current_key += 1

for key in convex_hull_mesh.vertices():
    nbrs = convex_hull_mesh.vertex_neighbors(key)
    for nbr in nbrs:
        halfedge = (key, nbr)
        pt_joint_descendent = mesh.vertex_coordinates(
            descdent_tree[key][nbr]['jp'])

        vec_edge = Vector.from_start_end(pt_center,
                                         mesh.vertex_coordinates(key))
        pln_end = Plane(mesh.vertex_coordinates(key), vec_edge)
        pt = project_point_plane(pt_joint_descendent, pln_end)
        vec_leaf = Vector.from_start_end(mesh.vertex_coordinates(key), pt)
        vec_leaf.unitize()
        vec_leaf.scale(leaf_width)
        pt = add_vectors(mesh.vertex_coordinates(key), vec_leaf)

        descdent_tree[key][nbr].update({'lp': current_key})

        mesh.add_vertex(current_key)
        mesh.vertex[current_key].update({'x': pt[0], 'y': pt[1], 'z': pt[2]})
        current_key += 1

for key in convex_hull_mesh.vertices():
    nbrs = convex_hull_mesh.vertex_neighbors(key, ordered=True)
    v_keys = nbrs + [nbrs[0]]
    for a, b in pairwise(v_keys):
Exemple #6
0
 def project_to_plane(self, plane):
     plane = (plane.point, plane.normal)
     return project_point_plane(self, plane)
Exemple #7
0
def test_project_point_plane():
    assert allclose(project_point_plane([0, 2.5, 2], ([3, 4, 5], [6, 7, 8.8])),
                    [2.0278256587047525, 4.865796601822211, 4.974144299433638])
Exemple #8
0
def cell_planarise(cell,
                   kmax=100,
                   target_centers={},
                   target_normals={},
                   target_areas={},
                   fix_vkeys=[],
                   avg_fkeys=[],
                   collapse_edge_length=0.1,
                   tolerance_flat=0.001,
                   tolerance_area=0.001,
                   callback=None,
                   callback_args=None,
                   print_result_info=False):
    """Planarise the faces of a cell.

    Planarisation of a cell is implemented as a three-step iterative procedure.
    At every iteration, each face is first individually projected to its best-fit plane (unless a target normal is given).
    Then, each face is re-sized to its target area (if given).
    Finally, the new vertex coordinates are computed by taking the centroid of the disconnected corners of the faces.

    Parameters
    ----------

    cell : Mesh
        A mesh object

    kmax : int, optional [100]
        Number of iterations.

    target_face_areas : dictionary, optional [{}]
        A dictionary of fkeys and target areas.
    target_face_normals : dictionary, optional [{}]
        A dictionary of fkeys and target face normals.
    target_face_centers : dictionary, optional [{}]
        A dictionary of fkeys and target face centers.

    fix_vkeys : list, optional [[]]
        List of vkeys to omit from arearisation.

    avg_fkeys : list, optional [[]]
        List of fkeys to average areas.

    collapse_edge_length : real, optional [0.1]
        Minimum length of edge to collapse.

    tolerance_flat: float, optional
        Convergence tolerance for face flatness.
    tolerance_area: float, optional
        Convergence tolerance for face areas.

    callback : callable, optional
        A user-defined callback function to be executed after every iteration.
        Default is ``None``.
    callback_args : tuple, optional
        Additional parameters to be passed to the callback.
        Default is ``None``.

    Raises
    ------
    Exception
        If a callback is provided, but it is not callable.

    .. seealso ::
        `compas.geometry.mesh_planarize_faces`

    """
    if callback:
        if not callable(callback):
            raise Exception('Callback is not callable.')

    # --------------------------------------------------------------------------
    #   1. initialise
    # --------------------------------------------------------------------------
    free_vkeys = list(set(cell.vertex) - set(fix_vkeys))

    # --------------------------------------------------------------------------
    #   2. loop
    # --------------------------------------------------------------------------
    for k in range(kmax):

        deviation_flat = 0
        deviation_area = 0
        deviation_perp = 0

        new_xyz = {vkey: [] for vkey in cell.vertex}

        # faces to be averaged -------------------------------------------------
        if avg_fkeys:
            avg_face_area = _avg_face_area(cell, avg_fkeys)

        for fkey in cell.faces():

            # evaluate current face --------------------------------------------
            f_vkeys = cell.face_vertices(fkey)
            f_v_xyz = cell.face_coordinates(fkey)
            f_normal = cell.face_normal(fkey)
            f_area = cell.face_area(fkey)
            f_center = cell.face_centroid(fkey)

            if fkey in target_centers:
                f_center = target_centers[fkey]

            if fkey in target_normals:
                target_normal = target_normals[fkey]

                # perpness deviation
                perpness = 1 - abs(dot_vectors(f_normal, target_normal))
                if perpness > deviation_perp:
                    deviation_perp = perpness

                f_normal = target_normal

            # projection plane -------------------------------------------------
            plane = (f_center, f_normal)

            # ------------------------------------------------------------------
            #   3. planarise
            # ------------------------------------------------------------------
            projected_face = []
            for xyz in f_v_xyz:
                projected_xyz = project_point_plane(xyz, plane)
                projected_face.append(projected_xyz)

                # planarisation deviation
                dist = distance_point_point(xyz, projected_xyz)
                if dist > deviation_flat:
                    deviation_flat = dist

            # ------------------------------------------------------------------
            #   4. arearise
            # ------------------------------------------------------------------
            if fkey in target_areas:
                target_area = target_areas[fkey]

            else:
                target_area = f_area

            if fkey in avg_fkeys:
                target_area = avg_face_area

            # scale factor -----------------------------------------------------
            if target_area != 0:
                scale = (target_area / f_area)**0.5

            elif target_area == 0:
                scale = 1 - f_area * 0.1
                # scale = (target_area / f_area) ** 0.5
                # scale = 0.9

            # scale ------------------------------------------------------------
            scaled_face = scale_polygon(projected_face, scale)

            # arearisation deviation
            areaness = abs(f_area - target_area)
            if areaness > deviation_area:
                deviation_area = areaness

            # collect new coordinates
            for i in range(len(f_vkeys)):
                new_xyz[f_vkeys[i]].append(scaled_face[i])

        # ----------------------------------------------------------------------
        #   5. compute new cell vertex coordinates
        # ----------------------------------------------------------------------
        for vkey in free_vkeys:
            final_xyz = centroid_points(new_xyz[vkey])
            cell.vertex_update_xyz(vkey, final_xyz)

        for u, v in cell.edges():
            cell_collapse_short_edge(cell,
                                     u,
                                     v,
                                     min_length=collapse_edge_length)

        # ----------------------------------------------------------------------
        #   7. check convergence
        # ----------------------------------------------------------------------
        if deviation_flat < tolerance_flat and deviation_area < tolerance_area:

            if print_result_info:

                name = "Cell planarisation"
                deviation = deviation_flat

                if target_areas:
                    name = "Cell arearisation"
                    deviation = deviation_area

                print_result(name, k, deviation)

            break

        # callback / conduit ---------------------------------------------------
        if callback:
            callback(cell, k, callback_args)
Exemple #9
0
def volmesh_planarise(volmesh,
                      kmax=100,
                      target_centers={},
                      target_normals={},
                      target_areas={},
                      fix_vkeys=[],
                      fix_boundary_normals=False,
                      fix_all_normals=False,
                      min_area=None,
                      max_area=None,
                      tolerance_flat=0.001,
                      tolerance_area=0.001,
                      tolerance_perp=0.001,
                      callback=None,
                      callback_args=None,
                      print_result_info=False):
    """Planarises the halffaces of a volmesh.

    Planarisation of a volmesh is implemented as a three-step iterative procedure.
    At every iteration, each halfface is first individually projected to its best-fit plane (unless a target normal is given).
    Then, each halfface is re-sized to its target area (if given).
    Finally, the new vertex coordinates are computed by taking the centroid of the disconnected corners of the halffaces.

    Parameters
    ----------
    volmesh : VolMesh
        A volmesh object.
    kmax : int, optional [100]
        Number of iterations.
    target_face_areas : dictionary, optional [{}]
        A dictionary of fkeys and target areas.
    target_face_normals : dictionary, optional [{}]
        A dictionary of fkeys and target face normals.
    target_face_centers : dictionary, optional [{}]
        A dictionary of fkeys and target face centers.
    omit_vkeys : list, optional [[]]
        List of vkeys to omit from arearisation.
    fix_boundary_face_normals : boolean, optional [False]
        Whether to keep the initial normals of the bondary faces.
    fix_all_face_normals : boolean, optional [False]
        Whether to keep the initial normals of all faces.
    tolerance_flat: float, optional
        Convergence tolerance for face flatness.
    tolerance_area: float, optional
        Convergence tolerance for face areas against target areas.
    tolerance_perp: float, optional
        Convergence tolerance for face perpendicularity against target normals.
    callback : callable, optional
        A user-defined callback function to be executed after every iteration.
        Default is ``None``.
    callback_args : tuple, optional
        Additional parameters to be passed to the callback.
        Default is ``None``.
    print_result_info : bool, optional
        If True, print the result of the algorithm.

    Raises
    ------
    Exception
        If a callback is provided, but it is not callable.

    .. seealso ::
        `compas.geometry.mesh_planarize_faces`

    """
    if callback:
        if not callable(callback):
            raise Exception('Callback is not callable.')

    # --------------------------------------------------------------------------
    #   1. initialise
    # --------------------------------------------------------------------------
    free_vkeys = list(set(volmesh.vertices()) - set(fix_vkeys))
    initial_normals = _get_current_volmesh_normals(volmesh)
    boundary_fkeys = volmesh.halffaces_on_boundaries()

    # --------------------------------------------------------------------------
    #   2. loop
    # --------------------------------------------------------------------------
    for k in range(kmax):

        deviation_flat = 0
        deviation_area = 0
        deviation_perp = 0

        new_xyz = {vkey: [] for vkey in volmesh.vertices()}

        for fkey in volmesh.faces():

            fkey_pair = volmesh.halfface_opposite_halfface(fkey)

            # evaluate current face --------------------------------------------
            f_vkeys = volmesh.halfface_vertices(fkey)
            f_v_xyz = volmesh.halfface_coordinates(fkey)
            f_center = volmesh.halfface_center(fkey)
            f_normal = volmesh.halfface_normal(fkey)
            f_area = volmesh.halfface_area(fkey)

            # override with manual target values -------------------------------
            if _pair_membership(fkey, fkey_pair, target_centers):
                f_center = target_centers[fkey]

            if _pair_membership(fkey, fkey_pair, target_normals):
                target_normal = target_normals[fkey]

                # perpness deviation
                perpness = 1 - abs(dot_vectors(f_normal, target_normal))
                if perpness > deviation_perp:
                    deviation_perp = perpness

                f_normal = target_normal

            if fix_boundary_normals:
                if fkey in boundary_fkeys:
                    f_normal = initial_normals[fkey]['normal']
            if fix_all_normals:
                f_normal = initial_normals[fkey]['normal']

            # projection plane -------------------------------------------------
            plane = (f_center, f_normal)

            # ------------------------------------------------------------------
            #   3. planarise
            # ------------------------------------------------------------------
            new_face = []
            for xyz in f_v_xyz:
                projected_xyz = project_point_plane(xyz, plane)
                new_face.append(projected_xyz)

                # planarisation deviation
                flatness = distance_point_point(xyz, projected_xyz)
                if flatness > deviation_flat:
                    deviation_flat = flatness

            # ------------------------------------------------------------------
            #   4. arearise
            # ------------------------------------------------------------------
            if target_areas:
                if fkey in target_areas:
                    target_area = target_areas[fkey]
                    scale = (target_area / f_area)**0.5

                    # scale
                    new_face = scale_polygon(new_face, scale)

                    # arearisation deviation
                    areaness = abs(f_area - target_area)
                    if areaness > deviation_area:
                        deviation_area = areaness

            # collect new coordinates ------------------------------------------
            for i in range(len(f_vkeys)):
                new_xyz[f_vkeys[i]].append(new_face[i])

        # ----------------------------------------------------------------------
        #   5. compute new volmesh vertex coordinates
        # ----------------------------------------------------------------------
        for vkey in free_vkeys:
            final_xyz = centroid_points(new_xyz[vkey])
            volmesh.vertex_update_xyz(vkey, final_xyz)

        # ----------------------------------------------------------------------
        #   6. check convergence
        # ----------------------------------------------------------------------
        if deviation_flat < tolerance_flat and deviation_area < tolerance_area and deviation_perp < tolerance_perp:

            if print_result_info:

                name = "Volmesh planarisation"
                deviation = deviation_flat

                if target_areas:
                    name = "Volmesh arearisation"
                    deviation = deviation_area

                print_result(name, k, deviation)

            break

        # callback / conduit ---------------------------------------------------
        if callback:
            callback(volmesh, k, callback_args)
    ring_w = mesh.vertex_attribute(w, 'ring')
    if not ring_w:
        continue

    length_uv = mesh.edge_length(u, v)
    size_uv = str(mesh.edge_attribute((u, v), 'size'))
    radius_w = mesh.attributes['radius'][ring_w]

    xyz_u = mesh.vertex_coordinates(u)
    xyz_w = mesh.vertex_coordinates(w)

    normal_w = mesh.vertex_normal(w)
    plane_w = xyz_w, normal_w

    u_on_plane_w = project_point_plane(xyz_u, plane_w)
    wu_dir = normalize_vector(subtract_vectors(u_on_plane_w, xyz_w))
    xyz_w_1 = add_vectors(xyz_w, scale_vector(wu_dir, radius_w))
    uw_dir_real = normalize_vector(subtract_vectors(xyz_w_1, xyz_u))
    wu_dir_real = normalize_vector(subtract_vectors(xyz_u, xyz_w_1))

    connector_u = mesh.attributes['connector'][size_uv]
    pin = mesh.attributes['pin'][size_uv]
    hole = mesh.attributes['hole'][size_uv]

    xyz_u_1 = add_vectors(
        xyz_u, scale_vector(uw_dir_real, 1e-3 * (0.5 * connector_u + 100)))
    xyz_u_2 = add_vectors(xyz_u, scale_vector(uw_dir_real, -1e-3 * 100))
    xyz_v_1 = add_vectors(
        xyz_u, scale_vector(uw_dir_real,
                            length_uv - 1e-3 * (80 + 4 + 5 + pin)))
Exemple #11
0
class SkeletonVol(Mesh):
    """ SkeletonVol is typologically constructed low poly mesh.
    It construct a branch like volumetric mesh from input lines. 
    """
    def __init__(self):
        super(SkeletonVol, self).__init__()

    @classmethod
    def from_skeleton_lines(cls, lines=[]):
        skeleton_vol = cls()

        network = Network.from_lines(lines)
        convex_hull_mesh = get_convex_hull_mesh(points)

    def get_convex_hull_mesh(points):
        faces = convex_hull(points)
        vertices = list(set(flatten(faces)))

        i_index = {i: index for index, i in enumerate(vertices)}
        vertices = [points[index] for index in vertices]
        faces = [[i_index[i] for i in face] for face in faces]
        faces = unify_cycles(vertices, faces)

        mesh = Mesh.from_vertices_and_faces(vertices, faces)

        return mesh

    guids = compas_rhino.select_lines()
    lines = compas_rhino.get_line_coordinates(guids)

    network = Network.from_lines(lines)

    leafs = []
    joints = []
    for key in network.node:
        if network.is_leaf(key):
            leafs.append(key)
        else:
            joints.append(key)

    pt_center = network.node_coordinates(joints[0])
    pts = [network.node_coordinates(key) for key in leafs]

    convex_hull_mesh = get_convex_hull_mesh(pts)

    mesh = Mesh()
    # for key in convex_hull_mesh.vertices():
    #     mesh.add_vertex(key)
    #     mesh.vertex[key].update(convex_hull_mesh.vertex[key])

    descdent_tree = copy.deepcopy(convex_hull_mesh.halfedge)

    for u, v in convex_hull_mesh.edges():
        descdent_tree[u][v] = {'jp': None, 'lp': None}
        descdent_tree[v][u] = {'jp': None, 'lp': None}

    # current_key = convex_hull_mesh.number_of_vertices()
    current_key = 0

    for fkey in convex_hull_mesh.faces():
        f_centroid = convex_hull_mesh.face_centroid(fkey)
        vec = Vector.from_start_end(pt_center, f_centroid)

        # if the branches has a 'convex' corner,
        # flip the vec to the corresponding face.
        f_normal = convex_hull_mesh.face_normal(fkey)
        angle = angle_vectors(f_normal, vec, False)
        if angle > math.pi * 0.5:
            pln = Plane(pt_center, f_normal)
            pt_mirror = mirror_point_plane(f_centroid, pln)
            vec = Vector.from_start_end(pt_center, pt_mirror)

        vec.unitize()
        vec.scale(joint_width)

        pt = add_vectors(pt_center, vec)

        face = convex_hull_mesh.face[fkey]
        v_keys = face + [face[0]]
        for u, v in pairwise(v_keys):
            descdent_tree[u][v].update({'jp': current_key})

        mesh.add_vertex(current_key)
        mesh.vertex[current_key].update({'x': pt[0], 'y': pt[1], 'z': pt[2]})

        current_key += 1

    for key in convex_hull_mesh.vertices():
        nbrs = convex_hull_mesh.vertex_neighbors(key)
        for nbr in nbrs:
            halfedge = (key, nbr)
            pt_joint_descendent = mesh.vertex_coordinates(
                descdent_tree[key][nbr]['jp'])

            vec_edge = Vector.from_start_end(
                pt_center, convex_hull_mesh.vertex_coordinates(key))
            pln_end = Plane(convex_hull_mesh.vertex_coordinates(key), vec_edge)
            pt = project_point_plane(pt_joint_descendent, pln_end)
            vec_leaf = Vector.from_start_end(
                convex_hull_mesh.vertex_coordinates(key), pt)
            vec_leaf.unitize()
            vec_leaf.scale(leaf_width)
            pt = add_vectors(convex_hull_mesh.vertex_coordinates(key),
                             vec_leaf)

            descdent_tree[key][nbr].update({'lp': current_key})

            mesh.add_vertex(current_key)
            mesh.vertex[current_key].update({
                'x': pt[0],
                'y': pt[1],
                'z': pt[2]
            })
            current_key += 1

    for key in convex_hull_mesh.vertices():
        nbrs = convex_hull_mesh.vertex_neighbors(key, ordered=True)
        v_keys = nbrs + [nbrs[0]]
        for a, b in pairwise(v_keys):
            face = [
                descdent_tree[key][a]['lp'], descdent_tree[key][a]['jp'],
                descdent_tree[key][b]['jp'], descdent_tree[key][b]['lp']
            ]
            mesh.add_face(face)

    fixed = list(mesh.vertices_where({'vertex_degree': 3}))
    fixed = list(mesh.vertices())

    mesh = mesh_subdivide_catmullclark(mesh, k=1, fixed=fixed)
    # mesh = mesh_subdivide_quad(mesh, k=1)
    mesh_smooth_centroid(mesh, fixed=fixed)

    artist = MeshArtist(mesh)
    artist.draw_mesh()