Ejemplo n.º 1
0
def create_planar_paths(mesh, planes):
    """
    Creates planar contours. Does not rely on external libraries.
    It is currently the only method that can return identify OPEN versus CLOSED paths.

    Parameters
    ----------
    mesh: :class: 'compas.datastructures.Mesh'
        The mesh to be sliced
    planes: list, :class: 'compas.geometry.Plane'
    """

    layers = []

    with progressbar.ProgressBar(max_value=len(planes)) as bar:
        for i, plane in enumerate(planes):

            intersection = PlanarContours(mesh, plane)
            intersection.compute()

            paths = []
            if len(intersection.sorted_point_clusters
                   ) > 0 and intersection.is_valid:
                for key in intersection.sorted_point_clusters:
                    is_closed = intersection.closed_paths_booleans[key]
                    path = Path(points=intersection.sorted_point_clusters[key],
                                is_closed=is_closed)
                    paths.append(path)

                layers.append(Layer(paths))

            bar.update(i)

    return layers
Ejemplo n.º 2
0
    def add_to_vertical_layers_manager(self, vertical_layers_manager):
        for key in self.sorted_point_clusters:
            pts = self.sorted_point_clusters[key]
            if len(pts) > 3:  # discard curves that are too small
                path = Path(pts, is_closed=self.closed_paths_booleans[key])

                vertical_layers_manager.add(path)
Ejemplo n.º 3
0
def create_overhang_texture(slicer, overhang_distance):
    """Creates a cool overhang texture"""

    print("Creating cool texture")

    for i, layer in enumerate(slicer.layers):
        if i % 10 == 0 and i > 0:
            # for every 10th layer, except for the brim
            # print(layer)
            for j, path in enumerate(layer.paths):
                # print(path)
                # create an empty layer in which we can store our modified points
                new_path = []
                for k, pt in enumerate(path.points):
                    # for every second point (only even points)
                    if k % 2 == 0:
                        # get the normal of the point in relation to the mesh
                        normal = get_normal_of_path_on_xy_plane(k,
                                                                pt,
                                                                path,
                                                                mesh=None)
                        # scale the vector by a number to move the point
                        normal_scaled = scale_vector(normal,
                                                     -overhang_distance)
                        # create a new point by adding the point and the normal vector
                        new_pt = add_vectors(pt, normal_scaled)
                        # recreate the new_pt values as compas_points
                        pt = Point(new_pt[0], new_pt[1], new_pt[2])

                    # append the points to the new path
                    new_path.append(pt)

                # replace the current path with the new path that we just created
                layer.paths[j] = Path(new_path, is_closed=path.is_closed)
def create_planar_paths_numpy(mesh, min_z, max_z, planes):
    """
    Creates planar contours using the compas mesh_contours_numpy function. To be replaced with a better alternative

    Considers all resulting paths as CLOSED paths

    Parameters
    ----------
    mesh : compas.datastructures.Mesh
        A compas mesh.
    min_z: float
    max_z: float
    planes: list, compas.geometry.Plane
    """

    # initializes progress_bar for measuring progress
    progress_bar = Bar('Slicing', max=len(planes), suffix='Layer %(index)i/%(max)i - %(percent)d%%')

    levels = [plane.point[2] for plane in planes]

    levels, np_layers = compas.datastructures.mesh_contours_numpy(mesh, levels=levels, density=20)


    layers = []

    for i, layer in enumerate(np_layers):

        for path in layer:
            paths_per_layer = []
            for polygon2d in path:
                points = [Point(p[0], p[1], levels[i]) for p in polygon2d[:-1]]


                if len(points) > 0:
                    # threshold_closed = 25.0  # TODO: Threshold should not be hardcoded
                    # is_closed = distance_point_point(points[0], points[-1]) < threshold_closed

                    # print_points = [PrintPoint(pt=p, layer_height=layer_height) for p in points]
                    path = Path(points=points, is_closed=True)

                    paths_per_layer.append(path)
            l = Layer(paths_per_layer)
            layers.append(l)

        # advance progressbar
        progress_bar.next()

    # finish progressbar
    progress_bar.finish()

    return layers
