Exemple #1
0
def mesh_unweld_edges(mesh, edges):
    """Unwelds a mesh along edges.

    Parameters
    ----------
    mesh : Mesh
        A mesh.
    edges: list
        List of edges as tuples of vertex keys.

    """

    # set of vertices in edges to unweld
    vertices = set([i for edge in edges for i in edge])

    # to store changes to do all at once
    vertex_changes = {}

    for vkey in vertices:

        # maps between old mesh face index and new network vertex index
        old_to_new = {nbr: i for i, nbr in enumerate(mesh.vertex_faces(vkey))}
        new_to_old = {i: nbr for i, nbr in enumerate(mesh.vertex_faces(vkey))}

        # get adjacency network of faces around the vertex excluding adjacency
        # through the edges to unweld
        network_vertices = [
            mesh.face_centroid(fkey) for fkey in mesh.vertex_faces(vkey)
        ]
        network_edges = []
        for nbr in mesh.vertex_neighbors(vkey):
            if not mesh.is_edge_on_boundary(vkey, nbr) and (
                    vkey, nbr) not in edges and (nbr, vkey) not in edges:
                network_edges.append((old_to_new[mesh.halfedge[vkey][nbr]],
                                      old_to_new[mesh.halfedge[nbr][vkey]]))

        adjacency = adjacency_from_edges(network_edges)
        for key, values in adjacency.items():
            adjacency[key] = {value: None for value in values}
        # include non connected vertices
        edge_vertices = list(set([i for edge in network_edges for i in edge]))
        for i in range(len(mesh.vertex_faces(vkey))):
            if i not in edge_vertices:
                adjacency[i] = {}

        # collect the disconnected parts around the vertex due to unwelding
        vertex_changes[vkey] = [[new_to_old[key] for key in part]
                                for part in connected_components(adjacency)]

    for vkey, changes in vertex_changes.items():
        # for each disconnected part replace the vertex by a new vertex in the
        # faces of the part
        for change in changes:
            mesh_substitute_vertex_in_faces(
                mesh, vkey, mesh.add_vertex(attr_dict=mesh.vertex[vkey]),
                change)

        # delete old vertices
        mesh.delete_vertex(vkey)
def quad_mesh_strip_n_coloring(quad_mesh):
    """Color the strips of a quad mesh with a minimum number of colors without overlapping strips with the same color.

    Parameters
    ----------
    quad_mesh : QuadMesh
        A quad mesh.

    Returns
    -------
    dict
        A dictionary with strip keys pointing to colors.
    """

    vertices, edges = quad_mesh.strip_graph()
    return vertex_coloring(adjacency_from_edges(edges))
def quad_mesh_polyedge_n_coloring(quad_mesh):
    """Color the polyedges of a quad mesh with a minimum number of colors without overlapping polyedges with the same color.
    Polyedges connected by their extremities, which are singularities, do not count as overlapping.

    Parameters
    ----------
    quad_mesh : QuadMesh
        A quad mesh.

    Returns
    -------
    dict
        A dictionary with polyedge keys pointing to colors.
    """

    vertices, edges = quad_mesh.polyedge_graph()
    return vertex_coloring(adjacency_from_edges(edges))
def quad_mesh_strip_2_coloring(quad_mesh):
    """Try to color the strips of a quad mesh with two colors only without overlapping strips with the same color.

    Parameters
    ----------
    quad_mesh : QuadMesh
        A quad mesh.

    Returns
    -------
    dict, None
        A dictionary with strip keys pointing to colors, if two-colorable.
        None if not two-colorable.
    """

    vertices, edges = quad_mesh.strip_graph()
    return is_adjacency_two_colorable(adjacency_from_edges(edges))
