Example #1
0
        faces = [[0, 1, 2, 3]]

        mesh = Mesh.from_vertices_and_faces(vertices, faces)

        key = mesh.insert_vertex(0)
        fixed = [key]

        plotter = MeshPlotter(mesh, figsize=(10, 7))

        plotter.draw_edges(width=0.5)

        def callback(mesh, k, args):
            print(k)
            plotter.update_edges()
            plotter.update()

        trimesh_remesh(mesh,
                       1.0,
                       kmax=200,
                       allow_boundary_split=True,
                       allow_boundary_swap=True,
                       allow_boundary_collapse=False,
                       fixed=fixed,
                       callback=callback)

        mesh_smooth_area(mesh, fixed=mesh.vertices_on_boundary())

        plotter.update_edges()
        plotter.update(pause=2.0)
        plotter.show()
Example #2
0
def trimesh_remesh(mesh,
                   target,
                   kmax=100,
                   tol=0.1,
                   divergence=0.01,
                   verbose=False,
                   allow_boundary_split=False,
                   allow_boundary_swap=False,
                   allow_boundary_collapse=False,
                   smooth=True,
                   fixed=None,
                   callback=None,
                   callback_args=None):
    """Remesh until all edges have a specified target length.

    Parameters
    ----------
    mesh : Mesh
        A triangle mesh.
    target : float
        The target length for the mesh edges.
    kmax : int, optional [100]
        The number of iterations.
    tol : float, optional [0.1]
        Length deviation tolerance.
    divergence : float, optional [0.01]
        ??
    verbose : bool, optional [False]
        Print feedback messages.
    allow_boundary_split : bool, optional [False]
        Allow boundary edges to be split.
    allow_boundary_swap : bool, optional [False]
        Allow boundary edges or edges connected to the boundary to be swapped.
    allow_boundary_collapse : bool, optional [False]
        Allow boundary edges or edges connected to the boundary to be collapsed.
    smooth : bool, optional [True]
        Apply smoothing at every iteration.
    fixed : list, optional [None]
        A list of vertices that have to stay fixed.
    callback : callable, optional [None]
        A user-defined function that is called after every iteration.
    callback_args : list, optional [None]
        A list of additional parameters to be passed to the callback function.

    Returns
    -------
    None

    Notes
    -----
    This algorithm not only changes the geometry of the mesh, but also its
    topology as needed to achieve the specified target lengths.
    Topological changes are made such that vertex valencies are well-balanced
    and close to six. This involves three operations:

        * split edges that are longer than a maximum length,
        * collapse edges that are shorter than a minimum length,
        * swap edges if this improves the valency error.

    The minimum and maximum lengths are calculated based on a desired target
    length.

    For more info, see [1]_.

    References
    ----------
    .. [1] Botsch, M. & Kobbelt, L., 2004. *A remeshing approach to multiresolution modeling*.
           Proceedings of the 2004 Eurographics/ACM SIGGRAPH symposium on Geometry processing - SGP '04, p.185.
           Available at: http://portal.acm.org/citation.cfm?doid=1057432.1057457.

    Warning
    -------
    In the current implementation, allowing boundary collapses may lead to unexpected
    results since it will not preserve the gometry of the original boundary.

    Examples
    --------
    .. plot::
        :include-source:

        from compas.datastructures import Mesh
        from compas.plotters import MeshPlotter
        from compas.topology import trimesh_remesh

        vertices = [
            (0.0, 0.0, 0.0),
            (10.0, 0.0, 0.0),
            (10.0, 10.0, 0.0),
            (0.0, 10.0, 0.0),
            (5.0, 5.0, 0.0)
        ]
        faces = [
            (0, 1, 4),
            (1, 2, 4),
            (2, 3, 4),
            (3, 0, 4)
        ]

        mesh = Mesh.from_vertices_and_faces(vertices, faces)

        trimesh_remesh(
            mesh,
            target=0.5,
            tol=0.05,
            kmax=300,
            allow_boundary_split=True,
            allow_boundary_swap=True,
            verbose=False
        )

        plotter = MeshPlotter(mesh)

        plotter.draw_vertices(radius=0.03)
        plotter.draw_faces()
        plotter.draw_edges()

        plotter.show()

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

    """
    if verbose:
        print(target)

    lmin = (1 - tol) * (4.0 / 5.0) * target
    lmax = (1 + tol) * (4.0 / 3.0) * target

    edge_lengths = [mesh.edge_length(u, v) for u, v in mesh.edges()]
    target_start = max(edge_lengths) / 2.0

    fac = target_start / target

    boundary = set(mesh.vertices_on_boundary())

    fixed = fixed or []
    fixed = set(fixed)

    count = 0

    kmax_start = kmax / 2.0

    for k in range(kmax):

        if k <= kmax_start:
            scale = fac * (1.0 - k / kmax_start)
            dlmin = lmin * scale
            dlmax = lmax * scale
        else:
            dlmin = 0
            dlmax = 0

        if verbose:
            print(k)

        count += 1

        if k % 20 == 0:
            num_vertices_1 = mesh.number_of_vertices()

        # split
        if count == 1:
            visited = set()

            for u, v in list(mesh.halfedges()):
                if u in visited or v in visited:
                    continue
                if mesh.edge_length(u, v) <= lmax + dlmax:
                    continue

                if verbose:
                    print('split edge: {0} - {1}'.format(u, v))

                mesh.split_edge_tri(u, v, allow_boundary=allow_boundary_split)

                visited.add(u)
                visited.add(v)

        # collapse
        elif count == 2:
            visited = set()

            for u, v in list(mesh.halfedges()):
                if u in visited or v in visited:
                    continue
                if mesh.edge_length(u, v) >= lmin - dlmin:
                    continue
                if verbose:
                    print('collapse edge: {0} - {1}'.format(u, v))

                mesh.collapse_edge_tri(u,
                                       v,
                                       allow_boundary=allow_boundary_collapse,
                                       fixed=fixed)

                visited.add(u)
                visited.add(v)

                visited.update(mesh.halfedge[u])

        # swap
        elif count == 3:
            visited = set()

            for u, v in list(mesh.halfedges()):
                if u in visited or v in visited:
                    continue

                f1 = mesh.halfedge[u][v]
                f2 = mesh.halfedge[v][u]

                if f1 is None or f2 is None:
                    continue

                face1 = mesh.face[f1]
                face2 = mesh.face[f2]

                v1 = face1[face1.index(u) - 1]
                v2 = face2[face2.index(v) - 1]

                valency1 = mesh.vertex_degree(u)
                valency2 = mesh.vertex_degree(v)
                valency3 = mesh.vertex_degree(v1)
                valency4 = mesh.vertex_degree(v2)

                if u in boundary:
                    valency1 += 2
                if v in boundary:
                    valency2 += 2
                if v1 in boundary:
                    valency3 += 2
                if v2 in boundary:
                    valency4 += 2

                current_error = abs(valency1 - 6) + abs(valency2 - 6) + abs(
                    valency3 - 6) + abs(valency4 - 6)
                flipped_error = abs(valency1 - 7) + abs(valency2 - 7) + abs(
                    valency3 - 5) + abs(valency4 - 5)

                if current_error <= flipped_error:
                    continue

                if verbose:
                    print('swap edge: {0} - {1}'.format(u, v))

                mesh.swap_edge_tri(u, v, allow_boundary=allow_boundary_swap)

                visited.add(u)
                visited.add(v)
        # count
        else:
            count = 0

        if (k - 10) % 20 == 0:
            num_vertices_2 = mesh.number_of_vertices()

            if abs(1 - num_vertices_1 /
                   num_vertices_2) < divergence and k > kmax_start:
                break

        # smoothen
        if smooth:
            if allow_boundary_split:
                boundary = set(mesh.vertices_on_boundary())

            mesh_smooth_area(mesh, fixed=fixed.union(boundary), kmax=1)

        # callback
        if callback:
            callback(mesh, k, callback_args)
    for key in mesh.vertices():
        if key in bound:
            pt = mesh.vertex_coordinates(key)
            pt_geo = geometric_key(pt, precision)
            # check if pt has same coordinates as any fixed point
            if pt_geo in pts_fixed_geo:
                mesh.set_vertex_attribute(key, 'fixed', True)
                fixed.append(key)
            else:
                # assign guid of closest curve to vertex
                mesh.set_vertex_attribute(
                    key, 'crv',
                    rs.PointClosestObject(pt, guid_crvs)[0])
        else:
            # assign surface guid to vertex
            mesh.set_vertex_attribute(key, 'srf', srf)

    # initialize conduit
    conduit = MeshConduit(mesh)

    # run smoothing with conduit
    with conduit.enabled():
        mesh_smooth_area(mesh,
                         fixed=fixed,
                         kmax=200,
                         damping=0.5,
                         callback=callback)

    artist = MeshArtist(mesh, layer='relaxed_mesh_on_surface')
    artist.draw()
