Esempio n. 1
0
    def test_spline_fitting(self):
        # Test a real spline interpolation
        xs = np.linspace(0.0, 2 * math.pi, 100)
        ys = np.array([math.sin(x) for x in xs])
        points = [geom.Vertex((xs[i], ys[i], 0.0)) for i in range(len(xs))]

        cpoints_chunks = fit.fit_bezier_spline(points, mmath.interp_bezier_curve_2, 2)
        curves = []
        cpoints = []
        for chunk in cpoints_chunks:
            cpoints += chunk
            verts = map(lambda x: geom.Vertex(x), chunk)
            curves += [geom.BaseCurve(list(verts), mmath.interp_bezier_curve_2)]
        curve = geom.SuperCurve(curves)

        # Compute the current spline
        spline_ts = np.linspace(0.0, 1.0, 100)
        spline_points = [curve.t(t) for t in spline_ts]
        spline_xs = [p.x for p in spline_points]
        spline_ys = [p.y for p in spline_points]

        # Compute the control points
        cpointsx = [cp[0] for cp in cpoints]
        cpointsy = [cp[1] for cp in cpoints]

        # Actually plot the graph
        if DRAW_GRAPHS:
            plt.plot(spline_xs, spline_ys)  # Approximate function
            plt.plot(xs, ys)  # Original function
            plt.plot(cpointsx, cpointsy, "o")
            plt.show()
def plot_beizer_spline_2d(points, function, n, figname, steps=100):
    points_xs = [p[0] for p in points]
    points_ys = [p[1] for p in points]

    cpoints_chunks = fit.fit_bezier_spline(points, function, n)
    curves = []
    cpoints = []
    for chunk in cpoints_chunks:
        cpoints += chunk
        verts = map(lambda x: geom.Vertex(x), chunk)
        curves += [geom.BaseCurve(list(verts), function)]
    curve = geom.SuperCurve(curves)

    spline_ts = np.linspace(0.0, 1.0, steps)
    spline_points = [curve.t(t) for t in spline_ts]
    curve_xs = [p.x for p in spline_points]
    curve_ys = [p.y for p in spline_points]

    # Compute the control points
    cpoints_xs = [cp[0] for cp in cpoints]
    cpoints_ys = [cp[1] for cp in cpoints]

    plt.plot(points_xs, points_ys, "o", color="blue", label="Input points")
    plt.plot(cpoints_xs, cpoints_ys, "o", color="red", label="Control points")
    plt.plot(curve_xs, curve_ys, color="green", label="Bezier spline")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    plt.show()
def plot_bezier_spline_3d(points, function, n, figname, steps=100):
    cpoints_chunks = fit.fit_bezier_spline(points, mmath.bezier_curve_3, 5)
    curves = []
    cpoints = []
    for chunk in cpoints_chunks:
        cpoints += chunk
        verts = map(lambda x: geom.Vertex(x), chunk)
        curves += [geom.BaseCurve(list(verts), mmath.bezier_curve_3)]
    curve = geom.SuperCurve(curves)
    
    # Compute the current spline
    spline_ts = np.linspace(0.0, 1.0, steps)
    spline_points = [curve.t(t) for t in spline_ts]
    spline_xs = [p.x for p in spline_points]
    spline_ys = [p.y for p in spline_points]
    spline_zs = [p.z for p in spline_points]
    
    # Compute the current control points
    cpointsx = [cp[0] for cp in cpoints]
    cpointsy = [cp[1] for cp in cpoints]
    cpointsz = [cp[2] for cp in cpoints]
    
    # Draw the graph
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.plot([p[0] for p in points], [p[1] for p in points], [p[2] for p in points], "o", color="blue", label="Input points")
    ax.plot(spline_xs, spline_ys, spline_zs, color="green", label='Bezier spline')
    ax.plot(cpointsx, cpointsy, cpointsz, "o", color="red", label="Control points")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    plt.show()
