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 offset_polygon_xy(points, dist, planarize=False): if len(points) < 3: return None frame = Frame.from_plane( Plane.from_three_points(points[0], points[1], points[2])) xform = Transformation.from_frame_to_frame(frame, Frame.worldXY()) if planarize: points = [point_on_plane(point, frame) for point in points] points = transform_points(points, xform) points = offset_polygon(points, dist) points = transform_points(points, xform.inverse()) return points
def test_offset_polygon(polygon, distance, tol, output_polygon): output_polygon = [v for v in output_polygon] assert allclose(offset_polygon(polygon, distance, tol), output_polygon)
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) }) POLYGONS.append({ 'points': [box1[1], box2[1], box2[2], box1[2], box1[1]], 'color': (0, 0, 0) })
# path = os.path.join(DATA, 'FABRIC', 'unrolled', SIDE, "{}.json".format(mesh.attributes['name'])) # mesh.to_json(path) # for mesh in NW_unrolled: # path = os.path.join(DATA, 'FABRIC', 'unrolled', SIDE, "{}.json".format(mesh.attributes['name'])) # mesh.to_json(path) # ============================================================================== # Visualize # ============================================================================== ARTIST = ShellArtist(None) for mesh in SOUTH_unrolled: points = [ mesh.vertex_coordinates(key) for key in mesh.vertices_on_boundary(ordered=True) ] polygon = offset_polygon(points, SEEM) polygons = [{'points': polygon + polygon[:1]}] ARTIST.mesh = mesh ARTIST.layer = "Unrolled::{}::{}".format(SIDE, mesh.attributes['name']) ARTIST.clear_layer() ARTIST.draw_faces() ARTIST.draw_facelabels(text={ key: "{}".format(attr['count']) for key, attr in mesh.faces(True) }) ARTIST.draw_polygons(polygons)
fkey = mesh.get_any_face() panel = mesh.get_face_attribute(fkey, 'panel') strip = mesh.get_face_attribute(fkey, 'strip') mesh.attributes['name'] = '{}-{}'.format(panel, strip.zfill(2)) # ============================================================================== # Visualize # ============================================================================== mesh = RING_unrolled[3] points = [ mesh.vertex_coordinates(key) for key in mesh.vertices_on_boundary(ordered=True) ] polygons = [{'points': offset_polygon(points, SEEM)}] fkey = list(mesh.faces_where({'count': 0}))[0] facecolor = {fkey: COLOR} PLOTTER = MeshPlotter(mesh, figsize=(10, 7)) PLOTTER.draw_polygons(polygons) PLOTTER.draw_faces(text={ fkey: "{}".format(str(attr['count']).zfill(2)) for fkey, attr in mesh.faces(True) }, facecolor=facecolor) PLOTTER.draw_edges() PLOTTER.show() # ==============================================================================
# make a block for every face # leave a 3cm gap between adjacent blocks blocks = [] for fkey in edos.faces(): vertices = edos.face_vertices(fkey) if len(vertices) != 4: continue # normals of the cablenet # coordinates of the extrados normals = [mesh.vertex_normal(key) for key in vertices] points = [edos.vertex_coordinates(key) for key in vertices] # bottom face coordinates offset from vertex coordinates bottom = offset_polygon(points, 0.015) top = [] for point, normal in zip(bottom, normals): x = point[0] + 0.1 * normal[0] y = point[1] + 0.1 * normal[1] z = point[2] + 0.1 * normal[2] top.append([x, y, z]) # vertices and faces of the block vertices = bottom[::-1] + top faces = [[0, 1, 2, 3], [4, 5, 6, 7], [4, 3, 2, 5], [5, 2, 1, 6], [6, 1, 0, 7], [7, 0, 3, 4]] block = Mesh.from_vertices_and_faces(vertices, faces) block.name = "Block.{}".format(fkey)
# # 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() for v in front:
def test_offset_colinear_polygon(polygon, distance, tol, output_polygon): output_polygon = [pytest.approx(v) for v in output_polygon] assert offset_polygon(polygon, distance, tol) == output_polygon
# # ============================================================================== # # Generate the formwork blocks. # # ============================================================================== blocks = [] for fkey in cablenet.faces(): vertices = cablenet.face_vertices(fkey) points = cablenet.get_vertices_attributes('xyz', keys=vertices) # the edges of the bottom face polygon have to be offset to create space # for the ribs. bottom = offset_polygon(points, OFFSET) # the vertices of the top face are the intersection points of the face normal # placed at each (offset) bottom vertex and a plane perpendicular to the # face normal placed at a distance THICKNESS along the face normal from the # face centroid. # define the plane origin = cablenet.face_centroid(fkey) normal = cablenet.face_normal(fkey, unitized=True) plane = add_vectors(origin, scale_vector(normal, THICKNESS)), normal top = [] for a in bottom: b = add_vectors(a, normal)
def mesh_subdivide_frames(mesh, offset, add_windows=False): """Subdivide a mesh by creating offset frames and windows on its faces. Parameters ---------- mesh : :class:`compas.datastructures.Mesh` The mesh object to be subdivided. offset : float | dict[int, float] The offset distance to create the frames. A single value will result in a constant offset everywhere. A dictionary mapping faces to offset values will be processed accordingly. add_windows : bool, optional If True, add a window face in the frame opening. Returns ------- :class:`compas.datastructures.Mesh` A new subdivided mesh. """ cls = type(mesh) SubdMesh = subd_factory(cls) subd = SubdMesh() # 0. pre-compute offset distances if not isinstance(offset, dict): distances = iterable_like(mesh.faces(), [offset], offset) offset = {fkey: od for fkey, od in zip(mesh.faces(), distances)} # 1. add vertices newkeys = {} for vkey, attr in mesh.vertices(True): newkeys[vkey] = subd.add_vertex(*mesh.vertex_coordinates(vkey)) # 2. add faces for fkey in mesh.faces(): face = [newkeys[vkey] for vkey in mesh.face_vertices(fkey)] d = offset.get(fkey) # 2a. add face and break if no offset is found if d is None: subd.add_face(face) continue polygon = offset_polygon(mesh.face_coordinates(fkey), d) # 2a. add offset vertices window = [] for xyz in polygon: x, y, z = xyz new_vkey = subd.add_vertex(x=x, y=y, z=z) window.append(new_vkey) # 2b. frame faces face = face + face[:1] window = window + window[:1] for sa, sb in zip(pairwise(face), pairwise(window)): subd.add_face([sa[0], sa[1], sb[1], sb[0]]) # 2c. window face if add_windows: subd.add_face(window) return cls.from_data(subd.data)
for fkey in cablenet.faces(): #fkey = cablenet.get_any_face() vertices = cablenet.face_vertices(fkey) points = cablenet.get_vertices_attributes('xyz', keys=vertices) normals = [cablenet.vertex_normal(key) for key in vertices] bottom = points[:] top = [] for point, normal in zip(points, normals): xyz = add_vectors(point, scale_vector(normal, OFFSET)) top.append(xyz) bottom = offset_polygon(bottom, 0.01) top = offset_polygon(top, 0.05) vertices = bottom + top faces = [[0, 3, 2, 1], [4, 5, 6, 7], [3, 0, 4, 7], [0, 1, 5, 4], [1, 2, 6, 5], [1, 2, 6, 5], [2, 3, 7, 6]] block = Mesh.from_vertices_and_faces(vertices, faces) blocks.append(block) # ============================================================================== # Visualize # ============================================================================== flag = False for i in blocks:
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)
# Make block # ============================================================================== blocks = [] all_vertices = [] all_faces = [] for fkey in cablenet.faces(): #fkey = cablenet.get_any_face() vertices = cablenet.face_vertices(fkey) points = cablenet.get_vertices_attributes("xyz", keys=vertices) polygon = cg. Polygon(points) offset_polygon = cg.offset_polygon(points, OFFSET) normals= [cablenet.vertex_normal(key) for key in vertices] bottom = offset_polygon[:] top = [] for point, normal in zip(offset_polygon, normals): xyz = add_vectors(point, scale_vector(normal, OFFSET)) top.append(xyz) vertices= bottom+top faces = [[0, 3, 2, 1], [4, 5, 6, 7], [4, 0, 1, 5], [2, 6, 5, 1], [6, 2, 3, 7], [0, 4, 7, 3]] block = Mesh.from_vertices_and_faces(vertices, faces) blocks.append(block) all_vertices.extend(vertices)
def test_offset_colinear_polygon(polygon, distance, tol, output_polygon): assert allclose(offset_polygon(polygon, distance, tol), output_polygon)
# 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() artist.draw()
def mesh_subdivide_frames(mesh, offset, add_windows=False): """Subdivide a mesh by creating offset frames and windows on its faces. Parameters ---------- mesh : Mesh The mesh object to be subdivided. offset : float or dict The offset distance to create the frames. A single value will result in a constant offset everywhere. A dictionary mapping facekey: offset will be processed accordingly. add_windows : boolean Optional. Flag to add window face. Default is ``False``. Returns ------- Mesh A new subdivided mesh. Examples -------- >>> """ subd = SubdMesh() # 0. pre-compute offset distances if not isinstance(offset, dict): distances = iterable_like(mesh.faces(), [offset], offset) offset = {fkey: od for fkey, od in zip(mesh.faces(), distances)} # 1. add vertices newkeys = {} for vkey, attr in mesh.vertices(True): newkeys[vkey] = subd.add_vertex(*mesh.vertex_coordinates(vkey)) # 2. add faces for fkey in mesh.faces(): face = [newkeys[vkey] for vkey in mesh.face_vertices(fkey)] d = offset.get(fkey) # 2a. add face and break if no offset is found if d is None: subd.add_face(face) continue polygon = offset_polygon(mesh.face_coordinates(fkey), d) # 2a. add offset vertices window = [] for xyz in polygon: x, y, z = xyz new_vkey = subd.add_vertex(x=x, y=y, z=z) window.append(new_vkey) # 2b. frame faces face = face + face[:1] window = window + window[:1] for sa, sb in zip(pairwise(face), pairwise(window)): subd.add_face([sa[0], sa[1], sb[1], sb[0]]) # 2c. window face if add_windows: subd.add_face(window) return subd
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]