def quad_mesh_polyedge_2_coloring(quad_mesh):
    """Try to color the polyedges of a quad mesh with two colors only without overlapping polyedges with the same color.
    Polyedges connected by their extremities, which are singularities, do not count as overlapping.

    Parameters
    ----------
    quad_mesh : QuadMesh
        A quad mesh.

    Returns
    -------
    dict, None
        A dictionary with polyedge keys pointing to colors, if two-colorable.
        None if not two-colorable.
    """

    vertices, edges = quad_mesh.polyedge_graph()
    return is_adjacency_two_colorable(adjacency_from_edges(edges))
Exemple #6
0
def mesh_face_n_coloring(mesh):
    """Color the faces of a mesh with a minimum number of colors without adjacent faces with the same color.

	Parameters
	----------
	mesh : Mesh
		A mesh.

	Returns
	-------
	dict
		A dictionary with face keys pointing to colors.
		
	"""

    edges = [(mesh.halfedge[u][v], mesh.halfedge[v][u])
             for u, v in mesh.edges() if not mesh.is_edge_on_boundary(u, v)]
    return vertex_coloring(adjacency_from_edges(edges))
Exemple #7
0
def mesh_face_2_coloring(mesh):
    """Try to color the faces of a mesh with two colors only without adjacent faces with the same color.

	Parameters
	----------
	mesh : Mesh
		A mesh.

	Returns
	-------
	dict, None
		A dictionary with face keys pointing to colors, if two-colorable.
		None if not two-colorable.

	"""

    edges = [(mesh.halfedge[u][v], mesh.halfedge[v][u])
             for u, v in mesh.edges() if not mesh.is_edge_on_boundary(u, v)]
    return is_adjacency_two_colorable(adjacency_from_edges(edges))
