コード例 #1
0
def gmap_edge_split_optimization(gmap, maximal_length=1.0):
    """
    Perform one iteration of edge split optimization:
    Rank the GMap edges by length and iterativelty split 
    those whose length exceeds maximal_length
    """

    vertex_positions = array_dict([gmap.get_position(v) for v in gmap.darts()],
                                  gmap.darts())
    vertex_valence = array_dict(
        np.array(map(len, [gmap.orbit(v, [1, 2]) for v in gmap.darts()])) / 2,
        gmap.darts())
    edge_vertices = np.array([(e, gmap.alpha(0, e)) for e in gmap.elements(1)])
    edge_lengths = array_dict(
        np.linalg.norm(vertex_positions.values(edge_vertices[:, 1]) -
                       vertex_positions.values(edge_vertices[:, 0]),
                       axis=1),
        keys=gmap.elements(1))

    sorted_edge_length_edges = np.array(
        gmap.elements(1))[np.argsort(-edge_lengths.values(gmap.elements(1)))]
    sorted_edge_length_edges = sorted_edge_length_edges[
        edge_lengths.values(sorted_edge_length_edges) > maximal_length]

    n_splits = 0
    print "--> Splitting edges"
    for e in sorted_edge_length_edges:
        triangular_gmap_split_edge(gmap, e)
        n_splits += 1
    print "<-- Splitting edges (", n_splits, " edges split)"

    return n_splits
コード例 #2
0
 def update_wisp_property(self,
                          property_name,
                          degree,
                          values,
                          keys=None,
                          erase_property=True):
     """todo"""
     if not (isinstance(values, np.ndarray) or isinstance(values, dict)
             or isinstance(values, array_dict)):
         raise TypeError(
             "Values are not in an acceptable type (array, dict, array_dict)"
         )
     if property_name not in self._wisp_properties[degree]:
         print PropertyError("property " + property_name +
                             " is undefined on elements of degree " +
                             str(degree))
         print "Creating property ", property_name, " for degree ", degree
         self._wisp_properties[degree][property_name] = array_dict()
     if isinstance(values, np.ndarray):
         if keys is None:
             keys = np.array(list(self.wisps(degree)))
         #self._wisp_properties[degree][property_name].update(values,keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = array_dict(
             values, keys)
     elif isinstance(values, dict):
         if keys is None:
             keys = np.array(values.keys())
         #self._wisp_properties[degree][property_name].update(np.array(values.values()),keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = array_dict(values)
     elif isinstance(values, array_dict):
         if keys is None:
             keys = values.keys()
         #self._wisp_properties[degree][property_name].update(values.values(),keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = values
コード例 #3
0
 def update_wisp_property(self, property_name, degree, values, keys=None,
                          erase_property=True):
     """todo"""
     if not (isinstance(values, np.ndarray) or isinstance(values,
                                                          dict) or isinstance(
             values, array_dict)):
         raise TypeError(
             "Values are not in an acceptable type (array, dict, array_dict)")
     if property_name not in self._wisp_properties[degree]:
         print PropertyError(
             "property " + property_name + " is undefined on elements of degree " + str(
                 degree))
         print "Creating property ", property_name, " for degree ", degree
         self._wisp_properties[degree][property_name] = array_dict()
     if isinstance(values, np.ndarray):
         if keys is None:
             keys = np.array(list(self.wisps(degree)))
         # self._wisp_properties[degree][property_name].update(values,keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = array_dict(values,
                                                                   keys)
     elif isinstance(values, dict):
         if keys is None:
             keys = np.array(values.keys())
         # self._wisp_properties[degree][property_name].update(np.array(values.values()),keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = array_dict(values)
     elif isinstance(values, array_dict):
         if keys is None:
             keys = values.keys()
         # self._wisp_properties[degree][property_name].update(values.values(),keys=keys,ignore_missing_keys=False,erase_missing_keys=erase_property)
         self._wisp_properties[degree][property_name] = values
コード例 #4
0
 def add_wisp_property(self,property_name,degree,values = None):
     """todo"""
     if property_name in self._wisp_properties[degree]:
         raise PropertyError("property "+property_name+" is already defined on wisps of degree "+str(degree))
     if values is None: values = array_dict()  
     elif isinstance(values,np.ndarray):
         keys = np.array(list(self.wisps(degree)))
         values = array_dict(values,keys) 
     elif isinstance(values,dict):
         values = array_dict(values)      
     if not isinstance(values, array_dict):
         raise TypeError("Values are not in an acceptable type (array, dict, array_dict)")  
     self._wisp_properties[degree][property_name] = values