Ejemplo n.º 5
0
    def get_layer_ppts(self, layer, base_boundary):
        """ Creates the PrintPoints of a single layer."""
        max_layer_height = get_param(self.parameters,
                                     key='max_layer_height',
                                     defaults_type='layers')
        min_layer_height = get_param(self.parameters,
                                     key='min_layer_height',
                                     defaults_type='layers')
        avg_layer_height = get_param(self.parameters, 'avg_layer_height',
                                     'layers')

        all_pts = [pt for path in layer.paths for pt in path.points]
        closest_fks, projected_pts = utils.pull_pts_to_mesh_faces(
            self.slicer.mesh, all_pts)
        normals = [
            Vector(*self.slicer.mesh.face_normal(fkey)) for fkey in closest_fks
        ]

        count = 0
        crv_to_check = Path(
            base_boundary.points,
            True)  # creation of fake path for the lower boundary

        layer_ppts = {}
        for i, path in enumerate(layer.paths):
            layer_ppts['path_%d' % i] = []

            for p in path.points:
                cp = closest_point_on_polyline(p,
                                               Polyline(crv_to_check.points))
                d = distance_point_point(cp, p)

                ppt = PrintPoint(pt=p,
                                 layer_height=avg_layer_height,
                                 mesh_normal=normals[count])

                ppt.closest_support_pt = Point(*cp)
                ppt.distance_to_support = d
                ppt.layer_height = max(min(d, max_layer_height),
                                       min_layer_height)
                ppt.up_vector = Vector(
                    *normalize_vector(Vector.from_start_end(cp, p)))
                ppt.frame = ppt.get_frame()

                layer_ppts['path_%d' % i].append(ppt)
                count += 1

            crv_to_check = path

        return layer_ppts
Ejemplo n.º 6
0
    def from_data(cls, data):
        """Construct a vertical layer from its data representation.

        Parameters
        ----------
        data: dict
            The data dictionary.

        Returns
        -------
        layer
            The constructed vertical layer.
        """
        paths_data = data['paths']
        paths = [Path.from_data(paths_data[key]) for key in paths_data]
        layer = cls(id=None)
        layer.paths = paths
        layer.min_max_z_height = data['min_max_z_height']
        return layer
Ejemplo n.º 7
0
    def from_data(cls, data):
        """Construct a layer from its data representation.

        Parameters
        ----------
        data: dict
            The data dictionary.

        Returns
        -------
        layer
            The constructed layer.
        """
        paths_data = data['paths']
        paths = [Path.from_data(paths_data[key]) for key in paths_data]
        layer = cls(paths=paths)
        layer.is_brim = data['is_brim']
        layer.number_of_brim_offsets = data['number_of_brim_offsets']
        layer.min_max_z_height = data['min_max_z_height']
        return layer
Ejemplo n.º 8
0
def create_planar_paths_igl(mesh, n):
    """
    Creates planar contours using the libigl function: https://libigl.github.io/libigl-python-bindings/igl_docs/#isolines
    TODO: ??? It is currently the only method that can return identify OPEN versus CLOSED paths.

    Parameters
    ----------
    mesh: :class: 'compas.datastructures.Mesh'
        The mesh to be sliced
    n: number of contours
    """
    utils.check_package_is_installed('igl')
    import igl
    v, f = mesh.to_vertices_and_faces()

    ds = np.array([vertex[2] for vertex in v])
    # save distances of each vertex from the floor: this will be the scalar field for the slicing operation.

    # --- generate disconnected segments of isolines using the libigl function
    v = np.array(v)
    f = np.array(f)
    isoV, isoE = igl.isolines(v, f, ds, n)

    # --- group resulting segments per level
    print('Grouping segments per level')
    segments_per_level = {}
    tol = 1e-6
    for e in isoE:
        v0 = isoV[e[0]]
        v1 = isoV[e[1]]
        assert(abs(v0[2] - v1[2]) < tol)
        h = v0[2]

        found = False
        for key in segments_per_level:
            if abs(key - h) < tol:
                if e[0] != e[1]:  # do not add null segments
                    segments_per_level[key].append(e)
                    found = True

        if not found:
            segments_per_level[h] = [e]

    #assert(len(segments_per_level) == n)

    utils.save_to_json(utils.point_list_to_dict(isoV), "/examples/1_planar_slicing_simple/data/output", 'isoV.json')

    sorted_keys = [key for key in segments_per_level]
    sorted(sorted_keys)

    layers = []

    # --- Create connectivity graph G for every group, and sort edges based on G
    with progressbar.ProgressBar(max_value=len(segments_per_level)) as bar:
        for index, key in enumerate(sorted_keys):
            #print(' Creating connectivity for level z = ', key)

            es = segments_per_level[key]  # list of the current edges, which whose indices we are working below

            G = nx.Graph()

            connections_found = [[False, False] for _ in es]

            for i, e in enumerate(es):
                G.add_node(i)  # node, attribute

            for i, ei in enumerate(es):  # ei here is the edge, not the index!
                for side_i in range(2):
                    if not connections_found[i][side_i]:
                        for jj, ej in enumerate(es[i+1:]):  # ej here is the edge, not the index!
                            j = i + jj + 1
                            for side_j in range(2):
                                if not connections_found[j][side_j]:
                                    vi = isoV[ei[side_i]]
                                    vj = isoV[ej[side_j]]
                                    if distance_point_point_sqrd(vi, vj) < tol:
                                        G.add_edge(i, j)
                                        connections_found[i][side_i] = True
                                        connections_found[j][side_j] = True

            # sort connected components of G
            sorted_indices_dict = sort_graph_connected_components(G)

            # for i, s_key in enumerate(sorted_indices_dict):
            #     sorted_eis = sorted_indices_dict[s_key]
            #     this_segment_pts = []
            #
            #     for j, ei in enumerate(sorted_eis):
            #         e = es[ei]
            #         # segment pts
            #         for k in range(2):
            #             this_segment_pts.append(isoV[e[k]])
            #
            #     utils.save_to_json(utils.point_list_to_dict(this_segment_pts),
            #                        "C:/dev/compas_slicer/examples/1_planar_slicing_simple/data/output", 'isoV.json')
            #     print('saved')

            # get points list from sorted edge indices (for each connected component)
            paths_pts = [[] for _ in sorted_indices_dict]
            are_closed = [True for _ in sorted_indices_dict]
            for i, s_key in enumerate(sorted_indices_dict):
                sorted_indices = sorted_indices_dict[s_key]

                for j, s_ei in enumerate(sorted_indices):
                    v0 = isoV[es[s_ei][0]]
                    v1 = isoV[es[s_ei][1]]
                    if j == 0:
                        s_ei_next = sorted_indices[j+1]
                        v0_next = isoV[es[s_ei_next][0]]
                        v1_next = isoV[es[s_ei_next][1]]
                        v_mid_next = (Point(*v0_next) + Point(*v1_next)) * 0.5
                        if distance_point_point_sqrd(v0, v_mid_next) < distance_point_point_sqrd(v1, v_mid_next):
                            paths_pts[i].extend([Point(*v1), Point(*v0)])
                        else:
                            paths_pts[i].extend([Point(*v0), Point(*v1)])

                    else:
                        v_prev = paths_pts[i][-1]
                        if distance_point_point_sqrd(v0, v_prev) < distance_point_point_sqrd(v1, v_prev):
                            paths_pts[i].append(Point(*v1))
                        else:
                            paths_pts[i].append(Point(*v0))

                if distance_point_point_sqrd(paths_pts[i][0], paths_pts[i][1]) > tol:
                    are_closed[i] = False

            paths = []
            for i in range(len(sorted_indices_dict)):
                paths.append(Path(points=paths_pts[i], is_closed=are_closed[i]))

            layers.append(Layer(paths))
            bar.update(index)

    return layers
