Example #1
0
    def test_simple_net(self):
        v0 = geom.Vertex((0.0, 0.0, 0.0))
        v1 = geom.Vertex((1.0, 0.0, 0.0))
        v2 = geom.Vertex((1.0, 1.0, 0.0))
        v3 = geom.Vertex((0.0, 1.0, 0.0))

        disp = geom.Vertex((0.0, 0.0, 0.5))

        m0 = (v0 + v1) / 2.0 + disp
        m1 = (v1 + v2) / 2.0 + disp
        m2 = (v2 + v3) / 2.0 + disp
        m3 = (v3 + v0) / 2.0 + disp

        c1 = geom.SuperCurve([geom.BaseCurve((v0, m0, v1), mmath.interp_bezier_curve_2)])
        c2 = geom.BaseCurve((v1, m1, v2), mmath.interp_bezier_curve_2)
        c3 = geom.BaseCurve((v2, m2, v3), mmath.interp_bezier_curve_2)
        c4 = geom.BaseCurve((v3, m3, v0), mmath.interp_bezier_curve_2)

        c1_points = list(geom.sample_curve_samples(c1, 10))
        c2_points = list(geom.sample_curve_samples(c2, 10))
        c3_points = list(geom.sample_curve_samples(c3, 10))
        c4_points = list(geom.sample_curve_samples(c4, 10))

        polygon = geom.PolygonsNetQuad((c1, c2, c3, c4))
        points = list(geom.sample_patch_samples(polygon, 70))

        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection="3d")
            ax.plot([p.x for p in c1_points], [p.y for p in c1_points], [p.z for p in c1_points])
            ax.plot([p.x for p in c2_points], [p.y for p in c2_points], [p.z for p in c2_points])
            ax.plot([p.x for p in c3_points], [p.y for p in c3_points], [p.z for p in c3_points])
            ax.plot([p.x for p in c4_points], [p.y for p in c4_points], [p.z for p in c4_points])
            ax.plot([p.x for p in points], [p.y for p in points], [p.z for p in points], "o", label="Geometry points")
            plt.show()
Example #2
0
    def test_complex_net(self):
        coords1 = [(0.0, 0.0, 0.0), (0.5, 0.0, 1.0), (1.0, -0.2, 0.5), (1.5, 0.0, 0.0), (2.0, -0.1, 0.5)]
        cpoints1 = [geom.Vertex(coord) for coord in coords1]
        curve1 = geom.generate_spline(cpoints1, mmath.interp_bezier_curve_2)

        coords2 = [(2.0, -0.1, 0.5), (2.0, 1.0, 1.0), (2.1, 2.0, 0.0)]
        cpoints2 = [geom.Vertex(coord) for coord in coords2]
        curve2 = geom.generate_spline(cpoints2, mmath.interp_bezier_curve_2)

        coords3 = [(2.1, 2.0, 0.0), (1.5, 2.0, 0.0), (1.0, 2.1, 0.5), (0.5, 2.0, 1.0), (0.0, 2.0, 0.0)]
        cpoints3 = [geom.Vertex(coord) for coord in coords3]
        curve3 = geom.generate_spline(cpoints3, mmath.interp_bezier_curve_2)

        coords4 = [(0.0, 2.0, 0.0), (-0.5, 1.0, 0.0), (0.0, 0.0, 0.0)]
        cpoints4 = [geom.Vertex(coord) for coord in coords4]
        curve4 = geom.generate_spline(cpoints4, mmath.interp_bezier_curve_2)

        polygon = geom.PolygonsNetQuad((curve1, curve2, curve3, curve4))
        points = list(geom.sample_patch_samples(polygon, 50))

        c1_points = list(geom.sample_curve_samples(curve1, 20))
        c2_points = list(geom.sample_curve_samples(curve2, 20))
        c3_points = list(geom.sample_curve_samples(curve3, 20))
        c4_points = list(geom.sample_curve_samples(curve4, 20))

        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection="3d")
            ax.plot([p.x for p in c1_points], [p.y for p in c1_points], [p.z for p in c1_points])
            ax.plot([p.x for p in c2_points], [p.y for p in c2_points], [p.z for p in c2_points])
            ax.plot([p.x for p in c3_points], [p.y for p in c3_points], [p.z for p in c3_points])
            ax.plot([p.x for p in c4_points], [p.y for p in c4_points], [p.z for p in c4_points])
            ax.plot([p.x for p in points], [p.y for p in points], [p.z for p in points], "o", label="Geometry points")
            plt.show()