def plot_beizer_spline_2d(points, function, n, figname, steps=100):
    points_xs = [p[0] for p in points]
    points_ys = [p[1] for p in points]
    
    cpoints_chunks = fit.fit_bezier_spline(points, function, n)
    curves = []
    cpoints = []
    for chunk in cpoints_chunks:
        cpoints += chunk
        verts = map(lambda x: geom.Vertex(x), chunk)
        curves += [geom.BaseCurve(list(verts), function)]
    curve = geom.SuperCurve(curves)
    
    spline_ts = np.linspace(0.0, 1.0, steps)
    spline_points = [curve.t(t) for t in spline_ts]
    curve_xs = [p.x for p in spline_points]
    curve_ys = [p.y for p in spline_points]
    
    # Compute the control points
    cpoints_xs = [cp[0] for cp in cpoints]
    cpoints_ys = [cp[1] for cp in cpoints]
    
    plt.plot(points_xs, points_ys, "o", color = "blue", label = "Input points")
    plt.plot(cpoints_xs, cpoints_ys, "o", color = "red", label = "Control points")
    plt.plot(curve_xs, curve_ys, color = "green", label = "Bezier spline")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    plt.show()
Esempio n. 5
0
    def test_spline_fitting(self):
        # Test a real spline interpolation
        xs = np.linspace(0.0, 2 * math.pi, 100)
        ys = np.array([math.sin(x) for x in xs])
        points = [geom.Vertex((xs[i], ys[i], 0.0)) for i in range(len(xs))]

        cpoints_chunks = fit.fit_bezier_spline(points,
                                               mmath.interp_bezier_curve_2, 2)
        curves = []
        cpoints = []
        for chunk in cpoints_chunks:
            cpoints += chunk
            verts = map(lambda x: geom.Vertex(x), chunk)
            curves += [
                geom.BaseCurve(list(verts), mmath.interp_bezier_curve_2)
            ]
        curve = geom.SuperCurve(curves)

        # Compute the current spline
        spline_ts = np.linspace(0.0, 1.0, 100)
        spline_points = [curve.t(t) for t in spline_ts]
        spline_xs = [p.x for p in spline_points]
        spline_ys = [p.y for p in spline_points]

        # Compute the control points
        cpointsx = [cp[0] for cp in cpoints]
        cpointsy = [cp[1] for cp in cpoints]

        # Actually plot the graph
        if DRAW_GRAPHS:
            plt.plot(spline_xs, spline_ys)  # Approximate function
            plt.plot(xs, ys)  # Original function
            plt.plot(cpointsx, cpointsy, "o")
            plt.show()
def plot_bezier_spline_3d(points, function, n, figname, steps=100):
    cpoints_chunks = fit.fit_bezier_spline(points, mmath.bezier_curve_3, 5)
    curves = []
    cpoints = []
    for chunk in cpoints_chunks:
        cpoints += chunk
        verts = map(lambda x: geom.Vertex(x), chunk)
        curves += [geom.BaseCurve(list(verts), mmath.bezier_curve_3)]
    curve = geom.SuperCurve(curves)

    # Compute the current spline
    spline_ts = np.linspace(0.0, 1.0, steps)
    spline_points = [curve.t(t) for t in spline_ts]
    spline_xs = [p.x for p in spline_points]
    spline_ys = [p.y for p in spline_points]
    spline_zs = [p.z for p in spline_points]

    # Compute the current control points
    cpointsx = [cp[0] for cp in cpoints]
    cpointsy = [cp[1] for cp in cpoints]
    cpointsz = [cp[2] for cp in cpoints]

    # Draw the graph
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.plot([p[0] for p in points], [p[1] for p in points],
            [p[2] for p in points],
            "o",
            color="blue",
            label="Input points")
    ax.plot(spline_xs,
            spline_ys,
            spline_zs,
            color="green",
            label='Bezier spline')
    ax.plot(cpointsx,
            cpointsy,
            cpointsz,
            "o",
            color="red",
            label="Control points")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    plt.show()