if __name__ == '__main__':

    # select rhino mesh
    guid = rs.GetObject("Select mesh", 32)
    # create compas mesh object from rhino mesh
    mesh = mesh_from_guid(Mesh, guid)

    # set vertices on boundary as fixed
    fixed = set(mesh.vertices_on_boundary())

    # run smoothing
    mesh_smooth_centroid(mesh,
                         fixed=fixed,
                         kmax=100,
                         damping=0.5,
                         callback=None,
                         callback_args=None)

    # draw mesh
    artist = MeshArtist(mesh, layer='relaxed_mesh_laplacian')
    artist.draw()

    mesh_smooth_area(mesh,
                     fixed=fixed,
                     kmax=100,
                     damping=0.5,
                     callback=None,
                     callback_args=None)
    artist = MeshArtist(mesh, layer='relaxed_mesh_area')
    artist.draw()
Example #5
0
import compas_tna

from compas.geometry import mesh_smooth_area

from compas_tna.diagrams import FormDiagram

# create a form diagram from a serialised file

form = FormDiagram.from_json('aag_01_formdiagram_from_mesh.json')

# smooth the diagram keeping some of the vertices fixed

fixed = list(form.vertices_where({'is_anchor': True}))
fixed += list(form.vertices_where({'is_fixed': True}))
fixed += [
    key for fkey in form.faces_where({'is_loaded': False})
    for key in form.face_vertices(fkey)
]

