You will learn how to generate a cutting pattern on the 3D form-found mesh along parallel edges.
For this, we will first strip and then split the mesh into separate strip meshes along the cable-net lines.
In preparation for later, flip the face orientation of the mesh so that its normals point outwards by flipping the cycle direction.
# ==============================================================================# Mesh# ==============================================================================# construct the form-found mesh from stored data contained in the json filemesh = Mesh.from_json(FILE_I)# reorient mesh so that normals point upwards by flipping the cycle directions of all facesmesh.flip_cycles()
The Mesh.flip_cycles function does not care about the directions being unified or not. It just reverses whatever direction it finds. In case you ever have a mesh with nonunified cycle directions, you should check out the Mesh.unify_cycle function, as it is a necessary condition for the data structures to work properly.
b1. Shorter or Longer Boundary
Find the shorter (or longer) boundary to strip off from. This is basically the same code of module B2.e1 with the addition that you can choose if you would like to find the shorter or longer boundary. It is also basically a subpart of the function from module C1.d1.
# ==============================================================================# Helpers# ==============================================================================defshorter_longer_boundary(mesh,direction='short'):"""Return boundary edges based on length, either short or long. """# select starting corner corners =list(mesh.vertices_where({'vertex_degree': 2})) corner = corners[0]# check in which direction the edge is shorter corner_edges = mesh.vertex_neighbors(corner) edgeA = (corner, corner_edges[0]) edgeB = (corner, corner_edges[1]) loopA = mesh.edge_loop(edgeA) loopB = mesh.edge_loop(edgeB)iflen(loopA)<=len(loopB):if direction =='short': start = edgeA loop = loopAelif direction =='long': start = edgeB loop = loopBelse:if direction =='short': start = edgeB loop = loopBelif direction =='long': start = edgeA loop = loopAreturn corner, start, loop
Strip off the short boundary along parallel edge strips and assign strip number as faces attribute. Also, store the total number of strips as mesh attribute.
In this step, the data structure is still one entire mesh with different face attributes only.
set(list) can be used to remove duplicates in a list.
defface_strips(mesh,loop):"""Strip the mesh in face attributes. """# create list of faces for each strip strips = []for row in loop: edge_strip = mesh.edge_strip(row) face_strip = [mesh.edge_faces(u, v)for (u, v) in edge_strip] face_strip =list(set(flatten(face_strip))) face_strip = [i for i in face_strip if i isnotNone] strips.append(face_strip)# set attributes for faces with strip indexfor i_strip, strip inenumerate(strips):for fkey in strip: mesh.face_attribute(fkey, 'strip', i_strip)# set attribute mesh on total number of strips mesh.attributes['strips']=len(strips)return strips...# ==============================================================================# Strip Faces of the Mesh# ==============================================================================# shorter boundary from which to strip offcorner, start, loop =shorter_longer_boundary(mesh, direction='short')# the previous code was wrapped into a functionstrip =face_strips(mesh, loop)
The Mesh.edge_strip method returns None if an edge is on the boundary and the thus neighbouring face does not exist. Remove the None elements from the list.
Display the mesh with various colours depending on the face attribute strip number and display the face keys.
For each strip create a new mesh that inherits the attributes of the entire mesh. Make sure to keep the vertex and face keys (the edge keys follow the vertex keys). This means that along the inner boundaries the vertices will be doubled with the same index.
Remember the difference in accessing and copying default attributes and custom attributes!
After this step, the meshes are all separate per strip as can be seen by the edge colours or the layer structure.
# ==============================================================================# Split Mesh into Strip Meshes# ==============================================================================# list to contain all new strip meshesstrip_meshes = []# do so for each stripfor i_strip inrange(mesh.attributes['strips']):# find all faces that are in the respective strip faces_strip =list(mesh.faces_where({'strip': i_strip}))# create submesh and inherit default attributes strip_mesh =Mesh() strip_mesh.attributes.update({'strip': i_strip}) strip_mesh.update_default_vertex_attributes(mesh.default_vertex_attributes) strip_mesh.update_default_edge_attributes(mesh.default_edge_attributes) strip_mesh.update_default_face_attributes(mesh.default_face_attributes)for fkey in faces_strip:# add vertices with copied attributes keys = mesh.face_vertices(fkey)for key in keys:if key notin strip_mesh.vertex: attr = mesh.vertex[key].copy() strip_mesh.add_vertex(key=key, attr_dict=attr)# add face with copied attributes attr = mesh.facedata[fkey].copy() strip_mesh.add_face(keys, fkey=fkey, attr_dict=attr)# copy edge attributesfor (u, v), attr in strip_mesh.edges(True):for name in attr: value = mesh.edge_attribute((u, v), name) attr[name]= value# append the new mesh strip to the list of meshes strip_meshes.append(strip_mesh)
Visualise each strip mesh separately with different edge colours for interior or boundary edges and display the strip/mesh number as face labels.
# ==============================================================================# Visualize Rhino# ==============================================================================# visualise each strip mesh separatefor strip_mesh in strip_meshes: facecolor ={} facetext ={}for fkey in strip_mesh.faces(): i_strip = strip_mesh.attributes['strip'] facecolor[fkey]=i_to_rgb(i_strip/mesh.attributes['strips']) facetext[fkey]=str(i_strip) edgecolor ={}for (u, v) in strip_mesh.edges():if (u, v) in strip_mesh.edges_on_boundary()or (v, u) in strip_mesh.edges_on_boundary(): edgecolor[(u, v)]= (0,0,0)else: edgecolor[(u, v)]= (125,125,125) baselayer ="CSD2::C2::MaterializeFabric" artist =MeshArtist(strip_mesh, layer=baselayer+"::{}::{}".format("StripMesh", strip_mesh.attributes['strip'])) artist.clear_layer() artist.draw_vertices() artist.draw_edges(color=edgecolor) artist.draw_faces(color=facecolor) artist.draw_facelabels(text=facetext, color=facecolor)
Congratulations, we reached the first part of materialising the fabric by creating the cutting pattern. Move on to unroll these mesh strips!