Esempio n. 7
0
    def test_spline_fitting_3d(self):
        """Fit a complex 3d curve with a bezier spline of degree three (5 curves)."""
        # Generate a cool 3d curve
        theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
        zs = np.linspace(-2, 2, 100)
        r = zs ** 2 + 1
        xs = r * np.sin(theta)
        ys = r * np.cos(theta)

        # Fit the data
        points = list(zip(xs, ys, zs))
        cpoints_chunks = fit.fit_bezier_spline(points, mmath.bezier_curve_3, 5)
        curves = []
        cpoints = []
        for chunk in cpoints_chunks:
            cpoints += chunk
            verts = map(lambda x: geom.Vertex(x), chunk)
            curves += [geom.BaseCurve(list(verts), mmath.bezier_curve_3)]
        curve = geom.SuperCurve(curves)

        # Compute the current spline
        spline_ts = np.linspace(0.0, 1.0, 100)
        spline_points = [curve.t(t) for t in spline_ts]
        spline_xs = [p.x for p in spline_points]
        spline_ys = [p.y for p in spline_points]
        spline_zs = [p.z for p in spline_points]

        # Compute the current control points
        cpointsx = [cp[0] for cp in cpoints]
        cpointsy = [cp[1] for cp in cpoints]
        cpointsz = [cp[2] for cp in cpoints]

        # Draw the graph
        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection="3d")
            ax.plot(xs, ys, zs, label="parametric curve")
            ax.plot(spline_xs, spline_ys, spline_zs, label="Fitted spline")
            ax.plot(cpointsx, cpointsy, cpointsz, "o", label="Control points")
            plt.legend()
            plt.show()
Esempio n. 8
0
    def test_spline_fitting_3d(self):
        '''Fit a complex 3d curve with a bezier spline of degree three (5 curves).'''
        # Generate a cool 3d curve
        theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
        zs = np.linspace(-2, 2, 100)
        r = zs**2 + 1
        xs = r * np.sin(theta)
        ys = r * np.cos(theta)

        # Fit the data
        points = list(zip(xs, ys, zs))
        cpoints_chunks = fit.fit_bezier_spline(points, mmath.bezier_curve_3, 5)
        curves = []
        cpoints = []
        for chunk in cpoints_chunks:
            cpoints += chunk
            verts = map(lambda x: geom.Vertex(x), chunk)
            curves += [geom.BaseCurve(list(verts), mmath.bezier_curve_3)]
        curve = geom.SuperCurve(curves)

        # Compute the current spline
        spline_ts = np.linspace(0.0, 1.0, 100)
        spline_points = [curve.t(t) for t in spline_ts]
        spline_xs = [p.x for p in spline_points]
        spline_ys = [p.y for p in spline_points]
        spline_zs = [p.z for p in spline_points]

        # Compute the current control points
        cpointsx = [cp[0] for cp in cpoints]
        cpointsy = [cp[1] for cp in cpoints]
        cpointsz = [cp[2] for cp in cpoints]

        # Draw the graph
        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection='3d')
            ax.plot(xs, ys, zs, label='parametric curve')
            ax.plot(spline_xs, spline_ys, spline_zs, label='Fitted spline')
            ax.plot(cpointsx, cpointsy, cpointsz, "o", label="Control points")
            plt.legend()
            plt.show()