fixed[:] = list(set(fixed))

mesh_smooth_area(form, fixed=fixed, kmax=50)

# serialise the result

form.to_json('aag_02_formdiagram.json')

# draw the result in the layer AAG > FormDiagram

form.draw(layer='AAG::FormDiagram')
                "message": "Select (ESC to exit)",
                "default": "openings",
                "show": None,
                "ID": "interface_1"
            }

            flag = command_line_menu(interface_1)

            if flag == "openings":
                set_openings(mesh)
            elif flag == "triangle_corner":
                if not set_tri_corners(mesh):
                    print "no triangles in mesh"
            elif flag == "target_length":
                trg_len = rs.GetReal("Insert target length: ", trg_len)
                if not trg_len:
                    break
            else:
                break

    coons_mesh = meshes_join([coons_mesh],
                             cull_duplicates=True,
                             precision=precision)
    fixed = coons_mesh.vertices_on_boundary()

    mesh_smooth_area(coons_mesh, fixed=fixed, kmax=25, damping=0.5)
    #mesh_smooth_centroid(coons_mesh, fixed=fixed, kmax=25,damping=0.5)

    artist = MeshArtist(coons_mesh, layer='form_quad')
    artist.draw()
Example #7
0
 def smooth(self, fixed, kmax=10):
     mesh_smooth_area(self, fixed=fixed, kmax=kmax)