Ejemplo n.º 9
0
def generate_raft(slicer,
                  raft_offset=10,
                  distance_between_paths=10,
                  direction="xy_diagonal",
                  raft_layers=1,
                  raft_layer_height=None):
    """Creates a raft.

    Parameters
    ----------
    slicer: :class:`compas_slicer.slicers.BaseSlicer`
        An instance of one of the compas_slicer.slicers.BaseSlicer classes.
    raft_offset: float
        Distance (in mm) that the raft should be offsetted from the first layer. Defaults to 10mm
    distance_between_paths: float
        Distance (in mm) between the printed lines of the raft. Defaults to 10mm
    direction: str
        x_axis: Create a raft aligned with the x_axis
        y_axis: Create a raft aligned with the y_axis
        xy_diagonal: Create a raft int the diagonal direction in the xy_plane
    raft_layers: int
        Number of raft layers to add. Defaults to 1
    raft_layer_height: float
        Layer height of the raft layers. Defaults to same value as used in the slicer.
    """

    # check if a raft_layer_height is specified, if not, use the slicer.layer_height value
    if not raft_layer_height:
        raft_layer_height = slicer.layer_height

    logger.info("Generating raft")

    # find if slicer has vertical or horizontal layers, and select which paths are to be offset.
    if isinstance(slicer.layers[0], compas_slicer.geometry.VerticalLayer):  # Vertical layers
        # then find all paths that lie on the print platform and make them brim.
        paths_to_offset, _ = slicer.find_vertical_layers_with_first_path_on_base()

    else:  # Horizontal layers
        # then replace the first layer with a raft layer.
        paths_to_offset = slicer.layers[0].paths

    # get flat lists of points in bottom layer
    all_pts = []
    for path in paths_to_offset:
        for pt in path.points:
            all_pts.append(pt)

    # get xy bounding box of bottom layer and create offset
    bb_xy = bounding_box_xy(all_pts)
    bb_xy_offset = offset_polygon(bb_xy, -raft_offset)
    # bring points in the xy_offset to the correct height
    for pt in bb_xy_offset:
        pt[2] = slicer.layers[0].paths[0].points[0][2]

    # calculate x range, y range, and number of steps
    x_range = abs(bb_xy_offset[0][0] - bb_xy_offset[1][0])
    y_range = abs(bb_xy_offset[0][1] - bb_xy_offset[3][1])

    # get maximum values of the bounding box
    bb_max_x_right = bb_xy_offset[1][0]
    bb_max_y_top = bb_xy_offset[3][1]

    # get point in bottom left corner as raft start point
    raft_start_pt = Point(bb_xy_offset[0][0], bb_xy_offset[0][1], bb_xy_offset[0][2])

    # create starting line for diagonal direction
    if direction == "xy_diagonal":
        c = math.sqrt(2*(distance_between_paths**2))

        pt1 = Point(raft_start_pt[0] + c, raft_start_pt[1], raft_start_pt[2])
        pt2 = Point(pt1[0] - y_range, pt1[1] + y_range, pt1[2])
        line = Line(pt1, pt2)

    # move all points in the slicer up so that raft layers can be inserted
    for i, layer in enumerate(slicer.layers):
        for j, path in enumerate(layer.paths):
            for k, pt in enumerate(path.points):
                slicer.layers[i].paths[j].points[k] = Point(pt[0], pt[1], pt[2] + (raft_layers)*raft_layer_height)

    for i in range(raft_layers):

        iter = 0
        raft_points = []

        # create raft points depending on the chosen direction
        while iter < 9999:  # to avoid infinite while loop in case something is not correct
            # ===============
            # VERTICAL RAFT
            # ===============
            if direction == "y_axis":
                raft_pt1 = Point(raft_start_pt[0] + iter*distance_between_paths, raft_start_pt[1], raft_start_pt[2] + i*raft_layer_height)
                raft_pt2 = Point(raft_start_pt[0] + iter*distance_between_paths, raft_start_pt[1] + y_range, raft_start_pt[2] + i*raft_layer_height)

                if raft_pt2[0] > bb_max_x_right or raft_pt1[0] > bb_max_x_right:
                    break

            # ===============
            # HORIZONTAL RAFT
            # ===============
            elif direction == "x_axis":
                raft_pt1 = Point(raft_start_pt[0], raft_start_pt[1] + iter*distance_between_paths, raft_start_pt[2] + i*raft_layer_height)
                raft_pt2 = Point(raft_start_pt[0] + x_range, raft_start_pt[1] + iter*distance_between_paths, raft_start_pt[2] + i*raft_layer_height)

                if raft_pt2[1] > bb_max_y_top or raft_pt1[1] > bb_max_y_top:
                    break

            # ===============
            # DIAGONAL RAFT
            # ===============
            elif direction == "xy_diagonal":
                # create offset of the initial diagonal line
                offset_l = offset_line(line, iter*distance_between_paths, Vector(0, 0, -1))

                # get intersections for the initial diagonal line with the left and bottom of the bb
                int_left = intersection_line_line(offset_l, [bb_xy_offset[0], bb_xy_offset[3]])
                int_bottom = intersection_line_line(offset_l, [bb_xy_offset[0], bb_xy_offset[1]])

                # get the points at the intersections
                raft_pt1 = Point(int_left[0][0], int_left[0][1], int_left[0][2] + i*raft_layer_height)
                raft_pt2 = Point(int_bottom[0][0], int_bottom[0][1], int_bottom[0][2] + i*raft_layer_height)

                # if the intersection goes beyond the height of the left side of the bounding box:
                if int_left[0][1] > bb_max_y_top:
                    # create intersection with the top side
                    int_top = intersection_line_line(offset_l, [bb_xy_offset[3], bb_xy_offset[2]])
                    raft_pt1 = Point(int_top[0][0], int_top[0][1], int_top[0][2] + i*raft_layer_height)

                    # if intersection goes beyond the length of the top side, break
                    if raft_pt1[0] > bb_max_x_right:
                        break

                # if the intersection goes beyond the length of the bottom side of the bounding box:
                if int_bottom[0][0] > bb_max_x_right:
                    # create intersection with the right side
                    int_right = intersection_line_line(offset_l, [bb_xy_offset[1], bb_xy_offset[2]])
                    raft_pt2 = Point(int_right[0][0], int_right[0][1], int_right[0][2] + i*raft_layer_height)

                    # if intersection goes beyond the height of the right side, break
                    if raft_pt2[1] > bb_xy_offset[2][1]:
                        break

            # append to list alternating
            if iter % 2 == 0:
                raft_points.extend((raft_pt1, raft_pt2))
            else:
                raft_points.extend((raft_pt2, raft_pt1))

            iter += 1

        # create raft layer
        raft_layer = Layer([Path(raft_points, is_closed=False)])
        raft_layer.is_raft = True
        # insert raft layer in the correct position into the slicer
        slicer.layers.insert(i, raft_layer)