Example #3
0
    def test_complex_net(self):
        coords1 = [(0.0, 0.0, 0.0), (0.5, 0.0, 1.0), (1.0, -0.2, 0.5),
                   (1.5, 0.0, 0.0), (2.0, -0.1, 0.5)]
        cpoints1 = [geom.Vertex(coord) for coord in coords1]
        curve1 = geom.generate_spline(cpoints1, mmath.interp_bezier_curve_2)

        coords2 = [(2.0, -0.1, 0.5), (2.0, 1.0, 1.0), (2.1, 2.0, 0.0)]
        cpoints2 = [geom.Vertex(coord) for coord in coords2]
        curve2 = geom.generate_spline(cpoints2, mmath.interp_bezier_curve_2)

        coords3 = [(2.1, 2.0, 0.0), (1.5, 2.0, 0.0), (1.0, 2.1, 0.5),
                   (0.5, 2.0, 1.0), (0.0, 2.0, 0.0)]
        cpoints3 = [geom.Vertex(coord) for coord in coords3]
        curve3 = geom.generate_spline(cpoints3, mmath.interp_bezier_curve_2)

        coords4 = [(0.0, 2.0, 0.0), (-0.5, 1.0, 0.0), (0.0, 0.0, 0.0)]
        cpoints4 = [geom.Vertex(coord) for coord in coords4]
        curve4 = geom.generate_spline(cpoints4, mmath.interp_bezier_curve_2)

        polygon = geom.PolygonsNetQuad((curve1, curve2, curve3, curve4))
        points = list(geom.sample_patch_samples(polygon, 50))

        c1_points = list(geom.sample_curve_samples(curve1, 20))
        c2_points = list(geom.sample_curve_samples(curve2, 20))
        c3_points = list(geom.sample_curve_samples(curve3, 20))
        c4_points = list(geom.sample_curve_samples(curve4, 20))

        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection='3d')
            ax.plot([p.x for p in c1_points], [p.y for p in c1_points],
                    [p.z for p in c1_points])
            ax.plot([p.x for p in c2_points], [p.y for p in c2_points],
                    [p.z for p in c2_points])
            ax.plot([p.x for p in c3_points], [p.y for p in c3_points],
                    [p.z for p in c3_points])
            ax.plot([p.x for p in c4_points], [p.y for p in c4_points],
                    [p.z for p in c4_points])
            ax.plot([p.x for p in points], [p.y for p in points],
                    [p.z for p in points],
                    "o",
                    label="Geometry points")
            plt.show()