def run(bm):
    '''Run the algorithm on the given bmesh (blender mesh), 
    returns the patches (TODO in a data structure we still need to decide)'''
    verts_list = list(bm.verts)
    faces_list = list(bm.faces)
    verts_indexes = [v.index for v in verts_list]
    
    # Extract the skeleton mesh.
    patches, macro_edges, singular_verts = extract_base_mesh(bm)

    def calculate_patches(macro_edges, bm):
        boundaries = set(sum(macro_edges, ()))
        
        # Now we need to understand which vertex belong to which face. We use the connected components approach.
        patch_verts_attribution = partition_mesh_vertices(verts_indexes, boundaries, bm)
        
        # Now we need to understand which superedges belong to which face.
        patches = compute_patch_edges(patch_verts_attribution, macro_edges)
        
        # We now need to reorder the vertices of each face so that we can build a spline on them.
        for i, part in enumerate(patches):
            try:
                patches[i] = reorder_patch_edges(part)
            except:
                patches[i] = None
        
        # Filter empty and quadrangualte.
        patches = [p for p in patches if p]
        patches = quadrangulate_patches_keep_separate_edges(patches, verts_list)
        
        return patch_verts_attribution, patches
    
    MIN_VERTS = 20
    
    threshold = PATCH_THRESHOLD * size_estimate(bm)
    print("Using Threshold:", threshold)        
    
    def is_patch_big_enough(patch):
        '''Returns if all the edges of the patch are big enough for it to be splitted.'''
        return all(len(edge) >= 5 for edge in flat_patch_edges(patch))
        
    
    def can_simplify(patch_verts):
        '''Returns wheter the patch contains enough point to be simplified.'''
        return patch_verts and len(patch_verts) > MIN_VERTS
    
    patch_verts_attribution, patches = calculate_patches(macro_edges, bm)
    skip_patches = []
    
    # Now that we defined the patches we should iterate to improve the error.
    result = []
    patch_improved = True
    #patch_improved = False
    while patch_improved:
        patch_improved = False
        print("Number of patches", len(patches))
        
        for i,current_patch in enumerate(patches):
            current_attr = patch_verts_attribution[i]
        
            if all([current_patch not in skip_patches,
                    can_simplify(current_attr),
                    is_patch_big_enough(current_patch),
                    compute_patch_error(flat_patch_edges(current_patch), bm, current_attr) > threshold]):
                
                #pr("ERROR", compute_patch_error(current_patch, bm, current_attr))
                try:
                    old_edges = sum(current_patch,[])
                    new_edges = split_patch_4(current_patch, current_attr, bm)
                    new_macro_edges = set(macro_edges)
                    
                    for edge in old_edges:
                        if edge in new_macro_edges:
                            new_macro_edges.remove(edge)
                        if edge[::-1] in macro_edges:
                            new_macro_edges.remove(edge[::-1])
                    
                    for edge in new_edges:
                        if edge[::-1] not in new_macro_edges:
                            new_macro_edges.add(edge)
                    
                    patch_verts_attribution, patches = calculate_patches(new_macro_edges, bm)
                    macro_edges = new_macro_edges
                    
                    patch_improved = True
                    break
                except Exception as e:
                    print("Patch discarded")
                    skip_patches += [current_patch]
                    
    result = patches
    
    #We need to fit the patch edges with lower degree curves
    #edges = set(sum(result, []))
    edges  = set(sum(sum(result, []), []))
    edges = sorted(edges, key=len)
    
    edge_correspondence = {}
    old_verts = [geom.Vertex((v.co.x, v.co.y, v.co.z)) for v in verts_list]
    
    # TODO Disable this. Returns the patches without the edge fitting.
    #return old_verts, result, old_verts, result
    
    # Output values
    new_verts = []
    new_edges = []
    new_patches = []
    
    SAMPLES = 20
    spline_threshold = SPLINE_THRESHOLD * size_estimate(bm)
    THRESHOLD_DISTANCE = VERTEX_MERGE_THRESHOLD * size_estimate(bm)
    
    edge_conversion = {}
    
    def merge_all_edges(edges):
        if not edges:
            return []
        result = edges[0]
        for e in edges[1:]:
            result += e[1:]
        return tuple(result)
    
    for i,edge in enumerate(edges):
        if edge_conversion.get(edge):
            pass
        else:
            cpoints = tuple([old_verts[i] for i in edge])
            
            def squash_chunks(chunks):
                if len(chunks) == 1:
                    return chunks[0]
                return chunks[0][:-1] + squash_chunks(chunks[1:])
            
            
            new_cpoints = []
            error = float("inf")
            n_splines = 1
            
            try:
                while error > spline_threshold:
                    cpoints_chunks = fit.fit_bezier_spline(cpoints, mmath.interp_bezier_curve_2, n_splines)
                    verts_chunks = [[geom.Vertex(p) for p in chunk] for chunk in cpoints_chunks]
                    verts_chunks[0][0] = cpoints[0]
                    verts_chunks[-1][-1] = cpoints[-1]
                    
                    # Increase the precision at the next step
                    n_splines += 1 
                    
                    # We need to use the original extremes control points to ensure continuity
                    new_cpoints = squash_chunks(verts_chunks)
                    
                    # Check the error and fallback to default spline if issues
                    new_curve = geom.generate_spline(new_cpoints, mmath.interp_bezier_curve_2)
                    new_curve_points = list(geom.sample_curve_samples(new_curve, len(cpoints)))
                    error = errors.simple_max_error(new_curve_points, cpoints)
            except:
                new_cpoints = cpoints
            
            print("Compressing edge: %s/%s. n.verts: %s -> %s" % (i+1, len(edges), len(cpoints), len(new_cpoints)))
            
            new_verts += new_cpoints
            new_verts_indexes = tuple([new_verts.index(v) for v in new_cpoints])
            edge_conversion[edge] = new_verts_indexes
            edge_conversion[edge[::-1]] = new_verts_indexes[::-1]
    
    final_patches = []
    
    # Convert the edges with the approximated ones.
    for patch in result:
        converted_edges = [[edge_conversion[edge] for edge in edges] for edges in patch]
        final_patches += [flat_patch_edges(converted_edges)]
    
    # TODO Remember to disable this.
    #return new_verts, final_patches, new_verts, final_patches
    
    def generate_convert(threshold_index, prev_index):
        def convert(index):
            if index < threshold_index:
                return index
            elif index == threshold_index:
                return prev_index
            else:
                return index - 1
        return convert
    
    def update_edge(e, convert):
        return tuple([convert(i) for i in e])
    
    def update_patch(p, convert):
        return [update_edge(e, convert) for e in p]
    
    def my_index(l, element):
        """Returns the element in the list, otherwise -1"""
        return l.index(element) if element in l else -1

    def my_index_closest(l, element):
        temp = list(l)
        mod = lambda x: x[0] * x[0] + x[1] * x[1] + x[2] * x[2]
        
        closest = sorted(temp, key=lambda x: mod(x - element))[0]
        
        return l.index(closest) if mod(element - closest) < THRESHOLD_DISTANCE else -1

    def recursive_max(l):
        if isinstance(l, (int, float)):
            return l
        return max((recursive_max(e) for e in l))
    
    # Filter out vertices which are not unique.
    
    final_verts = []
    for i, vert in enumerate(new_verts):
        index = my_index_closest(final_verts, vert) if final_verts else -1
        if index == -1:
            final_verts += [vert]
        else:
            # The element is not in the list. This means that we need to decrement all the indexes and replace i with index.
            convert_func = generate_convert(len(final_verts), index)
            for i, p in enumerate(final_patches):
                final_patches[i] = update_patch(p, convert_func)
    
    input_size = compute_input_mesh_size(bm)
    output_size = compute_output_mesh_size(final_patches)
    
    print("Statistics ------------------------------------------")
    print("Input Vertices:   ", len(verts_list))
    print("Input Faces:      ", len(faces_list))
    print("Output Vertices:  ", len(final_verts))
    print("Output Faces:     ", len(final_patches))
    print("Input Size:       ", input_size)
    print("Output Size:      ", output_size)
    print("Compression Ratio:", (input_size / output_size))
    print("-----------------------------------------------------")
    
    old_pathces = [flat_patch_edges(p) for p in result]
    
    return old_verts, old_pathces, final_verts, final_patches