Exemple #8
0
def dr_python(vertices,
              edges,
              fixed,
              loads,
              qpre,
              fpre,
              lpre,
              linit,
              E,
              radius,
              kmax=100,
              dt=1.0,
              tol1=1e-3,
              tol2=1e-6,
              c=0.1,
              callback=None,
              callback_args=None):
    """Implementation of dynamic relaxation with RK integration scheme in pure Python.

    Parameters
    ----------
    vertices : list
        XYZ coordinates of the vertices.
    edges : list
        Connectivity of the vertices.
    fixed : list
        Indices of the fixed vertices.
    loads : list
        XYZ components of the loads on the vertices.
    qpre : list
        Prescribed force densities in the edges.
    fpre : list
        Prescribed forces in the edges.
    lpre : list
        Prescribed lengths of the edges.
    linit : list
        Initial length of the edges.
    E : list
        Stiffness of the edges.
    radius : list
        Radius of the edges.
    kmax : int, optional
        Maximum number of iterations.
    dt : float, optional
        The time step.
    tol1 : float, optional
        Convergence criterion for the residual forces.
    tol2 : float, optional
        Convergence criterion for the displacements in between interations.
    c : float, optional
        Damping factor for viscous damping.
    callback : callable, optional
        A user-defined callback that is called after every iteration.
    callback_args : tuple, optional
        Additional arguments to be passed to the callback.

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

        import random

        import compas
        from compas.datastructures import Network
        from compas.plotters import NetworkPlotter
        from compas.utilities import i_to_rgb
        from compas.numerical import dr

        # make a network
        # and set the default vertex and edge attributes

        network = Network.from_obj(compas.get('lines.obj'))

        dva = {
            'is_fixed': False,
            'px': 0.0,
            'py': 0.0,
            'pz': 0.0,
            'rx': 0.0,
            'ry': 0.0,
            'rz': 0.0,
        }

        dea = {
            'qpre': 1.0,
            'fpre': 0.0,
            'lpre': 0.0,
            'linit': 0.0,
            'E': 0.0,
            'radius': 0.0,
        }

        network.update_default_vertex_attributes(dva)
        network.update_default_edge_attributes(dea)

        # identify the fixed vertices
        # and assign random prescribed force densities to the edges

        for key, attr in network.vertices(True):
            attr['is_fixed'] = network.vertex_degree(key) == 1

        for u, v, attr in network.edges(True):
            attr['qpre'] = 1.0 * random.randint(1, 7)

        # extract numerical data from the datastructure

        vertices = network.get_vertices_attributes(('x', 'y', 'z'))
        edges    = list(network.edges())
        fixed    = network.vertices_where({'is_fixed': True})
        loads    = network.get_vertices_attributes(('px', 'py', 'pz'))
        qpre     = network.get_edges_attribute('qpre')
        fpre     = network.get_edges_attribute('fpre')
        lpre     = network.get_edges_attribute('lpre')
        linit    = network.get_edges_attribute('linit')
        E        = network.get_edges_attribute('E')
        radius   = network.get_edges_attribute('radius')

        # make a plotter for (dynamic) visualization
        # plot the lines of the original configuration of the network as reference

        plotter = NetworkPlotter(network)

        lines = []
        for u, v in network.edges():
            lines.append({
                'start': network.vertex_coordinates(u, 'xy'),
                'end'  : network.vertex_coordinates(v, 'xy'),
                'color': '#cccccc',
                'width': 0.5
            })

        plotter.draw_lines(lines)

        # run the dynamic relaxation
        # update vertices and edges
        # visualize the final geometry
        # color the edges according to the size of the forces
        # set the width of the edges proportional to the internal forces

        xyz, q, f, l, r = dr(vertices, edges, fixed, loads, qpre, fpre, lpre, linit, E, radius,
                             kmax=100)

        for key, attr in network.vertices(True):
            attr['x'] = xyz[key][0]
            attr['y'] = xyz[key][1]
            attr['z'] = xyz[key][2]

        for index, (u, v, attr) in enumerate(network.edges(True)):
            attr['f'] = f[index]
            attr['l'] = l[index]

        fmax = max(network.get_edges_attribute('f'))

        plotter.clear_vertices()
        plotter.clear_edges()

        plotter.draw_vertices(
            facecolor={key: '#000000' for key in network.vertices_where({'is_fixed': True})}
        )

        plotter.draw_edges(
            text={(u, v): '{:.0f}'.format(attr['f']) for u, v, attr in network.edges(True)},
            color={(u, v): i_to_rgb(attr['f'] / fmax) for u, v, attr in network.edges(True)},
            width={(u, v): 10 * attr['f'] / fmax for u, v, attr in network.edges(True)}
        )

        plotter.show()


    See Also
    --------
    * :func:`compas.numerical.dr_numpy`
    * :func:`compas.numerical.drx_numpy`

    """
    if callback:
        if not callable(callback):
            raise Exception('The callback is not callable.')
    # --------------------------------------------------------------------------
    # preprocess
    # --------------------------------------------------------------------------
    n = len(vertices)

    # i_nbrs = {i: [ij[1] if ij[0] == i else ij[0] for ij in edges if i in ij] for i in range(n)}

    i_nbrs = adjacency_from_edges(edges)

    ij_e = {(i, j): index for index, (i, j) in enumerate(edges)}
    ij_e.update({(j, i): index for (i, j), index in ij_e.items()})

    coeff = Coeff(c)
    ca = coeff.a
    cb = coeff.b
    free = list(set(range(n)) - set(fixed))
    # --------------------------------------------------------------------------
    # attribute arrays
    # --------------------------------------------------------------------------
    X = vertices
    P = loads
    Q = qpre
    # --------------------------------------------------------------------------
    # initial values
    # --------------------------------------------------------------------------
    M = [
        sum(0.5 * dt**2 * Q[ij_e[(i, j)]] for j in i_nbrs[i]) for i in range(n)
    ]
    V = [[0.0, 0.0, 0.0] for _ in range(n)]
    R = [[0.0, 0.0, 0.0] for _ in range(n)]
    dX = [[0.0, 0.0, 0.0] for _ in range(n)]

    # --------------------------------------------------------------------------
    # helpers
    # --------------------------------------------------------------------------

    def update_R():
        for i in range(n):
            x = X[i][0]
            y = X[i][1]
            z = X[i][2]
            f = [0.0, 0.0, 0.0]
            for j in i_nbrs[i]:
                q = Q[ij_e[(i, j)]]
                f[0] += q * (X[j][0] - x)
                f[1] += q * (X[j][1] - y)
                f[2] += q * (X[j][2] - z)
            R[i] = [P[i][axis] + f[axis] for axis in (0, 1, 2)]

    def rk(X0, V0, steps=2):
        def a(t, V):
            dX = [[V[i][axis] * t for axis in (0, 1, 2)] for i in range(n)]
            for i in free:
                X[i] = [X0[i][axis] + dX[i][axis] for axis in (0, 1, 2)]
            update_R()
            return [[cb * R[i][axis] / M[i] for axis in (0, 1, 2)]
                    for i in range(n)]

        if steps == 2:
            B = [0.0, 1.0]
            a0 = a(K[0][0] * dt, V0)
            k0 = [[dt * a0[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            a1 = a(
                K[1][0] * dt,
                [[V0[i][axis] + K[1][1] * k0[i][axis] for axis in (0, 1, 2)]
                 for i in range(n)])
            k1 = [[dt * a1[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            return [[
                B[0] * k0[i][axis] + B[1] * k1[i][axis] for axis in (0, 1, 2)
            ] for i in range(n)]

        if steps == 4:
            B = [1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0]
            a0 = a(K[0][0] * dt, V0)
            k0 = [[dt * a0[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            a1 = a(
                K[1][0] * dt,
                [[V0[i][axis] + K[1][1] * k0[i][axis] for axis in (0, 1, 2)]
                 for i in range(n)])
            k1 = [[dt * a1[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            a2 = a(K[2][0] * dt, [[
                V0[i][axis] + K[2][1] * k0[i][axis] + K[2][2] * k1[i][axis]
                for axis in (0, 1, 2)
            ] for i in range(n)])
            k2 = [[dt * a2[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            a3 = a(K[3][0] * dt, [[
                V0[i][axis] + K[3][1] * k0[i][axis] + K[3][2] * k1[i][axis] +
                K[3][3] * k2[i][axis] for axis in (0, 1, 2)
            ] for i in range(n)])
            k3 = [[dt * a3[i][axis] for axis in (0, 1, 2)] for i in range(n)]
            return [[
                B[0] * k0[i][axis] + B[1] * k1[i][axis] + B[2] * k2[i][axis] +
                B[3] * k3[i][axis] for axis in (0, 1, 2)
            ] for i in range(n)]

        raise NotImplementedError

    # --------------------------------------------------------------------------
    # start iterating
    # --------------------------------------------------------------------------
    for k in range(kmax):
        X0 = deepcopy(X)
        V0 = [[ca * V[i][axis] for axis in (0, 1, 2)] for i in range(n)]

        # RK
        dV = rk(X0, V0, steps=4)

        # update
        for i in free:
            V[i] = [V0[i][axis] + dV[i][axis] for axis in (0, 1, 2)]
            dX[i] = [V[i][axis] * dt for axis in (0, 1, 2)]
            X[i] = [X0[i][axis] + dX[i][axis] for axis in (0, 1, 2)]

        L = [
            sum((X[i][axis] - X[j][axis])**2 for axis in (0, 1, 2))**0.5
            for i, j in iter(edges)
        ]
        F = [q * l for q, l in zip(Q, L)]

        update_R()

        # crits
        crit1 = max(norm_vectors([R[i] for i in free]))
        crit2 = max(norm_vectors([dX[i] for i in free]))

        # callback
        if callback:
            callback(k, X, (crit1, crit2), callback_args)

        # convergence
        if crit1 < tol1:
            break
        if crit2 < tol2:
            break
    # --------------------------------------------------------------------------
    # update
    # --------------------------------------------------------------------------
    update_R()

    return X, Q, F, L, R
    def projection_0(self, kmax=1):
        """Projection of a coarse quad mesh to the closest two-colourable sub-spaces.

		Parameters
		----------
		mesh : CoarseQuadMesh
			A coarse quad mesh.

		Returns
		-------
		results : dict
			The combination pointing to the its result. If the combination is valid, the result is a tuple of the the two-colourable mesh, the two-colourable network, and the network vertex colors.

		References
		----------
		.. [1] Oval et al., *Topology Finding of Two-Colourable Quad-Mesh Patterns in Structural Design*. Submitted.

		"""

        mesh = self.quad_mesh

        # result for input mesh
        vertices, edges = mesh.strip_graph()
        if is_adjacency_two_colorable(adjacency_from_edges(edges)) is not None:
            self.results = True
            return True

        results = {}

        # guarantee valid kmax
        n = mesh.number_of_strips()
        if kmax < 1 or kmax > n:
            kmax = n

        t0 = time.time()
        t1 = -float('inf')
        t2 = -float('inf')
        total_valid = 0
        # start iteration
        k = 0
        discarding_combination = []
        discarding_combination_type = {}
        while k < kmax:
            k += 1
            to_continue = False
            at_least_one_valid_k = False
            # test all combinations of (n k) strips
            for combination in itertools.combinations(mesh.strips(), k):
                set_combi = set(combination)
                # check results from potential previous sub-combinations
                for disc_comb in discarding_combination:
                    if disc_comb.issubset(set_combi):
                        #if are_items_in_list(previous_combination, combination):
                        # if a sub-combination yielded an invalid topology do not pursue
                        if discarding_combination_type[tuple(
                                disc_comb)] == 'invalid shape topology':
                            results[
                                combination] = 'already invalid shape topology'
                            break
                        elif discarding_combination_type[tuple(
                                disc_comb)] == 'two-colourable':
                            results[combination] = 'already two-colourable'
                            break

                if len(collateral_strip_deletions(mesh, combination)) > 0:
                    results[combination] = 'collateral deletions'

                if len(total_boundary_deletions(mesh, combination)) > 0:
                    results[combination] = 'invalid shape topology'
                    discarding_combination.append(set(combination))
                    discarding_combination_type[tuple(
                        combination)] = 'invalid shape topology'

                if combination in results:
                    continue

                # delete strips in mesh and check validity
                copy_mesh = mesh.copy()
                #copy_mesh.collect_strips()
                delete_strips(copy_mesh, combination, preserve_boundaries=True)
                topological_validity = copy_mesh.is_manifold(
                ) and copy_mesh.euler() == mesh.euler()
                if not topological_validity:
                    results[combination] = 'invalid shape topology'
                    discarding_combination.append(set(combination))
                    discarding_combination_type[tuple(
                        combination)] = 'invalid shape topology'

                # delete strip vertices in network and check colourability
                else:
                    #vertices, edges = copy_mesh.strip_graph()
                    new_vertices = {
                        vkey: xyz
                        for vkey, xyz in vertices.items()
                        if vkey not in combination
                    }
                    new_edges = [
                        (u, v) for u, v in edges
                        if u not in combination and v not in combination
                    ]
                    two_colourability = is_adjacency_two_colorable(
                        adjacency_from_edges(new_edges))
                    if not two_colourability:
                        results[combination] = 'not two-colourable'
                        to_continue = True
                    else:
                        results[combination] = (copy_mesh, (new_vertices,
                                                            new_edges),
                                                two_colourability)
                        discarding_combination.append(set(combination))
                        discarding_combination_type[tuple(
                            combination)] = 'two-colourable'
                        at_least_one_valid_k = True
                        total_valid += 1
                        if t1 < 0:
                            t1 = time.time()

            if t2 < 0 and total_valid > 0 and not at_least_one_valid_k:
                t2 = time.time()

            if not to_continue:
                break

        t3 = time.time()
        print(len(discarding_combination))
        print(t3 - t0)
        self.results = results
        self.times = (t1 - t0, t2 - t0, t3 - t0)
    def projection(self, kmax=1):
        """Projection of a coarse quad mesh to the closest two-colourable sub-spaces.

		Parameters
		----------
		mesh : CoarseQuadMesh
			A coarse quad mesh.

		Returns
		-------
		results : dict
			The combination pointing to the its result. If the combination is valid, the result is a tuple of the the two-colourable mesh, the two-colourable network, and the network vertex colors.

		References
		----------
		.. [1] Oval et al., *Topology Finding of Two-Colourable Quad-Mesh Patterns in Structural Design*. Submitted.

		"""

        mesh = self.quad_mesh

        # result for input mesh
        vertices, edges = mesh.strip_graph()
        if is_adjacency_two_colorable(adjacency_from_edges(edges)) is not None:
            self.results = True
            return True

        results = {}

        # guarantee valid kmax
        n = mesh.number_of_strips()
        if kmax < 1 or kmax > n:
            kmax = n

        t0 = time.time()

        # start iteration
        k = 0
        while k < kmax:
            k += 1
            to_continue = False
            # test all combinations of (n k) strips
            for combination in itertools.combinations(mesh.strips(), k):
                # check results from potential previous sub-combinations
                for previous_combination in results:
                    if are_items_in_list(previous_combination, combination):
                        # if a sub-combination yielded an invalid topology do not pursue
                        if results[
                                previous_combination] == 'invalid shape topology':
                            results[
                                combination] = 'already invalid shape topology'
                            break
                        elif type(results[previous_combination]) == tuple:
                            results[combination] = 'already two-colourable'
                            break
                if combination in results:
                    continue

                if len(collateral_strip_deletions(mesh, combination)) > 0:
                    to_continue = True
                    continue

                if len(total_boundary_deletions(mesh, combination)) > 0:
                    results[combination] = 'invalid shape topology'
                    continue

                # delete strips in mesh and check validity
                copy_mesh = mesh.copy()
                copy_mesh.collect_strips()
                delete_strips(copy_mesh, combination, preserve_boundaries=True)
                topological_validity = copy_mesh.is_manifold(
                ) and copy_mesh.euler() == mesh.euler()
                if not topological_validity:
                    results[combination] = 'invalid shape topology'

                # delete strip vertices in network and check colourability
                else:
                    vertices, edges = copy_mesh.strip_graph()
                    two_colourability = is_adjacency_two_colorable(
                        adjacency_from_edges(edges))
                    if not two_colourability:
                        results[combination] = 'not two-colourable'
                        to_continue = True
                    else:
                        results[combination] = (copy_mesh, (vertices, edges),
                                                two_colourability)

            if not to_continue:
                break

        t1 = time.time()

        self.results = results
        self.time = t1 - t0
    def projection_4(self, kmax=1):
        """Projection of a coarse quad mesh to the closest two-colourable sub-spaces.

		Parameters
		----------
		mesh : CoarseQuadMesh
			A coarse quad mesh.

		Returns
		-------
		results : dict
			The combination pointing to the its result. If the combination is valid, the result is a tuple of the the two-colourable mesh, the two-colourable network, and the network vertex colors.

		References
		----------
		.. [1] Oval et al., *Topology Finding of Two-Colourable Quad-Mesh Patterns in Structural Design*. Submitted.

		"""

        mesh = self.quad_mesh
        vertices, edges = mesh.strip_graph()
        if is_adjacency_two_colorable(adjacency_from_edges(edges)) is not None:
            self.results = True
            return True

        results = {}

        t0 = time.time()
        k = 0

        current_pool = [[skey] for skey in mesh.strips()]

        while k < kmax:
            k += 1
            next_pool = []
            #print(current_pool)
            for combination in current_pool:
                if len(combination) > 1:
                    combination = list(
                        set([i for item in combination for i in item]))
                else:
                    combination = list(set(combination))

                if len(collateral_strip_deletions(mesh, combination)) > 0:
                    continue
                if len(total_boundary_deletions(mesh, combination)) > 0:
                    continue

                # delete strips in mesh and check validity
                copy_mesh = mesh.copy()
                delete_strips(copy_mesh, combination, preserve_boundaries=True)
                topological_validity = copy_mesh.is_manifold(
                ) and copy_mesh.euler() == mesh.euler()
                if not topological_validity:
                    pass

                # delete strip vertices in network and check colourability
                else:
                    new_vertices = {
                        vkey: xyz
                        for vkey, xyz in vertices.items()
                        if vkey not in combination
                    }
                    new_edges = [
                        (u, v) for u, v in edges
                        if u not in combination and v not in combination
                    ]
                    two_colourability = is_adjacency_two_colorable(
                        adjacency_from_edges(new_edges))
                    if not two_colourability:
                        next_pool.append(combination)
                    else:
                        results[tuple(combination)] = (copy_mesh,
                                                       (new_vertices,
                                                        new_edges),
                                                       two_colourability)

            current_pool = itertools.combinations(next_pool, 2)

        t1 = time.time()
        print(t1 - t0)

        self.results = results
    def projection_2(self, kmax=1):
        """Projection of a coarse quad mesh to the closest two-colourable sub-spaces.

		Parameters
		----------
		mesh : CoarseQuadMesh
			A coarse quad mesh.

		Returns
		-------
		results : dict
			The combination pointing to the its result. If the combination is valid, the result is a tuple of the the two-colourable mesh, the two-colourable network, and the network vertex colors.

		References
		----------
		.. [1] Oval et al., *Topology Finding of Two-Colourable Quad-Mesh Patterns in Structural Design*. Submitted.

		"""

        mesh = self.quad_mesh

        # result for input mesh
        vertices, edges = mesh.strip_graph()
        if is_adjacency_two_colorable(adjacency_from_edges(edges)) is not None:
            self.results = True
            return True

        results = {}

        # guarantee valid kmax
        n = mesh.number_of_strips()
        if kmax < 1 or kmax > n:
            kmax = n

        t0 = time.time()

        # start iteration
        k = 0
        discarding_combination = []
        while k < kmax:
            k += 1
            to_continue = False
            at_least_one_valid_k = False
            # test all combinations of (n k) strips
            for combination in itertools.combinations(mesh.strips(), k):
                set_combi = set(combination)
                # check results from potential previous sub-combinations
                for disc_comb in discarding_combination:
                    if disc_comb.issubset(set_combi):
                        break

                if len(collateral_strip_deletions(mesh, combination)) > 0:
                    to_continue = True
                    continue

                if len(total_boundary_deletions(mesh, combination)) > 0:
                    discarding_combination.append(set(combination))
                    continue

                # delete strips in mesh and check validity
                copy_mesh = mesh.copy()
                delete_strips(copy_mesh, combination, preserve_boundaries=True)
                topological_validity = copy_mesh.is_manifold(
                ) and copy_mesh.euler() == mesh.euler()
                if not topological_validity:
                    discarding_combination.append(set(combination))

                # delete strip vertices in network and check colourability
                else:
                    new_vertices = {
                        vkey: xyz
                        for vkey, xyz in vertices.items()
                        if vkey not in combination
                    }
                    new_edges = [
                        (u, v) for u, v in edges
                        if u not in combination and v not in combination
                    ]
                    two_colourability = is_adjacency_two_colorable(
                        adjacency_from_edges(new_edges))
                    if not two_colourability:
                        to_continue = True
                    else:
                        results[combination] = (copy_mesh, (new_vertices,
                                                            new_edges),
                                                two_colourability)
                        discarding_combination.append(set(combination))

            if not to_continue:
                break

        t1 = time.time()
        print(t1 - t0)

        #print(results)
        self.results = results
Exemple #13
0
    def from_quad_mesh(cls,
                       quad_mesh,
                       collect_strips=True,
                       collect_polyedges=True,
                       attribute_density=True):
        """Build coarse quad mesh from quad mesh with density and child-parent element data.

		Parameters
		----------
		quad_mesh : QuadMesh
			A quad mesh.
		attribute_density : bool, optional
			Keep density data of dense quad mesh and inherit it as aatribute.

		Returns
		----------
		coarse_quad_mesh : CoarseQuadMesh
			A coarse quad mesh with density data.
		"""

        polyedges = quad_mesh.singularity_polyedge_decomposition()

        # vertex data
        vertices = {
            vkey: quad_mesh.vertex_coordinates(vkey)
            for vkey in quad_mesh.vertices()
        }
        coarse_vertices_children = {
            vkey: vkey
            for polyedge in polyedges for vkey in [polyedge[0], polyedge[-1]]
        }
        coarse_vertices = {
            vkey: quad_mesh.vertex_coordinates(vkey)
            for vkey in coarse_vertices_children
        }

        # edge data
        coarse_edges_children = {(polyedge[0], polyedge[-1]): polyedge
                                 for polyedge in polyedges}
        singularity_edges = [(x, y) for polyedge in polyedges
                             for u, v in pairwise(polyedge)
                             for x, y in [(u, v), (v, u)]]

        # face data
        faces = {
            fkey: quad_mesh.face_vertices(fkey)
            for fkey in quad_mesh.faces()
        }
        adj_edges = {(f1, f2)
                     for f1 in quad_mesh.faces()
                     for f2 in quad_mesh.face_neighbors(f1)
                     if f1 < f2 and quad_mesh.face_adjacency_halfedge(f1, f2)
                     not in singularity_edges}
        coarse_faces_children = {}
        for i, connected_faces in enumerate(
                connected_components(adjacency_from_edges(adj_edges))):
            mesh = Mesh.from_vertices_and_faces(
                vertices, [faces[face] for face in connected_faces])
            coarse_faces_children[i] = [
                vkey for vkey in reversed(mesh.boundaries()[0])
                if mesh.vertex_degree(vkey) == 2
            ]

        coarse_quad_mesh = cls.from_vertices_and_faces(coarse_vertices,
                                                       coarse_faces_children)

        # attribute relation child-parent element between coarse and dense quad meshes
        coarse_quad_mesh.data['attributes'][
            'vertex_coarse_to_dense'] = coarse_vertices_children
        coarse_quad_mesh.data['attributes']['edge_coarse_to_dense'] = {
            u: {}
            for u in coarse_quad_mesh.vertices()
        }
        for (u, v), polyedge in coarse_edges_children.items():
            coarse_quad_mesh.data['attributes']['edge_coarse_to_dense'][u][
                v] = polyedge
            coarse_quad_mesh.data['attributes']['edge_coarse_to_dense'][v][
                u] = list(reversed(polyedge))

        # collect strip and polyedge attributes
        if collect_strips:
            coarse_quad_mesh.collect_strips()
        if collect_polyedges:
            coarse_quad_mesh.collect_polyedges()

        # store density attribute from input dense quad mesh
        if attribute_density:
            coarse_quad_mesh.set_strips_density(1)
            for skey in coarse_quad_mesh.strips():
                u, v = coarse_quad_mesh.strip_edges(skey)[0]
                d = len(
                    coarse_edges_children.get((u, v),
                                              coarse_edges_children.get((v, u),
                                                                        [])))
                coarse_quad_mesh.set_strip_density(skey, d)

        # store quad mesh and use as polygonal mesh
        coarse_quad_mesh.set_quad_mesh(quad_mesh)
        coarse_quad_mesh.set_polygonal_mesh(quad_mesh.copy())

        return coarse_quad_mesh