def marchingCubes(cls, voxel_model: VoxelModel, smooth: bool = False, color: Tuple[float, float, float, float] = (0.8, 0.8, 0.8, 1)): """ Generate a mesh object from a VoxelModel object using a marching cubes algorithm. This meshing approach is best suited to high resolution models where some smoothing is acceptable. Args: voxel_model: VoxelModel object to be converted to a mesh smooth: Enable smoothing color: Mesh color in the format (r, g, b, a) Returns: None """ voxel_model_fit = voxel_model.fitWorkspace().getOccupied() voxels = voxel_model_fit.voxels.astype(np.uint16) x, y, z = voxels.shape model_offsets = voxel_model_fit.coords voxels_padded = np.zeros((x + 2, y + 2, z + 2)) voxels_padded[1:-1, 1:-1, 1:-1] = voxels if smooth: voxels_padded = mcubes.smooth(voxels_padded) levelset = 0 else: levelset = 0.5 verts, tris = mcubes.marching_cubes(voxels_padded, levelset) # Shift model to align with origin verts = np.subtract(verts, 0.5) verts[:, 0] = np.add(verts[:, 0], model_offsets[0]) verts[:, 1] = np.add(verts[:, 1], model_offsets[1]) verts[:, 2] = np.add(verts[:, 2], model_offsets[2]) verts_colors = generateColors(len(verts), color) return cls(voxels_padded, verts, verts_colors, tris, voxel_model.resolution)
def simpleSquares(cls, voxel_model: VoxelModel, color: Tuple[float, float, float, float] = None): """ Generate a mesh object from a VoxelModel object using large square faces. This function can greatly reduce the file size of generated meshes. However, it may not correctly recognize small (1 voxel) model features and currently produces files with a nonstandard vertex arrangement. Use at your own risk. ---- Example: ``mesh1 = vf.Mesh.simpleSquares(model1)`` ---- Args: voxel_model: VoxelModel object to be converted to a mesh color: Mesh color in the format (r, g, b, a), None to use voxel colors Returns: Mesh """ voxel_model_fit = voxel_model.fitWorkspace() voxel_model_array = voxel_model_fit.voxels.astype(np.uint16) model_materials = voxel_model_fit.materials model_offsets = voxel_model_fit.coords x_len, y_len, z_len = voxel_model_array.shape # Determine vertex types vert_type = np.zeros((x_len + 1, y_len + 1, z_len + 1), dtype=np.uint8) vert_color = np.zeros((x_len + 1, y_len + 1, z_len + 1, 4), dtype=np.float32) for x in tqdm(range(x_len), desc='Finding voxel vertices'): for y in range(y_len): for z in range(z_len): if voxel_model_array[x, y, z] > 0: vert_type[x:x + 2, y:y + 2, z:z + 2] = 1 # Type 1 = occupied/exterior if color is None: r = 0 g = 0 b = 0 for i in range(voxel_model.materials.shape[1] - 1): r = r + model_materials[voxel_model_array[ x, y, z]][i + 1] * material_properties[i]['r'] g = g + model_materials[voxel_model_array[ x, y, z]][i + 1] * material_properties[i]['g'] b = b + model_materials[voxel_model_array[ x, y, z]][i + 1] * material_properties[i]['b'] r = 1 if r > 1 else r g = 1 if g > 1 else g b = 1 if b > 1 else b a = 1 - model_materials[voxel_model_array[x, y, z]][1] voxel_color = np.array([r, g, b, a]) else: voxel_color = np.array(color) for cx in range(x, x + 2): for cy in range(y, y + 2): for cz in range(z, z + 2): vert_color[cx, cy, cz, :] = voxel_color for x in tqdm(range(1, x_len), desc='Finding interior vertices'): for y in range(1, y_len): for z in range(1, z_len): vert_type = markInterior(vert_type, x, y, z) for x in tqdm(range(0, x_len + 1), desc='Finding feature vertices'): for y in range(0, y_len + 1): for z in range(0, z_len + 1): vert_type = markInsideCorner(vert_type, x, y, z) # Initialize arrays vi = 0 # Tracks current vertex index verts = [] colors = [] tris = [] quads = [] vert_index = np.multiply(np.ones_like(vert_type, dtype=np.int32), -1) for x in tqdm(range(x_len + 1), desc='Meshing'): for y in range(y_len + 1): for z in range(z_len + 1): dirs = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] for d in dirs: vi, vert_type, vert_index, new_verts, new_colors, new_tris, new_quads = findSquare( vi, vert_type, vert_index, vert_color, voxel_model_array, x, y, z, d[0], d[1], d[2]) verts += new_verts colors += new_colors tris += new_tris quads += new_quads verts = np.array(verts, dtype=np.float32) colors = np.array(colors, dtype=np.float32) tris = np.array(tris, dtype=np.uint32) quads = np.array(quads, dtype=np.uint32) # Shift model to align with origin verts[:, 0] = np.add(verts[:, 0], model_offsets[0]) verts[:, 1] = np.add(verts[:, 1], model_offsets[1]) verts[:, 2] = np.add(verts[:, 2], model_offsets[2]) return cls(voxel_model_array, verts, colors, tris, voxel_model.resolution)
def fromVoxelModel(cls, voxel_model: VoxelModel, color: Tuple[float, float, float, float] = None): """ Generate a mesh object from a VoxelModel object. ---- Example: ``mesh1 = vf.Mesh.fromVoxelModel(model1)`` ---- Args: voxel_model: VoxelModel object to be converted to a mesh color: Mesh color in the format (r, g, b, a), None to use voxel colors Returns: Mesh """ voxel_model_fit = voxel_model.fitWorkspace() voxel_model_array = voxel_model_fit.voxels.astype(np.uint16) model_materials = voxel_model_fit.materials model_offsets = voxel_model_fit.coords # Find exterior voxels exterior_voxels_array = voxel_model_fit.difference( voxel_model_fit.erode(radius=1, connectivity=1)).voxels x_len, y_len, z_len = voxel_model_array.shape # Create list of exterior voxel coordinates exterior_voxels_coords = [] for x in tqdm(range(x_len), desc='Finding exterior voxels'): for y in range(y_len): for z in range(z_len): if exterior_voxels_array[x, y, z] != 0: exterior_voxels_coords.append([x, y, z]) # Get voxel array voxel_model_array[voxel_model_array < 0] = 0 # Initialize arrays verts = [] verts_colors = [] verts_indices = np.zeros((x_len + 1, y_len + 1, z_len + 1)) tris = [] vi = 1 # Tracks current vertex index # Loop through voxel_model_array data for voxel_coords in tqdm(exterior_voxels_coords, desc='Meshing'): x, y, z = voxel_coords if color is None: r = 0 g = 0 b = 0 for i in range(voxel_model.materials.shape[1] - 1): r = r + model_materials[voxel_model_array[x, y, z]][ i + 1] * material_properties[i]['r'] g = g + model_materials[voxel_model_array[x, y, z]][ i + 1] * material_properties[i]['g'] b = b + model_materials[voxel_model_array[x, y, z]][ i + 1] * material_properties[i]['b'] r = 1 if r > 1 else r g = 1 if g > 1 else g b = 1 if b > 1 else b a = 1 - model_materials[voxel_model_array[x, y, z]][1] voxel_color = [r, g, b, a] else: voxel_color = list(color) # Add cube vertices new_verts, verts_indices, new_tris, vi = addVerticesAndTriangles( voxel_model_array, verts_indices, model_offsets, x, y, z, vi) verts += new_verts tris += new_tris # Apply color to all vertices for i in range(len(new_verts)): verts_colors.append(voxel_color) verts = np.array(verts, dtype=np.float32) verts_colors = np.array(verts_colors, dtype=np.float32) tris = np.array(tris, dtype=np.uint32) return cls(voxel_model_array, verts, verts_colors, tris, voxel_model.resolution)