Esempio n. 10
0
def run(bm):
    '''Run the algorithm on the given bmesh (blender mesh), 
    returns the patches (TODO in a data structure we still need to decide)'''
    verts_list = list(bm.verts)
    faces_list = list(bm.faces)
    verts_indexes = [v.index for v in verts_list]

    # Extract the skeleton mesh.
    patches, macro_edges, singular_verts = extract_base_mesh(bm)

    def calculate_patches(macro_edges, bm):
        boundaries = set(sum(macro_edges, ()))

        # Now we need to understand which vertex belong to which face. We use the connected components approach.
        patch_verts_attribution = partition_mesh_vertices(
            verts_indexes, boundaries, bm)

        # Now we need to understand which superedges belong to which face.
        patches = compute_patch_edges(patch_verts_attribution, macro_edges)

        # We now need to reorder the vertices of each face so that we can build a spline on them.
        for i, part in enumerate(patches):
            try:
                patches[i] = reorder_patch_edges(part)
            except:
                patches[i] = None

        # Filter empty and quadrangualte.
        patches = [p for p in patches if p]
        patches = quadrangulate_patches_keep_separate_edges(
            patches, verts_list)

        return patch_verts_attribution, patches

    MIN_VERTS = 20

    threshold = PATCH_THRESHOLD * size_estimate(bm)
    print("Using Threshold:", threshold)

    def is_patch_big_enough(patch):
        '''Returns if all the edges of the patch are big enough for it to be splitted.'''
        return all(len(edge) >= 5 for edge in flat_patch_edges(patch))

    def can_simplify(patch_verts):
        '''Returns wheter the patch contains enough point to be simplified.'''
        return patch_verts and len(patch_verts) > MIN_VERTS

    patch_verts_attribution, patches = calculate_patches(macro_edges, bm)
    skip_patches = []

    # Now that we defined the patches we should iterate to improve the error.
    result = []
    patch_improved = True
    #patch_improved = False
    while patch_improved:
        patch_improved = False
        print("Number of patches", len(patches))

        for i, current_patch in enumerate(patches):
            current_attr = patch_verts_attribution[i]

            if all([
                    current_patch not in skip_patches,
                    can_simplify(current_attr),
                    is_patch_big_enough(current_patch),
                    compute_patch_error(flat_patch_edges(current_patch), bm,
                                        current_attr) > threshold
            ]):

                #pr("ERROR", compute_patch_error(current_patch, bm, current_attr))
                try:
                    old_edges = sum(current_patch, [])
                    new_edges = split_patch_4(current_patch, current_attr, bm)
                    new_macro_edges = set(macro_edges)

                    for edge in old_edges:
                        if edge in new_macro_edges:
                            new_macro_edges.remove(edge)
                        if edge[::-1] in macro_edges:
                            new_macro_edges.remove(edge[::-1])

                    for edge in new_edges:
                        if edge[::-1] not in new_macro_edges:
                            new_macro_edges.add(edge)

                    patch_verts_attribution, patches = calculate_patches(
                        new_macro_edges, bm)
                    macro_edges = new_macro_edges

                    patch_improved = True
                    break
                except Exception as e:
                    print("Patch discarded")
                    skip_patches += [current_patch]

    result = patches

    #We need to fit the patch edges with lower degree curves
    #edges = set(sum(result, []))
    edges = set(sum(sum(result, []), []))
    edges = sorted(edges, key=len)

    edge_correspondence = {}
    old_verts = [geom.Vertex((v.co.x, v.co.y, v.co.z)) for v in verts_list]

    # TODO Disable this. Returns the patches without the edge fitting.
    #return old_verts, result, old_verts, result

    # Output values
    new_verts = []
    new_edges = []
    new_patches = []

    SAMPLES = 20
    spline_threshold = SPLINE_THRESHOLD * size_estimate(bm)
    THRESHOLD_DISTANCE = VERTEX_MERGE_THRESHOLD * size_estimate(bm)

    edge_conversion = {}

    def merge_all_edges(edges):
        if not edges:
            return []
        result = edges[0]
        for e in edges[1:]:
            result += e[1:]
        return tuple(result)

    for i, edge in enumerate(edges):
        if edge_conversion.get(edge):
            pass
        else:
            cpoints = tuple([old_verts[i] for i in edge])

            def squash_chunks(chunks):
                if len(chunks) == 1:
                    return chunks[0]
                return chunks[0][:-1] + squash_chunks(chunks[1:])

            new_cpoints = []
            error = float("inf")
            n_splines = 1

            try:
                while error > spline_threshold:
                    cpoints_chunks = fit.fit_bezier_spline(
                        cpoints, mmath.interp_bezier_curve_2, n_splines)
                    verts_chunks = [[geom.Vertex(p) for p in chunk]
                                    for chunk in cpoints_chunks]
                    verts_chunks[0][0] = cpoints[0]
                    verts_chunks[-1][-1] = cpoints[-1]

                    # Increase the precision at the next step
                    n_splines += 1

                    # We need to use the original extremes control points to ensure continuity
                    new_cpoints = squash_chunks(verts_chunks)

                    # Check the error and fallback to default spline if issues
                    new_curve = geom.generate_spline(
                        new_cpoints, mmath.interp_bezier_curve_2)
                    new_curve_points = list(
                        geom.sample_curve_samples(new_curve, len(cpoints)))
                    error = errors.simple_max_error(new_curve_points, cpoints)
            except:
                new_cpoints = cpoints

            print("Compressing edge: %s/%s. n.verts: %s -> %s" %
                  (i + 1, len(edges), len(cpoints), len(new_cpoints)))

            new_verts += new_cpoints
            new_verts_indexes = tuple(
                [new_verts.index(v) for v in new_cpoints])
            edge_conversion[edge] = new_verts_indexes
            edge_conversion[edge[::-1]] = new_verts_indexes[::-1]

    final_patches = []

    # Convert the edges with the approximated ones.
    for patch in result:
        converted_edges = [[edge_conversion[edge] for edge in edges]
                           for edges in patch]
        final_patches += [flat_patch_edges(converted_edges)]

    # TODO Remember to disable this.
    #return new_verts, final_patches, new_verts, final_patches

    def generate_convert(threshold_index, prev_index):
        def convert(index):
            if index < threshold_index:
                return index
            elif index == threshold_index:
                return prev_index
            else:
                return index - 1

        return convert

    def update_edge(e, convert):
        return tuple([convert(i) for i in e])

    def update_patch(p, convert):
        return [update_edge(e, convert) for e in p]

    def my_index(l, element):
        """Returns the element in the list, otherwise -1"""
        return l.index(element) if element in l else -1

    def my_index_closest(l, element):
        temp = list(l)
        mod = lambda x: x[0] * x[0] + x[1] * x[1] + x[2] * x[2]

        closest = sorted(temp, key=lambda x: mod(x - element))[0]

        return l.index(closest) if mod(element -
                                       closest) < THRESHOLD_DISTANCE else -1

    def recursive_max(l):
        if isinstance(l, (int, float)):
            return l
        return max((recursive_max(e) for e in l))

    # Filter out vertices which are not unique.

    final_verts = []
    for i, vert in enumerate(new_verts):
        index = my_index_closest(final_verts, vert) if final_verts else -1
        if index == -1:
            final_verts += [vert]
        else:
            # The element is not in the list. This means that we need to decrement all the indexes and replace i with index.
            convert_func = generate_convert(len(final_verts), index)
            for i, p in enumerate(final_patches):
                final_patches[i] = update_patch(p, convert_func)

    input_size = compute_input_mesh_size(bm)
    output_size = compute_output_mesh_size(final_patches)

    print("Statistics ------------------------------------------")
    print("Input Vertices:   ", len(verts_list))
    print("Input Faces:      ", len(faces_list))
    print("Output Vertices:  ", len(final_verts))
    print("Output Faces:     ", len(final_patches))
    print("Input Size:       ", input_size)
    print("Output Size:      ", output_size)
    print("Compression Ratio:", (input_size / output_size))
    print("-----------------------------------------------------")

    old_pathces = [flat_patch_edges(p) for p in result]

    return old_verts, old_pathces, final_verts, final_patches