コード例 #5
0
def gmap_triangular_quality(gmap):
    n_triangles = len(gmap.elements(2))
    positions = array_dict([gmap.get_position(v) for v in gmap.elements(0)],
                           keys=gmap.elements(0))
    triangles = np.array([[
        gmap.get_embedding_dart(v, positions, 0)
        for v in gmap.incident_cells(t, 2, 0)
    ] for t in gmap.elements(2)])

    if (triangles.ndim != 2) or (triangles.shape[1] != 3):
        face_valences = np.array(map(len, triangles))
        print dict(
            zip(
                np.unique(face_valences),
                nd.sum(np.ones_like(face_valences), face_valences,
                       np.unique(face_valences))))
        print "Non triangular GMap! Impossible to compute triangle quality!"

    triangle_eccentricities = triangle_geometric_features(
        triangles, positions, features=['sinus_eccentricity'])[:, 0]
    vertex_valences = np.array(
        map(len, [gmap.incident_cells(v, 0, 1) for v in gmap.elements(0)]))

    quality = {}
    quality['eccentricity'] = np.mean(triangle_eccentricities)
    quality['valence'] = np.mean(vertex_valences)

    print n_triangles, "Triangles  [ eccentricity :", np.round(
        quality['eccentricity'],
        4), ", valence :", np.round(quality['valence'], 4), "]"

    return quality
コード例 #6
0
 def add_interface_property(self, property_name, degree, values=None):
     """todo"""
     if property_name in self._interface_properties[degree]:
         raise PropertyError(
             "property " + property_name +
             " is already defined on interfaces of degree " + str(degree))
     if values is None: values = array_dict()
     elif isinstance(values, np.ndarray):
         keys = np.array(self._interface.keys())
         values = array_dict(values, keys)
     elif isinstance(values, dict):
         values = array_dict(values)
     if not isinstance(values, array_dict):
         raise TypeError(
             "Values are not in an acceptable type (array, dict, array_dict)"
         )
     self._interface_properties[degree][property_name] = values
コード例 #7
0
 def wisp_property(self,property_name,degree,wids = None):
     """todo"""
     try:
         if wids is not None:
             return array_dict(self._wisp_properties[degree][property_name].values(wids),wids)
         else:
             return self._wisp_properties[degree][property_name]
     except KeyError:
         raise PropertyError("property "+property_name+" is undefined on wisps of degree "+str(degree))
コード例 #8
0
def remeshing(filename):
    # Load a PLY file into a GMap
    # Display it
    # Compute the maximal length for an edge
    # While more than 10% of edges have been split or flipped :
    # Perform a split optimization (returns n_splits)
    # Display the GMap
    # Perform a flip optimization (returns n_flips)
    # Display the GMap
    # Perform n iterations of Taubin smoothing
    # Display the GMap
    maximal_length = 1.0
    points, triangles = read_ply_mesh(filename)
    gmap = gmap_from_triangular_mesh(points, triangles, center=True)
    gmap.display()
    # Compute the maximal length for an edge

    vertex_positions = array_dict([gmap.get_position(v) for v in gmap.darts()],
                                  gmap.darts())
    edge_vertices = np.array([(e, gmap.alpha(0, e)) for e in gmap.elements(1)])
    edge_lengths = array_dict(
        np.linalg.norm(vertex_positions.values(edge_vertices[:, 1]) -
                       vertex_positions.values(edge_vertices[:, 0]),
                       axis=1),
        keys=gmap.elements(1))
    sorted_edge_length_edges = np.array(
        gmap.elements(1))[np.argsort(-edge_lengths.values(gmap.elements(1)))]
    sorted_edge_length_edges = sorted_edge_length_edges[
        edge_lengths.values(sorted_edge_length_edges) > maximal_length]
    max_len = sorted_edge_length_edges[0]
    edge_number = len(sorted_edge_length_edges)

    sum = edge_number
    while sum > 0.1 * edge_number:
        sum = 0
        sum += gmap_edge_split_optimization(gmap, 0.1)
        gmap.display()
        sum += gmap_edge_flip_optimization(gmap, 0.1)
        gmap.display()
        for i in range(4):
            gmap_taubin_smoothing(gmap)
        gmap.display()
