def draw_blocks(self, keys=None, facecolor=None, edgecolor=None, edgewidth=None, textcolor=None, fontsize=None): """Draw the blocks of an assembly. Notes ----- The blocks are drawn as the boundaing boxes of their vertices. """ keys = keys or list(self.assembly.vertices()) facecolordict = valuedict(keys, facecolor, self.block_plotter.defaults['face.facecolor']) edgecolordict = valuedict(keys, edgecolor, self.block_plotter.defaults['face.edgecolor']) edgewidthdict = valuedict(keys, edgewidth, self.block_plotter.defaults['face.edgewidth']) textcolordict = valuedict(keys, textcolor, self.block_plotter.defaults['face.textcolor']) fontsizedict = valuedict(keys, fontsize, self.block_plotter.defaults['face.fontsize']) polygons = [] for key, attr in self.assembly.vertices(True): block = self.assembly.blocks[key] xyz = block.get_vertices_attributes('xyz') box = bounding_box_xy(xyz) polygons.append({ 'points': box, 'edgecolor': edgecolordict[key], 'edgewidth': edgewidthdict[key], 'facecolor': facecolordict[key] }) self.draw_polygons(polygons)
def offset_bbox_xy(pts, dist): bbox = pca_numpy(pts) frame1 = Frame(bbox[0], bbox[1][0], bbox[1][1]) xform = Transformation.from_frame_to_frame(frame1, Frame.worldXY()) pts = transform_points(pts, xform) bbox = bounding_box_xy(pts) bbox = offset_polygon(bbox, dist) return bbox, xform
def mesh_bounding_box_xy(mesh): """Compute the (axis aligned) bounding box of a projection of the mesh in the XY plane. Parameters ---------- mesh : compas.datastructures.Mesh The mesh data structure. Returns ------- list of point The 4 corners of the bounding polygon in the XY plane. Examples -------- >>> mesh_bounding_box_xy(mesh) [[0.0, 0.0, 0.0], [10.0, 0.0, 0.0], [10.0, 10.0, 0.0], [0.0, 10.0, 0.0]] """ xyz = mesh.get_vertices_attributes('xyz') return bounding_box_xy(xyz)
def mesh_bounding_box_xy(mesh): """Compute the (axis aligned) bounding box of a projection of the mesh in the XY plane. Parameters ---------- mesh : compas.datastructures.Mesh The mesh data structure. Returns ------- box_xy The bounding box. Examples -------- .. code-block:: python pass """ xyz = mesh.get_vertices_attributes('xyz') return bounding_box_xy(xyz)
def mesh_bounding_box_xy(mesh): """Compute the (axis aligned) bounding box of a projection of the mesh in the XY plane. Parameters ---------- mesh : :class:`compas.datastructures.Mesh` The mesh data structure. Returns ------- list[list[float]] The 4 corners of the bounding polygon in the XY plane. Examples -------- >>> from compas.datastructures import Mesh >>> mesh = Mesh.from_obj(compas.get('faces.obj')) >>> mesh_bounding_box_xy(mesh) [[0.0, 0.0, 0.0], [10.0, 0.0, 0.0], [10.0, 10.0, 0.0], [0.0, 10.0, 0.0]] """ xyz = mesh.vertices_attributes('xyz') return bounding_box_xy(xyz)
origin = result[0][0] xaxis = normalize_vector(result[1][0]) yaxis = normalize_vector(result[1][1]) zaxis = normalize_vector(result[1][2]) frame = Frame(origin, xaxis, yaxis) if yaxis[2] > 0: offset = scale_vector(zaxis, -0.050) else: offset = scale_vector(zaxis, +0.050) points_xy = [ frame.represent_point_in_local_coordinates(point) for point in points ] box_xy = bounding_box_xy(points_xy) box = [ frame.represent_point_in_global_coordinates(corner_xy)[:] for corner_xy in box_xy ] box1 = offset_polygon(box, -0.025) box2 = [add_vectors(point, offset) for point in box1] POLYGONS.append({'points': box1 + box1[:1], 'color': (0, 0, 0)}) POLYGONS.append({'points': box2 + box2[:1], 'color': (0, 0, 0)}) POLYGONS.append({ 'points': [box1[0], box1[3], box2[3], box2[0], box1[0]], 'color': (0, 0, 0) })
# ============================================================================== # Transform the local coordinates to world coordinates to make it an axis-aligned # problem. # ============================================================================== X = Transformation.from_frame_to_frame(frame1, Frame.worldXY()) points = transform_points(points, X) # ============================================================================== # Compute the axis aligned bounding box in world coordinates, ignoring the Z # components of the points. Add some padding to the bounding box to avoid having # vertices on the boundaries of the box. Convert the box back to the local # coordinate system. # ============================================================================== bbox = bounding_box_xy(points) bbox = offset_polygon(bbox, -PADDING) bbox = transform_points(bbox, X.inverse()) # ============================================================================== # Convert the box to a mesh for visualisation. # ============================================================================== bbox = Mesh.from_vertices_and_faces(bbox, [[0, 1, 2, 3]]) # ============================================================================== # Use a frame artist to visualize the boundary frame. # ============================================================================== artist = FrameArtist(frame, layer="SOUTH::Frame", scale=0.3) artist.clear_layer()
def test_bounding_box_xy(coords, expected): assert expected == bounding_box_xy(coords)
# # ============================================================================== # # Transform the local coordinates to world coordinates to make it an axis-aligned # # problem. # # ============================================================================== X = Transformation.from_frame_to_frame(frame, Frame.worldXY()) points = transform_points(points, X) # # ============================================================================== # # Compute the axis aligned bounding box in world coordinates, ignoring the Z # # components of the points. Add some padding to the bounding box to avoid having # # vertices on the boundaries of the box. Convert the box back to the local # # coordinate system. # # ============================================================================== front = bounding_box_xy(points) front = offset_polygon(front, -PADDING) front = transform_points(front, X.inverse()) # # ============================================================================== # # Check if boundary normal and local normal have the same direction, if not reverse # # list of vertices. Create a 3D box by moving the vertices of the found 2D bounding # # box along the z-axis of the boundary coordinate system. # # ============================================================================== back = [] angle = angles_vectors(frame.zaxis, frame_0.zaxis, deg=False) if angle[0] != 0: front.reverse()
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)
def GetBoundingBox(pca_numpy, intersections, PADDING, start, end, frame): points = intersections[start:end] width = 0.1 bbox = [] if ((end - start == 2)): #print("2Points") vector = subtract_vectors(points[0], points[1]) vector90 = cross_vectors(frame.zaxis, vector) X0 = Translation( scale_vector([vector90[0], vector90[1], vector90[2]], width)) X1 = Translation( scale_vector([vector90[0], vector90[1], vector90[2]], -width)) pointsMoved0 = [points[0], points[1]] pointsMoved0 = transform_points(pointsMoved0, X0) pointsMoved1 = [points[0], points[1]] pointsMoved1 = transform_points(pointsMoved1, X1) bbox = [ pointsMoved0[0], pointsMoved0[1], pointsMoved1[1], pointsMoved1[0] ] poly = Polygon(bbox) #print(poly) offset = offset_polygon(poly.points, -PADDING) X = Translation((0, 0, 0)) bbox_ = transform_points(offset, X) # bbox = bbox_ #print(bbox) #print(bbox_) frame_1 = Frame(points[0], vector, vector90) vectorLen = length_vector(vector) vectorSmall = subtract_vectors(bbox[1], bbox[2]) vectorLenSmall = length_vector(vectorSmall) else: #print("N+2Points") origin, axes, values = pca_numpy([list(point) for point in points]) frame_1 = Frame(origin, axes[0], axes[1]) X = Transformation.from_frame_to_frame(frame_1, Frame.worldXY()) points2 = transform_points(points, X) bbox = bounding_box_xy(points2) bbox = offset_polygon(bbox, -PADDING) bbox = transform_points(bbox, X.inverse()) vector = subtract_vectors(bbox[0], bbox[1]) vectorLen = length_vector(vector) vectorSmall = subtract_vectors(bbox[1], bbox[2]) vectorLenSmall = length_vector(vectorSmall) #Unify box pt0 = Point(bbox[0][0], bbox[0][1], bbox[0][2]) pt1 = Point(bbox[1][0], bbox[1][1], bbox[1][2]) pt2 = Point(bbox[2][0], bbox[2][1], bbox[2][2]) pt3 = Point(bbox[3][0], bbox[3][1], bbox[3][2]) pt03 = Point((bbox[0][0] + bbox[3][0]) * 0.5, (bbox[0][1] + bbox[3][1]) * 0.5, (bbox[0][2] + bbox[3][2]) * 0.5) pt12 = Point((bbox[1][0] + bbox[2][0]) * 0.5, (bbox[1][1] + bbox[2][1]) * 0.5, (bbox[1][2] + bbox[2][2]) * 0.5) vectorSmall = normalize_vector(vectorSmall) X0 = Translation(scale_vector(vectorSmall, width * 0.5)) X1 = Translation(scale_vector(vectorSmall, -width * 0.5)) pt01 = transform_points([pt03, pt12], X0) pt23 = transform_points([pt03, pt12], X1) bbox[0] = pt01[0] bbox[1] = pt01[1] bbox[2] = pt23[1] bbox[3] = pt23[0] bbox = Mesh.from_vertices_and_faces(bbox, [[0, 1, 2, 3]]) return [frame_1, bbox, vectorLen, vectorLenSmall]