def plot_structure(structure): eks = [] for ep in structure.element_properties: elements = structure.element_properties[ep].elements elset = structure.element_properties[ep].elset if elements: eks = elements elif elset: eks = structure.sets[elset].selection sec = structure.element_properties[ep].section t = structure.sections[sec].geometry['t'] for ek in eks: nodes = structure.elements[ek].nodes vert = [structure.nodes[nk].xyz() for nk in nodes] n = scale_vector(normalize_vector(normal_polygon(vert)), t / 2.) n_ = scale_vector(n, -1) v_out = [add_vectors(v, n) for v in vert] v_in = [add_vectors(v, n_) for v in vert] s1 = rs.AddSrfPt(v_out) s2 = rs.AddSrfPt(v_in) srfs = [s1, s2] for i in range(len(v_in)): srf = rs.AddSrfPt( [v_in[-i], v_in[-i - 1], v_out[-i - 1], v_out[-i]]) if srf: srfs.append(srf) rs.JoinSurfaces(srfs, delete_input=True)
def bmesh_face_normals(bmesh): """ Retrieve the face normals of a Blender mesh. Parameters: bmesh (obj): Blender mesh object. Returns: list: Normals of each face. """ vertices, edges, faces = bmesh_data(bmesh) polygons = [[vertices[i] for i in face] for face in faces] normals = [normal_polygon(polygon) for polygon in polygons] return normals
def offset_polygon(polygon, distance, tol=1e-6): """Offset a polygon (closed) by a distance. Parameters ---------- polygon : sequence[point] | :class:`compas.geometry.Polygon` The XYZ coordinates of the corners of the polygon. The first and last coordinates must not be identical. distance : float | list[tuple[float, float]] The offset distance as float. A single value determines a constant offset globally. A list of pairs of local offset values per line segment can be used to create variable offsets. tol : float, optional A tolerance value for intersection calculations. Returns ------- list[[float, float, float]] The XYZ coordinates of the corners of the offset polygon. The first and last coordinates are identical. Notes ----- The offset direction is determined by the normal of the polygon. If the polygon is in the XY plane and the normal is along the positive Z axis, positive offset distances will result in an offset towards the inside of the polygon. The algorithm works also for spatial polygons that do not perfectly fit a plane. Examples -------- >>> """ normal = normal_polygon(polygon) if not is_item_iterable(distance): distance = [distance] distances = iterable_like(polygon, distance, distance[-1]) polygon = polygon + polygon[:1] segments = offset_segments(polygon, distances, normal) offset = [] for s1, s2 in pairwise(segments[-1:] + segments): point = intersect(s1, s2, tol) offset.append(point) return offset
def face_normal(self, face, unitized=True): """Compute the oriented normal of a face. Parameters ---------- face : int The identifier of the face. unitized : bool, optional If True, unitize the normal vector. Returns ------- list[float] The components of the normal vector. """ return normal_polygon(self.face_coordinates(face), unitized=unitized)
def face_normal(self, fkey, unitized=True): """Compute the normal of a face. Parameters ---------- fkey : int The identifier of the face. unitized : bool, optional Unitize the normal vector. Default is ``True``. Returns ------- list The components of the normal vector. """ return normal_polygon(self.face_coordinates(fkey), unitized=unitized)
def in_polyhedron(self, polyhedron): """Determine if the point lies inside the given polyhedron. Parameters ---------- polyhedron : [vertices, faces] or :class:`compas.geometry.Polyhedron`. The polyhedron. Returns ------- bool True, if the point lies on the polyline. False, otherwise. """ vertices, faces = polyhedron polygons = [[vertices[index] for index in face] for face in faces] planes = [[centroid_points(polygon), normal_polygon(polygon)] for polygon in polygons] return all(is_point_behind_plane(self, plane) for plane in planes)
def face_normal(self, fkey, normalized=True): """Return the normal of a face.""" return normal_polygon(self.face_coordinates(fkey), normalized=normalized)
def offset_polygon(polygon, distance): """Offset a polygon (closed) by a distance. Parameters ---------- polygon : list of point The XYZ coordinates of the corners of the polygon. The first and last coordinates must be identical. distance : float or list of tuples of floats The offset distance as float. A single value determines a constant offset globally. Alternatively, pairs of local offset values per line segment can be used to create variable offsets. Distance > 0: offset to the outside, distance < 0: offset to the inside. Returns ------- offset polygon : list of point The XYZ coordinates of the corners of the offset polygon. The first and last coordinates are identical. Notes ----- The offset direction is determined by the normal of the polygon. The algorithm works also for spatial polygons that do not perfectly fit a plane. Examples -------- .. code-block:: python polygon = [ (0.0, 0.0, 0.0), (3.0, 0.0, 1.0), (3.0, 3.0, 2.0), (1.5, 1.5, 2.0), (0.0, 3.0, 1.0), (0.0, 0.0, 0.0) ] distance = 0.5 # constant offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) distance = [ (0.1, 0.2), (0.2, 0.3), (0.3, 0.4), (0.4, 0.3), (0.3, 0.1) ] # variable offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) """ normal = normal_polygon(polygon) if isinstance(distance, list) or isinstance(distance, tuple): distances = distance if len(distances) < len(polygon): distances = distances + [distances[-1] ] * (len(polygon) - len(distances) - 1) else: distances = [[distance, distance]] * len(polygon) lines = [polygon[i:i + 2] for i in range(len(polygon[:-1]))] lines_offset = [] for i, line in enumerate(lines): lines_offset.append(offset_line(line, distances[i], normal)) polygon_offset = [] for i in range(len(lines_offset)): intx_pt1, intx_pt2 = intersection_line_line(lines_offset[i - 1], lines_offset[i]) if intx_pt1 and intx_pt2: polygon_offset.append(centroid_points([intx_pt1, intx_pt2])) else: polygon_offset.append(lines_offset[i][0]) polygon_offset.append(polygon_offset[0]) return polygon_offset
def offset_polygon(polygon, distance, tol=1e-6): """Offset a polygon (closed) by a distance. Parameters ---------- polygon : list of point The XYZ coordinates of the corners of the polygon. The first and last coordinates must not be identical. distance : float or list of float The offset distance as float. A single value determines a constant offset globally. Alternatively, pairs of local offset values per line segment can be used to create variable offsets. Distance > 0: offset to the outside, distance < 0: offset to the inside. Returns ------- offset polygon : list of point The XYZ coordinates of the corners of the offset polygon. The first and last coordinates are identical. Notes ----- The offset direction is determined by the normal of the polygon. If the polygon is in the XY plane and the normal is along the positive Z axis, positive offset distances will result in an offset towards the inside of the polygon. The algorithm works also for spatial polygons that do not perfectly fit a plane. Examples -------- .. code-block:: python polygon = [ (0.0, 0.0, 0.0), (3.0, 0.0, 1.0), (3.0, 3.0, 2.0), (1.5, 1.5, 2.0), (0.0, 3.0, 1.0), (0.0, 0.0, 0.0) ] distance = 0.5 # constant offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) distance = [ (0.1, 0.2), (0.2, 0.3), (0.3, 0.4), (0.4, 0.3), (0.3, 0.1) ] # variable offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) """ normal = normal_polygon(polygon) if not is_item_iterable(distance): distance = [distance] distances = iterable_like(polygon, distance, distance[-1]) polygon = polygon + polygon[:1] segments = offset_segments(polygon, distances, normal) offset = [] for s1, s2 in pairwise(segments[-1:] + segments): point = intersect(s1, s2, tol) offset.append(point) return offset
poly = BRepBuilderAPI_MakePolygon() for point in points: poly.Add(point) poly.Build() poly.Close() # ============================================================================== # BRep Filling # ============================================================================== edges = list(TopologyExplorer(poly.Wire()).edges()) nsided = BRepFill_Filling() for edge in edges: nsided.Add(edge, GeomAbs_C0) nsided.Add(gp_Pnt(*(polygon.centroid + normal_polygon(polygon)))) nsided.Build() # ============================================================================== # Surface from BRep Filling Face # ============================================================================== face = nsided.Face() surface = OCCNurbsSurface.from_face(face) # ============================================================================== # BRep # ============================================================================== brep = BRep() brep.shape = face
def conforming(patch_decomposition, delaunay_mesh, medial_branches, boundary_polylines, edges_to_polyline, feature_points=[], feature_polylines=[]): # convert tri faces [a, b, c] into quad faces [a, b, c, c] # collect pole locations poles = [] poles += [ geometric_key([float(x), float(y), float(z)]) for x, y, z in feature_points ] for polyline in feature_polylines: start = [float(i) for i in polyline[0]] end = [float(i) for i in polyline[-1]] poles += [geometric_key(start), geometric_key(end)] # modify tri faces into quad faces with a double vertex as pole point for fkey in patch_decomposition.faces(): face_vertices = patch_decomposition.face_vertices(fkey) if len(face_vertices) == 3: # find pole location pole = None for vkey in face_vertices: geom_key = geometric_key( patch_decomposition.vertex_coordinates(vkey)) if geom_key in poles: pole = vkey break # modify face if pole is not None: new_face_vertices = face_vertices[:] idx = new_face_vertices.index(vkey) new_face_vertices.insert(idx, vkey) patch_decomposition.delete_face(fkey) patch_decomposition.add_face(new_face_vertices, fkey) # remove remaining tri faces that are not pseudo quads # loop over boundary vertices boundary_vertices = patch_decomposition.vertices_on_boundary() for vkey in boundary_vertices: vertex_faces = patch_decomposition.vertex_faces(vkey, ordered=True) vertex_neighbours = patch_decomposition.vertex_neighbors(vkey, ordered=True) for fkey in vertex_faces: # apply changes if there is an adjacent tri face (pseudo-quads are already transformed) if len(patch_decomposition.face_vertices(fkey)) == 3: # remove quad faces at each extremity tri_faces = vertex_faces[1:-1] vertex_left = vertex_neighbours[-1] vertex_right = vertex_neighbours[0] new_vertices = [] n = len(tri_faces) m = int(floor((n + 1) / 2)) # add new vertices on the adjacent boundary edges depending on the number of triangles to modify polyline_left = edges_to_polyline[(vertex_left, vkey)] point_left = polyline_point(polyline_left, t=.9, snap_to_point=True) if point_left == polyline_left[-1]: # assuming there is at least 3 points point_left = polyline_left[-2] #if geometric_key(point_left) == geometric_key(patch_decomposition.vertex_coordinates(vkey)): # point_left = patch_decomposition.edge_point(vertex_left, vkey, t = .9) vertices_left = [ line_point([ point_left, patch_decomposition.vertex_coordinates(vkey) ], t=float(i) / float(m)) for i in range(m) ] #vertices_left = [patch_decomposition.edge_point(vertex_left, vkey, t = .9 + float(i) / float(m) * .1) for i in range(m)] vertices_left = [ patch_decomposition.add_vertex(attr_dict={ 'x': x, 'y': y, 'z': z }) for x, y, z in vertices_left ] polyline_right = edges_to_polyline[(vertex_right, vkey)] point_right = polyline_point(polyline_right, t=.9, snap_to_point=True) if point_right == polyline_right[-1]: # assuming there is at least 3 points point_right = polyline_right[-2] #if geometric_key(point_right) == geometric_key(patch_decomposition.vertex_coordinates(vkey)): # point_right = patch_decomposition.edge_point(vertex_right, vkey, t = .9) vertices_right = [ line_point([ point_right, patch_decomposition.vertex_coordinates(vkey) ], t=float(i) / float(m)) for i in range(m) ] #vertices_right = [patch_decomposition.edge_point(vertex_right, vkey, t = .9 + float(i) / float(m) * .1) for i in range(m)] vertices_right = [ patch_decomposition.add_vertex(attr_dict={ 'x': x, 'y': y, 'z': z }) for x, y, z in vertices_right ] vertex_centre = [] # add existing vertex if there is an even number of triangles if n % 2 == 0: vertex_centre = [vkey] vertices = vertices_right + vertex_centre + list( reversed(vertices_left)) # modfiy the triangle face vertices for i, fkey in enumerate(tri_faces): face_vertices = patch_decomposition.face_vertices(fkey)[:] idx = face_vertices.index(vkey) face_vertices.insert(idx, vertices[i + 1]) face_vertices.insert(idx, vertices[i]) del face_vertices[idx + 2 - len(face_vertices)] # update edges_to_polyline b = face_vertices[idx - 3] c = face_vertices[idx - 2] d = face_vertices[idx - 1] a = face_vertices[idx] for u, v in [[a, b], [a, d], [b, c]]: if (u, v) in edges_to_polyline: del edges_to_polyline[(u, v)] if (v, u) in edges_to_polyline: del edges_to_polyline[(v, u)] edges_to_polyline[(u, v)] = [ patch_decomposition.vertex_coordinates(u), patch_decomposition.vertex_coordinates(v) ] edges_to_polyline[(v, u)] = [ patch_decomposition.vertex_coordinates(v), patch_decomposition.vertex_coordinates(u) ] # update faces patch_decomposition.delete_face(fkey) patch_decomposition.add_face(face_vertices, fkey) # update quad face on the left fkey_left = vertex_faces[-1] face_vertices = patch_decomposition.face_vertices(fkey_left)[:] idx = face_vertices.index(vkey) face_vertices[idx] = vertices[-1] patch_decomposition.delete_face(fkey_left) patch_decomposition.add_face(face_vertices, fkey_left) del edges_to_polyline[(vertex_left, vkey)] del edges_to_polyline[(vkey, vertex_left)] idx = polyline_left.index(point_left) edges_to_polyline[(vertex_left, vertices[-1])] = polyline_left[:idx + 1] edges_to_polyline[(vertices[-1], vertex_left)] = list( reversed(polyline_left[:idx + 1])) # update quad face on the right fkey_right = vertex_faces[0] face_vertices = patch_decomposition.face_vertices( fkey_right)[:] idx = face_vertices.index(vkey) face_vertices[idx] = vertices[0] patch_decomposition.delete_face(fkey_right) patch_decomposition.add_face(face_vertices, fkey_right) del edges_to_polyline[(vertex_right, vkey)] del edges_to_polyline[(vkey, vertex_right)] idx = polyline_right.index(point_right) edges_to_polyline[(vertex_right, vertices[0])] = polyline_right[:idx + 1] edges_to_polyline[(vertices[0], vertex_right)] = list( reversed(polyline_right[:idx + 1])) break # subidive low quality faces using reference to initial edge polyline faces = list(patch_decomposition.faces()) while len(faces) > 0: fkey = faces.pop() # face values face_normal = patch_decomposition.face_normal(fkey) face_area = patch_decomposition.face_area(fkey) face_vertices = patch_decomposition.face_vertices(fkey) # collect initial polylines polylines = [] for i in range(len(face_vertices)): # exception for pseudo-quads with poles if face_vertices[i - 1] != face_vertices[i]: polylines.append(edges_to_polyline[(face_vertices[i - 1], face_vertices[i])]) # patch values polygon = [] for polyline in polylines: polygon += polyline[:-1] patch_area = area_polygon(polygon) patch_normal = normal_polygon(polygon) signed_face_area = dot_vectors(face_normal, patch_normal) / abs( dot_vectors(face_normal, patch_normal)) * face_area # if degenerated face compared to patch, modify if signed_face_area < 0.1 * patch_area: for u, v in patch_decomposition.face_halfedges(fkey): # collect vertices if patch_decomposition.is_edge_on_boundary(u, v): w = patch_decomposition.face_vertex_descendant(fkey, v) x = patch_decomposition.face_vertex_descendant(fkey, w) fkey_bis = patch_decomposition.halfedge[x][w] if fkey_bis in faces: faces.remove(fkey_bis) z = patch_decomposition.face_vertex_descendant(fkey_bis, w) y = patch_decomposition.face_vertex_descendant(fkey_bis, z) # add new vertices on polylines xa, ya, za = polyline_point(edges_to_polyline[(u, v)], t=.5, snap_to_point=True) a = patch_decomposition.add_vertex(attr_dict={ 'x': xa, 'y': ya, 'z': za }) xb, yb, zb = polyline_point(edges_to_polyline[(w, x)], t=.5, snap_to_point=True) b = patch_decomposition.add_vertex(attr_dict={ 'x': xb, 'y': yb, 'z': zb }) xc, yc, zc = polyline_point(edges_to_polyline[(z, y)], t=.5, snap_to_point=True) c = patch_decomposition.add_vertex(attr_dict={ 'x': xc, 'y': yc, 'z': zc }) # update edges to polylines # uv -> ua + av (and flipped) polyline_uv = edges_to_polyline[(u, v)] del edges_to_polyline[(u, v)] del edges_to_polyline[(v, u)] idx_a = polyline_uv.index([xa, ya, za]) edges_to_polyline[(u, a)] = polyline_uv[:idx_a + 1] edges_to_polyline[(a, u)] = list( reversed(polyline_uv[:idx_a + 1])) edges_to_polyline[(a, v)] = polyline_uv[idx_a:] edges_to_polyline[(v, a)] = list( reversed(polyline_uv[idx_a:])) # wx -> wb + bx (and flipped) polyline_wx = edges_to_polyline[(w, x)] del edges_to_polyline[(w, x)] del edges_to_polyline[(x, w)] idx_b = polyline_wx.index([xb, yb, zb]) edges_to_polyline[(w, b)] = polyline_wx[:idx_b + 1] edges_to_polyline[(b, w)] = list( reversed(polyline_wx[:idx_b + 1])) edges_to_polyline[(b, x)] = polyline_wx[idx_b:] edges_to_polyline[(x, b)] = list( reversed(polyline_wx[idx_b:])) # zy -> zc + yc (and flipped) polyline_zy = edges_to_polyline[(z, y)] del edges_to_polyline[(z, y)] del edges_to_polyline[(y, z)] idx_c = polyline_zy.index([xc, yc, zc]) edges_to_polyline[(z, c)] = polyline_zy[:idx_c + 1] edges_to_polyline[(c, z)] = list( reversed(polyline_zy[:idx_c + 1])) edges_to_polyline[(c, y)] = polyline_zy[idx_c:] edges_to_polyline[(y, c)] = list( reversed(polyline_zy[idx_c:])) # + ab + bc (and flipped) edges_to_polyline[(a, b)] = [ patch_decomposition.vertex_coordinates(a), patch_decomposition.vertex_coordinates(b) ] edges_to_polyline[(b, a)] = [ patch_decomposition.vertex_coordinates(b), patch_decomposition.vertex_coordinates(a) ] edges_to_polyline[(b, c)] = [ patch_decomposition.vertex_coordinates(b), patch_decomposition.vertex_coordinates(c) ] edges_to_polyline[(c, b)] = [ patch_decomposition.vertex_coordinates(c), patch_decomposition.vertex_coordinates(b) ] # delete faces patch_decomposition.delete_face(fkey) patch_decomposition.delete_face(fkey_bis) # add new faces face_1 = patch_decomposition.add_face([u, a, b, x]) face_2 = patch_decomposition.add_face([a, v, w, b]) face_3 = patch_decomposition.add_face([b, w, z, c]) face_4 = patch_decomposition.add_face([b, c, y, x]) faces += [face_1, face_2, face_3, face_4] break initial_vertices = list(patch_decomposition.vertices()) # propagate across curve features if feature_polylines is not None: # store vertex keys on the polyline feature using the map of geometric keys feature_map = [ geometric_key([float(x), float(y), float(z)]) for polyline in feature_polylines for x, y, z in polyline ] vertices_on_feature = [ vkey for vkey in patch_decomposition.vertices() if geometric_key( patch_decomposition.vertex_coordinates(vkey)) in feature_map ] # dictionary mapping vertex geometric keys to vertex indices vertex_map = { geometric_key(patch_decomposition.vertex_coordinates(vkey)): vkey for vkey in patch_decomposition.vertices() } count = patch_decomposition.number_of_faces() * 100 while count > 0 and not patch_decomposition.is_quadmesh(): count -= 1 for fkey in patch_decomposition.faces(): face_vertices = patch_decomposition.face_vertices(fkey) if len(face_vertices) > 4: regular_vertices = [] for i in range(len(face_vertices)): a = face_vertices[i - 2] b = face_vertices[i - 1] c = face_vertices[i] if b in initial_vertices: if b not in vertices_on_feature or a not in vertices_on_feature or c not in vertices_on_feature: regular_vertices.append(b) face_propagation(patch_decomposition, fkey, regular_vertices) break return patch_decomposition # propagate across curve features if feature_polylines is not None: # store vertex keys on the polyline feature using the map of geometric keys feature_map = [ geometric_key([float(x), float(y), float(z)]) for polyline in feature_polylines for x, y, z in polyline ] vertices_on_feature = [ vkey for vkey in patch_decomposition.vertices() if geometric_key( patch_decomposition.vertex_coordinates(vkey)) in feature_map ] # dictionary mapping vertex geometric keys to vertex indices vertex_map = { geometric_key(patch_decomposition.vertex_coordinates(vkey)): vkey for vkey in patch_decomposition.vertices() } # list of edges on curve features polyline_keys = [[ geometric_key([float(x), float(y), float(z)]) for x, y, z in polyline ] for polyline in feature_polylines] feature_edges = [] for u, v in patch_decomposition.edges(): u_key = geometric_key(patch_decomposition.vertex_coordinates(u)) v_key = geometric_key(patch_decomposition.vertex_coordinates(v)) for polyline in polyline_keys: if u_key in polyline and v_key in polyline: feature_edges.append((u, v)) break # store face keys along the polyline faces_along_feature = [] for vkey in vertices_on_feature: for fkey in patch_decomposition.vertex_faces(vkey): if fkey not in faces_along_feature: faces_along_feature.append(fkey) for fkey in faces_along_feature: # check if face has not been deleted in-between if fkey not in list(patch_decomposition.faces()): continue face_vertices = patch_decomposition.face_vertices(fkey) # check if not quad patch if len(face_vertices) > 4: print 'to split' for u, v in patch_decomposition.face_halfedges(fkey): # check where to make the split if u not in vertices_on_feature and v in vertices_on_feature: vkey = patch_decomposition.face_vertex_descendant( fkey, v) wkey = poly_poly_1(patch_decomposition, fkey, vkey) faces_along_feature.append( patch_decomposition.halfedge[wkey][vkey]) # update feature_edges b = patch_decomposition.face_vertex_descendant( patch_decomposition.halfedge[vkey][wkey], wkey) c = patch_decomposition.face_vertex_ancestor( patch_decomposition.halfedge[wkey][vkey], wkey) if (b, c) in feature_edges or (c, b) in feature_edges: if (b, c) in feature_edges: feature_edges.remove((b, c)) else: feature_edges.remove((c, b)) feature_edges.append((b, wkey)) feature_edges.append((wkey, c)) # propagate until boundary or closed loop count = patch_decomposition.number_of_faces() while count > 0: count -= 1 next_fkey = patch_decomposition.halfedge[vkey][ wkey] ukey = patch_decomposition.face_vertex_descendant( next_fkey, wkey) if wkey in patch_decomposition.halfedge[ ukey] and patch_decomposition.halfedge[ ukey][wkey] is not None: next_fkey = patch_decomposition.halfedge[ukey][ wkey] print next_fkey if len( patch_decomposition.face_vertices( next_fkey)) == 5: vkey = wkey old_neighbours = patch_decomposition.vertex_neighbors( vkey) wkey = penta_quad_1( patch_decomposition, next_fkey, wkey) print next_fkey original_vertices = patch_decomposition.face_vertices( next_fkey) original_vertices.remove(vkey) face_propagation(patch_decomposition, next_fkey, original_vertices) new_neighbours = patch_decomposition.vertex_neighbors( vkey) for nbr in new_neighbours: if nbr not in old_neighbours: wkey = nbr break # update feature_edges b = patch_decomposition.face_vertex_descendant( patch_decomposition.halfedge[vkey] [wkey], wkey) c = patch_decomposition.face_vertex_ancestor( patch_decomposition.halfedge[wkey] [vkey], wkey) if (b, c) in feature_edges or ( c, b) in feature_edges: if (b, c) in feature_edges: feature_edges.remove((b, c)) else: feature_edges.remove((c, b)) feature_edges.append((b, wkey)) feature_edges.append((wkey, c)) # add to faces along feature to check faces_along_feature.append( patch_decomposition.halfedge[vkey] [wkey]) faces_along_feature.append( patch_decomposition.halfedge[wkey] [vkey]) continue elif len( patch_decomposition.face_vertices( next_fkey)) == 6: vkey = wkey face_vertices = patch_decomposition.face_vertices( next_fkey) idx = face_vertices.index(vkey) wkey = face_vertices[idx - 3] wkey = hexa_quad_1(patch_decomposition, next_fkey, wkey) original_vertices = patch_decomposition.face_vertices( next_fkey) original_vertices.remove(vkey) original_vertices.remove(wkey) face_propagation(patch_decomposition, next_fkey, original_vertices) # add to faces along feature to check faces_along_feature.append( patch_decomposition.halfedge[vkey] [wkey]) faces_along_feature.append( patch_decomposition.halfedge[wkey] [vkey]) break #elif len(patch_decomposition.face_vertices(next_fkey)) == 4: # vkey = wkey # wkey = quad_tri_1(patch_decomposition, next_fkey, wkey) # # add to faces along feature to check # faces_along_feature.append(patch_decomposition.halfedge[vkey][wkey]) # faces_along_feature.append(patch_decomposition.halfedge[wkey][vkey]) # break break return patch_decomposition
def offset_polygon(polygon, distance): """Offset a polygon (closed) by a distance. Parameters ---------- polygon : list of point The XYZ coordinates of the corners of the polygon. The first and last coordinates must not be identical. distance : float or list of float The offset distance as float. A single value determines a constant offset globally. Alternatively, pairs of local offset values per line segment can be used to create variable offsets. Distance > 0: offset to the outside, distance < 0: offset to the inside. Returns ------- offset polygon : list of point The XYZ coordinates of the corners of the offset polygon. The first and last coordinates are identical. Notes ----- The offset direction is determined by the normal of the polygon. If the polygon is in the XY plane and the normal is along the positive Z axis, positive offset distances will result in an offset towards the inside of the polygon. The algorithm works also for spatial polygons that do not perfectly fit a plane. Examples -------- .. code-block:: python polygon = [ (0.0, 0.0, 0.0), (3.0, 0.0, 1.0), (3.0, 3.0, 2.0), (1.5, 1.5, 2.0), (0.0, 3.0, 1.0), (0.0, 0.0, 0.0) ] distance = 0.5 # constant offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) distance = [ (0.1, 0.2), (0.2, 0.3), (0.3, 0.4), (0.4, 0.3), (0.3, 0.1) ] # variable offset polygon_offset = offset_polygon(polygon, distance) print(polygon_offset) """ p = len(polygon) if isinstance(distance, (list, tuple)): distances = distance else: distances = [distance] * p d = len(distances) if d < p: distances.extend(distances[-1:] * (p - d)) normal = normal_polygon(polygon) offset = [] for line, distance in zip(pairwise(polygon + polygon[:1]), distances): offset.append(offset_line(line, distance, normal)) points = [] for l1, l2 in pairwise(offset[-1:] + offset): x1, x2 = intersection_line_line(l1, l2) if x1 and x2: points.append(centroid_points([x1, x2])) else: points.append(x1) return points