コード例 #9
0
 def wisp_property(self, property_name, degree, wids=None):
     """todo"""
     try:
         if wids is not None:
             return array_dict(
                 self._wisp_properties[degree][property_name].values(wids),
                 wids)
         else:
             return self._wisp_properties[degree][property_name]
     except KeyError:
         raise PropertyError("property " + property_name +
                             " is undefined on wisps of degree " +
                             str(degree))
コード例 #10
0
def gmap_edge_flip_optimization(gmap, target_neighborhood=6):
    """
    Perform one iteration of edge flip optimization:
    Identify the GMap edges that can be flipped and 
    compute the neighborhood error variation induced by
    their flip. Rank them along this variation and 
    perform allowed edge flips for edges with a negative
    variation.
    """

    vertex_positions = array_dict([gmap.get_position(v) for v in gmap.darts()],
                                  gmap.darts())
    vertex_valence = array_dict(
        np.array(map(len, [gmap.orbit(v, [1, 2]) for v in gmap.darts()])) / 2,
        gmap.darts())

    edge_vertices = np.array([(e, gmap.alpha(0, e)) for e in gmap.elements(1)])
    edge_lengths = array_dict(
        np.linalg.norm(vertex_positions.values(edge_vertices[:, 1]) -
                       vertex_positions.values(edge_vertices[:, 0]),
                       axis=1),
        keys=gmap.elements(1))
    edge_flipped_vertices = np.array([[
        gmap.alpha(0, gmap.alpha(1, e)),
        gmap.alpha(0, gmap.alpha(1, gmap.alpha(2, e)))
    ] for e in gmap.elements(1)])

    flippable_edges = np.array(
        gmap.elements(1))[edge_flipped_vertices[:,
                                                0] != edge_flipped_vertices[:,
                                                                            1]]

    flippable_edge_vertices = edge_vertices[
        edge_flipped_vertices[:, 0] != edge_flipped_vertices[:, 1]]
    flippable_edge_flipped_vertices = np.array([
        e for e in edge_flipped_vertices[
            edge_flipped_vertices[:, 0] != edge_flipped_vertices[:, 1]]
    ])

    flippable_edge_triangle_vertices = np.array([[
        np.concatenate([e, [v]]) for v in f
    ] for (e,
           f) in zip(flippable_edge_vertices, flippable_edge_flipped_vertices)
                                                 ])
    flippable_edge_flipped_triangle_vertices = np.array([[
        np.concatenate([f, [v]]) for v in e
    ] for (e,
           f) in zip(flippable_edge_vertices, flippable_edge_flipped_vertices)
                                                         ])

    from gmap_tools import triangle_geometric_features
    flippable_edge_triangle_areas = np.concatenate([
        triangle_geometric_features(flippable_edge_triangle_vertices[:, e],
                                    vertex_positions,
                                    features=['area']) for e in [0, 1]
    ],
                                                   axis=1)
    flippable_edge_flipped_triangle_areas = np.concatenate([
        triangle_geometric_features(
            flippable_edge_flipped_triangle_vertices[:, e],
            vertex_positions,
            features=['area']) for e in [0, 1]
    ],
                                                           axis=1)

    average_area = np.nanmean(flippable_edge_triangle_areas)
    flippable_edge_flipped_triangle_areas[np.isnan(
        flippable_edge_flipped_triangle_areas)] = 100.
    wrong_edges = np.where(
        np.abs(
            flippable_edge_triangle_areas.sum(axis=1) -
            flippable_edge_flipped_triangle_areas.sum(axis=1)) > average_area /
        10.)

    flippable_edges = np.delete(flippable_edges, wrong_edges, 0)
    flippable_edge_vertices = np.delete(flippable_edge_vertices, wrong_edges,
                                        0)
    flippable_edge_triangle_vertices = np.delete(
        flippable_edge_triangle_vertices, wrong_edges, 0)
    flippable_edge_flipped_vertices = np.delete(
        flippable_edge_flipped_vertices, wrong_edges, 0)
    flippable_edge_flipped_triangle_vertices = np.delete(
        flippable_edge_flipped_triangle_vertices, wrong_edges, 0)
    flippable_edge_triangle_areas = np.delete(flippable_edge_triangle_areas,
                                              wrong_edges, 0)
    flippable_edge_flipped_triangle_areas = np.delete(
        flippable_edge_flipped_triangle_areas, wrong_edges, 0)

    flippable_edge_neighborhood_error = np.power(
        vertex_valence.values(flippable_edge_vertices) - target_neighborhood,
        2.0).sum(axis=1)
    flippable_edge_neighborhood_error += np.power(
        vertex_valence.values(flippable_edge_flipped_vertices) -
        target_neighborhood, 2.0).sum(axis=1)
    flippable_edge_neighborhood_flipped_error = np.power(
        vertex_valence.values(flippable_edge_vertices) - 1 -
        target_neighborhood, 2.0).sum(axis=1)
    flippable_edge_neighborhood_flipped_error += np.power(
        vertex_valence.values(flippable_edge_flipped_vertices) + 1 -
        target_neighborhood, 2.0).sum(axis=1)

    n_flips = 0
    if len(flippable_edges) > 0:

        flippable_edge_energy_variation = array_dict(
            flippable_edge_neighborhood_flipped_error -
            flippable_edge_neighborhood_error, flippable_edges)

        flippable_edge_sorted_energy_variation_edges = flippable_edges[
            np.argsort(
                flippable_edge_energy_variation.values(flippable_edges))]
        flippable_edge_sorted_energy_variation_edges = flippable_edge_sorted_energy_variation_edges[
            flippable_edge_energy_variation.values(
                flippable_edge_sorted_energy_variation_edges) < 0]
        modified_darts = set()
        print "--> Flipping edges"

        for e in flippable_edge_sorted_energy_variation_edges:

            flippable_edge = (len(
                modified_darts.intersection(set(gmap.orbit(e, [1, 2])))) == 0)
            flippable_edge = flippable_edge and (len(
                modified_darts.intersection(
                    set(gmap.orbit(gmap.alpha(0, e), [1, 2])))) == 0)
            flippable_edge = flippable_edge and (len(
                modified_darts.intersection(
                    set(gmap.orbit(gmap.alpha(0, gmap.alpha(1, e)), [1, 2]))))
                                                 == 0)
            flippable_edge = flippable_edge and (len(
                modified_darts.intersection(
                    set(
                        gmap.orbit(
                            gmap.alpha(0, gmap.alpha(1, gmap.alpha(2, e))),
                            [1, 2])))) == 0)

            if flippable_edge:
                n_e = len(gmap.elements(1))
                mod = triangular_gmap_flip_edge(gmap, e)
                modified_darts = modified_darts.union(set(mod))
                n_flips += 1
        print "<-- Flipping edges (", n_flips, " edges flipped)"

    return n_flips
