|
| 1 | +.. _compas_slicer_example_6: |
| 2 | + |
| 3 | +************************************ |
| 4 | +Transferring attributes to PrintPoints |
| 5 | +************************************ |
| 6 | + |
| 7 | +Often in 3D printing we need to transfer information from the mesh that is being sliced to the PrintPoints that |
| 8 | +are used in the fabrication process. We might want, for example, to print paths that are generated from different parts of |
| 9 | +the geometry using different parameters. This is enabled by the *transfer_mesh_attributes_to_printpoints()* function, as |
| 10 | +shown in the example below. During the slicing process each printpoint is projected to the closest mesh face. |
| 11 | +It takes directly all the face attributes, and it takes the averaged vertex attributes of the face vertices using |
| 12 | +barycentric coordinates. |
| 13 | + |
| 14 | +.. figure:: figures/06_attributes.png |
| 15 | + :figclass: figure |
| 16 | + :class: figure-img img-fluid |
| 17 | + |
| 18 | + *PrintPoints with visualization of the attribute: overhang angle of the underlying mesh.* |
| 19 | + |
| 20 | + |
| 21 | +.. code-block:: python |
| 22 | +
|
| 23 | + import logging |
| 24 | + import os |
| 25 | + from compas.geometry import Point, Vector, distance_point_plane, normalize_vector |
| 26 | + from compas.datastructures import Mesh |
| 27 | + import compas_slicer.utilities as slicer_utils |
| 28 | + from compas_slicer.post_processing import simplify_paths_rdp |
| 29 | + from compas_slicer.slicers import PlanarSlicer |
| 30 | + import compas_slicer.utilities.utils as utils |
| 31 | + from compas_slicer.utilities.attributes_transfer import transfer_mesh_attributes_to_printpoints |
| 32 | + from compas_slicer.print_organization import PlanarPrintOrganizer |
| 33 | + import numpy as np |
| 34 | +
|
| 35 | + logger = logging.getLogger('logger') |
| 36 | + logging.basicConfig(format='%(levelname)s-%(message)s', level=logging.INFO) |
| 37 | +
|
| 38 | + DATA_PATH = os.path.join(os.path.dirname(__file__), 'data') |
| 39 | + OUTPUT_PATH = slicer_utils.get_output_directory(DATA_PATH) |
| 40 | + MODEL = 'distorted_v_closed_low_res.obj' |
| 41 | +
|
| 42 | + if __name__ == '__main__': |
| 43 | + # load mesh |
| 44 | + mesh = Mesh.from_obj(os.path.join(DATA_PATH, MODEL)) |
| 45 | +
|
| 46 | + # --------------- Add attributes to mesh |
| 47 | + # Face attributes can be anything (ex. float, bool, array, text ...) |
| 48 | + # Vertex attributes can only be entities that can be meaningfully multiplied with a float (ex. float, np.array ...) |
| 49 | +
|
| 50 | + # overhand attribute - Scalar value (per face) |
| 51 | + mesh.update_default_face_attributes({'overhang': 0.0}) |
| 52 | + for f_key, data in mesh.faces(data=True): |
| 53 | + face_normal = mesh.face_normal(f_key, unitized=True) |
| 54 | + data['overhang'] = Vector(0.0, 0.0, 1.0).dot(face_normal) |
| 55 | +
|
| 56 | + # face looking towards the positive y axis - Boolean value (per face) |
| 57 | + mesh.update_default_face_attributes({'positive_y_axis': False}) |
| 58 | + for f_key, data in mesh.faces(data=True): |
| 59 | + face_normal = mesh.face_normal(f_key, unitized=True) |
| 60 | + is_positive_y = Vector(0.0, 1.0, 0.0).dot(face_normal) > 0 # boolean value |
| 61 | + data['positive_y_axis'] = is_positive_y |
| 62 | +
|
| 63 | + # distance from plane - Scalar value (per vertex) |
| 64 | + mesh.update_default_vertex_attributes({'dist_from_plane': 0.0}) |
| 65 | + plane = (Point(0.0, 0.0, -30.0), Vector(0.0, 0.5, 0.5)) |
| 66 | + for v_key, data in mesh.vertices(data=True): |
| 67 | + v_coord = mesh.vertex_coordinates(v_key, axes='xyz') |
| 68 | + data['dist_from_plane'] = distance_point_plane(v_coord, plane) |
| 69 | +
|
| 70 | + # direction towards point - Vector value (per vertex) |
| 71 | + mesh.update_default_vertex_attributes({'direction_to_pt': 0.0}) |
| 72 | + pt = Point(4.0, 1.0, 0.0) |
| 73 | + for v_key, data in mesh.vertices(data=True): |
| 74 | + v_coord = mesh.vertex_coordinates(v_key, axes='xyz') |
| 75 | + data['direction_to_pt'] = np.array(normalize_vector(Vector.from_start_end(v_coord, pt))) |
| 76 | +
|
| 77 | + # --------------- Slice mesh |
| 78 | + slicer = PlanarSlicer(mesh, slicer_type="default", layer_height=5.0) |
| 79 | + slicer.slice_model() |
| 80 | + simplify_paths_rdp(slicer, threshold=1.0) |
| 81 | + slicer_utils.save_to_json(slicer.to_data(), OUTPUT_PATH, 'slicer_data.json') |
| 82 | +
|
| 83 | + # --------------- Create printpoints |
| 84 | + print_organizer = PlanarPrintOrganizer(slicer) |
| 85 | + print_organizer.create_printpoints() |
| 86 | +
|
| 87 | + # --------------- Transfer mesh attributes to printpoints |
| 88 | + transfer_mesh_attributes_to_printpoints(mesh, print_organizer.printpoints_dict) |
| 89 | +
|
| 90 | + # --------------- Save printpoints to json (only json-serializable attributes are saved) |
| 91 | + printpoints_data = print_organizer.output_printpoints_dict() |
| 92 | + utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json') |
| 93 | +
|
| 94 | + # --------------- Print the info to see the attributes of the printpoints (you can also visualize them on gh) |
| 95 | + print_organizer.printout_info() |
| 96 | +
|
| 97 | + # --------------- Save printpoints attributes for visualization |
| 98 | + overhangs_list = print_organizer.get_printpoints_attribute(attr_name='overhang') |
| 99 | + positive_y_axis_list = print_organizer.get_printpoints_attribute(attr_name='positive_y_axis') |
| 100 | + dist_from_plane_list = print_organizer.get_printpoints_attribute(attr_name='dist_from_plane') |
| 101 | + direction_to_pt_list = print_organizer.get_printpoints_attribute(attr_name='direction_to_pt') |
| 102 | +
|
| 103 | + utils.save_to_json(overhangs_list, OUTPUT_PATH, 'overhangs_list.json') |
| 104 | + utils.save_to_json(positive_y_axis_list, OUTPUT_PATH, 'positive_y_axis_list.json') |
| 105 | + utils.save_to_json(dist_from_plane_list, OUTPUT_PATH, 'dist_from_plane_list.json') |
| 106 | + utils.save_to_json(utils.point_list_to_dict(direction_to_pt_list), OUTPUT_PATH, 'direction_to_pt_list.json') |
0 commit comments