Example #4
0
    def test_simple_net(self):
        v0 = geom.Vertex((0.0, 0.0, 0.0))
        v1 = geom.Vertex((1.0, 0.0, 0.0))
        v2 = geom.Vertex((1.0, 1.0, 0.0))
        v3 = geom.Vertex((0.0, 1.0, 0.0))

        disp = geom.Vertex((0.0, 0.0, 0.5))

        m0 = (v0 + v1) / 2.0 + disp
        m1 = (v1 + v2) / 2.0 + disp
        m2 = (v2 + v3) / 2.0 + disp
        m3 = (v3 + v0) / 2.0 + disp

        c1 = geom.SuperCurve(
            [geom.BaseCurve((v0, m0, v1), mmath.interp_bezier_curve_2)])
        c2 = geom.BaseCurve((v1, m1, v2), mmath.interp_bezier_curve_2)
        c3 = geom.BaseCurve((v2, m2, v3), mmath.interp_bezier_curve_2)
        c4 = geom.BaseCurve((v3, m3, v0), mmath.interp_bezier_curve_2)

        c1_points = list(geom.sample_curve_samples(c1, 10))
        c2_points = list(geom.sample_curve_samples(c2, 10))
        c3_points = list(geom.sample_curve_samples(c3, 10))
        c4_points = list(geom.sample_curve_samples(c4, 10))

        polygon = geom.PolygonsNetQuad((c1, c2, c3, c4))
        points = list(geom.sample_patch_samples(polygon, 70))

        if DRAW_GRAPHS:
            fig = plt.figure()
            ax = fig.gca(projection='3d')
            ax.plot([p.x for p in c1_points], [p.y for p in c1_points],
                    [p.z for p in c1_points])
            ax.plot([p.x for p in c2_points], [p.y for p in c2_points],
                    [p.z for p in c2_points])
            ax.plot([p.x for p in c3_points], [p.y for p in c3_points],
                    [p.z for p in c3_points])
            ax.plot([p.x for p in c4_points], [p.y for p in c4_points],
                    [p.z for p in c4_points])
            ax.plot([p.x for p in points], [p.y for p in points],
                    [p.z for p in points],
                    "o",
                    label="Geometry points")
            plt.show()
Example #5
0
    def test_curve2_fitting(self):
        A = geom.Vertex((0.0, 0.0, 0.0))
        B = geom.Vertex((0.5, 2.0, 0.0))
        C = geom.Vertex((1.0, 2.0, 1.0))
        curve = geom.BaseCurve((A, B, C), mmath.interp_bezier_curve_2)

        points = list(geom.sample_curve_samples(curve, 100))
        vals = fit.fit_bezier_curve(points, mmath.interp_bezier_curve_2)

        self.assertTrue(np.allclose(np.array(vals), np.array([A, B, C])))

        if HUMAN_READABLE_TESTS:
            print(DELIMETER)
            print(vals)
            print([A, B, C])
            print(DELIMETER)
Example #6
0
    def test_curve2_fitting(self):
        A = geom.Vertex((0.0, 0.0, 0.0))
        B = geom.Vertex((0.5, 2.0, 0.0))
        C = geom.Vertex((1.0, 2.0, 1.0))
        curve = geom.BaseCurve((A, B, C), mmath.interp_bezier_curve_2)

        points = list(geom.sample_curve_samples(curve, 100))
        vals = fit.fit_bezier_curve(points, mmath.interp_bezier_curve_2)

        self.assertTrue(np.allclose(np.array(vals), np.array([A, B, C])))

        if HUMAN_READABLE_TESTS:
            print(DELIMETER)
            print(vals)
            print([A, B, C])
            print(DELIMETER)
def plot_bezier_curve(points, function, figname):
    points_xs = [p[0] for p in points]
    points_ys = [p[1] for p in points]
    
    cpoints = fit.fit_bezier_curve(list(zip(points_xs, points_ys, [0] * len(points_xs))), function)
    cpoints_xs = [p[0] for p in cpoints]
    cpoints_ys = [p[1] for p in cpoints]
    
    curve = geom.BaseCurve([geom.Vertex(p) for p in cpoints], function)
    curve_points = list(geom.sample_curve_samples(curve, 100))
    curve_xs = [p.x for p in curve_points]
    curve_ys = [p.y for p in curve_points]
    
    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 curve")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    plt.show()
def plot_bezier_curve(points, function, figname):
    points_xs = [p[0] for p in points]
    points_ys = [p[1] for p in points]

    cpoints = fit.fit_bezier_curve(
        list(zip(points_xs, points_ys, [0] * len(points_xs))), function)
    cpoints_xs = [p[0] for p in cpoints]
    cpoints_ys = [p[1] for p in cpoints]

    curve = geom.BaseCurve([geom.Vertex(p) for p in cpoints], function)
    curve_points = list(geom.sample_curve_samples(curve, 100))
    curve_xs = [p.x for p in curve_points]
    curve_ys = [p.y for p in curve_points]

    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 curve")
    plt.grid()
    plt.legend()
    plt.savefig(OUT_GRAPH_DIR + figname)
    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
Example #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