コード例 #11
0
def gmap_edge_collapse_optimization(gmap,
                                    target_triangles=100,
                                    max_error=None,
                                    iterations_max=5):
    """
    Perform several iterations of edge collapse optimization:
    Compute the error associated with the collapse of each
    GMap edge. Then at each iteration, sort the edges by 
    ascending error, and collapse those that have a length >
    minimal_length and an error < max_error, until the GMap
    has target_triangles faces, or iterations_max iterations
    have been performed.
    """
    vertex_positions = array_dict([gmap.get_position(v) for v in gmap.darts()],
                                  gmap.darts())
    vertex_valence = array_dict(
        np.array(map(len, [gmap.orbit(v, [1, 2]) for v in gmap.darts()])) / 2,
        gmap.darts())

    edge_vertices = np.array([(e, gmap.alpha(0, e)) for e in gmap.elements(1)])
    edge_lengths = array_dict(
        np.linalg.norm(vertex_positions.values(edge_vertices[:, 1]) -
                       vertex_positions.values(edge_vertices[:, 0]),
                       axis=1),
        keys=gmap.elements(1))

    if target_triangles is None:
        target_triangles = len(gmap.elements(2)) / 4
    if minimal_length is None:
        minimal_length = np.percentile(edge_lengths.values(), 20)
    if max_error is None:
        max_error = np.power(minimal_length, 2)

    triangle_vertices = np.array(
        [gmap.incident_cells(f, 2, 0) for f in gmap.elements(2)])
    vertices_positions = np.array([[gmap.get_position(v) for v in t]
                                   for t in triangle_vertices])
    normal_vectors = np.cross(
        vertices_positions[:, 1] - vertices_positions[:, 0],
        vertices_positions[:, 2] - vertices_positions[:, 0])
    normal_norms = np.linalg.norm(normal_vectors, axis=1)
    normal_vectors = normal_vectors / normal_norms[:, np.newaxis]
    plane_d = -np.einsum('...ij,...ij->...i', normal_vectors,
                         vertices_positions[:, 0])
    triangle_planes = np.concatenate([normal_vectors, plane_d[:, np.newaxis]],
                                     axis=1)

    triangle_plane_quadrics = array_dict(
        np.einsum('...i,...j->...ij', triangle_planes, triangle_planes),
        gmap.elements(2))
    vertex_triangles = array_dict([[
        gmap.get_embedding_dart(d, triangle_plane_quadrics, 2)
        for d in gmap.incident_cells(v, 0, 2)
    ] for v in gmap.elements(0)],
                                  keys=gmap.elements(0))

    iteration = 0
    while iteration < iterations_max and len(
            gmap.elements(2)) > target_triangles:
        edge_vertices = np.array([[
            gmap.get_embedding_dart(d, vertex_triangles, 0)
            for d in gmap.incident_cells(e, 1, 0)
        ] for e in gmap.elements(1)])
        edge_lengths = array_dict(
            np.linalg.norm(vertex_positions.values(edge_vertices[:, 1]) -
                           vertex_positions.values(edge_vertices[:, 0]),
                           axis=1),
            keys=gmap.elements(1))

        edge_middles = np.array(
            [gmap.element_center(e, 1) for e in gmap.elements(1)])
        edge_middles_homogeneous = np.concatenate(
            [edge_middles, np.ones((len(gmap.elements(1)), 1))], axis=1)

        edge_vertex_faces = np.array([
            np.concatenate(vertex_triangles.values(v)) for v in edge_vertices
        ])
        edge_vertex_face_quadrics = np.array(
            [triangle_plane_quadrics.values(t) for t in edge_vertex_faces])
        edge_vertex_face_middles = np.array(
            [[e for t in e_v_t]
             for e, e_v_t in zip(edge_middles_homogeneous, edge_vertex_faces)])
        edge_quadrics_errors = np.array([
            np.abs(
                np.einsum('...ij,...ij->...i', m,
                          np.einsum('...ij,...j->...i', q, m))).sum() for q, m
            in zip(edge_vertex_face_quadrics, edge_vertex_face_middles)
        ])

        edge_quadrics_errors = array_dict(edge_quadrics_errors,
                                          gmap.elements(1))

        permutated_edges = np.array(gmap.elements(1))
        np.random.shuffle(permutated_edges)

        # sorted_quadrics_errors_edges = np.array(gmap.elements(1))[np.argsort(edge_quadrics_errors)]
        sorted_quadrics_errors_edges = permutated_edges[np.argsort(
            edge_quadrics_errors.values(permutated_edges))]
        # sorted_quadrics_errors_edges = sorted_quadrics_errors_edges[np.sort(edge_quadrics_errors) < np.percentile(edge_quadrics_errors,5)]
        sorted_quadrics_errors_edges = sorted_quadrics_errors_edges[
            edge_quadrics_errors.values(
                sorted_quadrics_errors_edges) < max_error]
        sorted_quadrics_errors_edges = sorted_quadrics_errors_edges[
            edge_lengths.values(sorted_quadrics_errors_edges) < minimal_length]
        sorted_quadrics_errors_edges = list(sorted_quadrics_errors_edges)

        # sorted_length_edges = permutated_edges[np.argsort(edge_lengths.values(permutated_edges))]
        # sorted_length_edges = sorted_length_edges[edge_quadrics_errors.values(sorted_length_edges) < max_error]
        # sorted_length_edges = list(sorted_length_edges)

        print "--> Collapsing edges"
        modified_edges = set()
        n_collapses = 0
        while len(sorted_quadrics_errors_edges) > 0 and len(
                gmap.elements(2)) > target_triangles:
            # while len(sorted_length_edges)>0 and len(gmap.elements(2))>target_triangles:
            e = sorted_quadrics_errors_edges.pop(0)
            # e = sorted_length_edges.pop(0)
            if not e in modified_edges and len(
                    gmap.elements(2)) > target_triangles:
                #print [gmap.orbit(d,[0,2]) for d in gmap.adjacent_cells(e,1)]
                #print np.unique(np.concatenate([gmap.orbit(d,[0,2]) for d in gmap.adjacent_cells(e,1)]))
                modified_edges = modified_edges.union(
                    set(
                        np.unique(
                            np.concatenate([
                                gmap.orbit(d, [0, 2])
                                for d in gmap.adjacent_cells(e, 1)
                            ])))).union({e})
                collapsed = triangular_gmap_dual_collapse_edge(gmap, e)
                n_collapses += collapsed
                if collapsed:
                    print "  --> Collapsed edge ", e, " [", len(
                        gmap.elements(2)), " Faces]"
        print "<-- Collapsing edges (", n_collapses, " edges collapsed)"

        iteration += 1

    return n_collapses
コード例 #12
0
def read_ply_mesh(filename):
    import csv
    import re

    property_types = {}
    property_types['int'] = 'int'
    property_types['int32'] = 'int'
    property_types['uint'] = 'int'
    property_types['uint8'] = 'int'
    property_types['uchar'] = 'int'
    property_types['float'] = 'float'
    property_types['float32'] = 'float'
    property_types['list'] = 'list'

    ply_file = open(filename, 'rb')

    assert "ply" in ply_file.next()
    assert "ascii" in ply_file.next()

    n_wisps = {}
    properties = {}
    properties_types = {}
    properties_list_types = {}
    element_name = ""
    property_name = ""
    elements = []

    line = ply_file.next()
    while not 'end_header' in line:

        if re.split(' ', line)[0] == 'element':
            element_name = re.split(' ', line)[1]
            elements.append(element_name)
            n_wisps[element_name] = int(re.split(' ', line)[2])
            properties[element_name] = []
            properties_types[element_name] = {}
            properties_list_types[element_name] = {}

        if re.split(' ', line)[0] == 'property':
            property_name = re.split(' ', line)[-1][:-1]
            properties[element_name].append(property_name)
            properties_types[element_name][property_name] = re.split(
                ' ', line)[1]
            if properties_types[element_name][property_name] == 'list':
                list_type = re.split(' ', line)[-2]
                properties_list_types[element_name][property_name] = list_type

        line = ply_file.next()

    print n_wisps
    print properties

    element_properties = {}

    for element_name in elements:
        element_properties[element_name] = {}
        for wid in xrange(n_wisps[element_name]):
            line = ply_file.next()
            line_props = {}
            prop_index = 0
            for prop in properties[element_name]:
                prop_type = properties_types[element_name][prop]
                if property_types[prop_type] == 'float':
                    line_props[prop] = float(re.split(' ', line)[prop_index])
                    prop_index += 1
                elif property_types[prop_type] == 'int':
                    line_props[prop] = int(re.split(' ', line)[prop_index])
                    prop_index += 1
                elif property_types[prop_type] == 'list':
                    list_length = int(re.split(' ', line)[prop_index])
                    prop_index += 1
                    list_type = properties_list_types[element_name][prop]
                    if property_types[list_type] == 'float':
                        line_props[prop] = [
                            float(p) for p in re.split(' ', line)
                            [prop_index:prop_index + list_length]
                        ]
                    elif property_types[list_type] == 'int':
                        line_props[prop] = [
                            int(p) for p in re.split(' ', line)
                            [prop_index:prop_index + list_length]
                        ]
                    prop_index += list_length
            element_properties[element_name][wid] = line_props
    ply_file.close()

    point_positions = {}
    for pid in xrange(n_wisps['vertex']):
        point_positions[pid] = np.array([
            element_properties['vertex'][pid][dim] for dim in ['x', 'y', 'z']
        ])

    face_vertices = {}
    for fid in xrange(n_wisps['face']):
        if element_properties['face'][fid].has_key('vertex_index'):
            face_vertices[fid] = element_properties['face'][fid][
                'vertex_index']
        elif element_properties['face'][fid].has_key('vertex_indices'):
            face_vertices[fid] = element_properties['face'][fid][
                'vertex_indices']

    print len(point_positions), " Points, ", len(face_vertices), " Faces"
    #raw_input()

    unique_points = array_unique(np.array(point_positions.values()))
    point_matching = array_dict(
        vq(np.array(point_positions.values()), unique_points)[0],
        point_positions.keys())

    print len(unique_points), " Unique Points"
    #raw_input()

    faces = np.array(face_vertices.values())
    if faces.ndim == 2:
        triangular = True
        triangles = np.sort(point_matching.values(faces))
        unique_triangles = array_unique(triangles)
        triangle_matching = array_dict(
            vq(triangles, unique_triangles)[0], face_vertices.keys())
    else:
        triangular = False
        unique_triangles = point_matching.values(faces)
        triangle_matching = array_dict(face_vertices.keys(),
                                       face_vertices.keys())

    print len(unique_triangles), " Unique Faces"

    return unique_points, unique_triangles