diff --git a/docs/examples.rst b/docs/examples.rst index 4ac1202d..4837627d 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -2,7 +2,15 @@ Examples ******************************************************************************** -Here you can find some examples files for compas_slicer +All of the examples of compas_slicer can be found in the folder `/examples/`.In each example folder you can find: + +* the python file that performs the slicing, + +* a data folder with all the data required for the slicing + +* and a grasshopper file for visualizing the results. + +In the links below we go through some of these examples in more detail: .. toctree:: :numbered: @@ -14,4 +22,4 @@ Here you can find some examples files for compas_slicer examples/03_planar_slicing_vertical_sorting examples/04_gcode_generation examples/05_non_planar_slicing_on_custom_base - examples/06_attributes_transfer + examples/06_attributes_transfer \ No newline at end of file diff --git a/docs/examples/01_planar_slicing_simple.rst b/docs/examples/01_planar_slicing_simple.rst index 577b3912..b37ae379 100644 --- a/docs/examples/01_planar_slicing_simple.rst +++ b/docs/examples/01_planar_slicing_simple.rst @@ -6,9 +6,8 @@ Simple planar slicing A general introduction of the concepts organization of compas_slicer can be found in the :ref:`introduction tutorial `. - This example describes the planar slicing process for a simple shape, consisting -out of a shape with a single contour (also known as a 'vase'). +out of a shape with a single contour (also known as a 'vase'). Its files can be found in the folder `/examples/1_planar_slicing_simple/` Imports and initialization ========================== @@ -28,13 +27,13 @@ The first step is to import the required functions: from compas_slicer.post_processing import generate_raft from compas_slicer.post_processing import simplify_paths_rdp_igl from compas_slicer.post_processing import seams_smooth + from compas_slicer.post_processing import seams_align from compas_slicer.print_organization import PlanarPrintOrganizer from compas_slicer.print_organization import set_extruder_toggle from compas_slicer.print_organization import add_safety_printpoints from compas_slicer.print_organization import set_linear_velocity_constant from compas_slicer.print_organization import set_blend_radius from compas_slicer.utilities import save_to_json - from compas_view2 import app from compas.datastructures import Mesh from compas.geometry import Point @@ -56,9 +55,10 @@ checks if the ``output`` folder exists and if not, it creates it. .. code-block:: python DATA = os.path.join(os.path.dirname(__file__), 'data') - OUTPUT_DIR = utils.get_output_directory(DATA) + OUTPUT_DIR = utils.get_output_directory(DATA) # creates 'output' folder if it doesn't already exist MODEL = 'simple_vase_open_low_res.obj' + Slicing process =============== @@ -67,7 +67,6 @@ file. We then move it to the origin, but this can be any specified point, such a a point on your print bed. .. code-block:: python - compas_mesh = Mesh.from_obj(os.path.join(DATA, MODEL)) move_mesh_to_point(compas_mesh, Point(0, 0, 0)) @@ -85,6 +84,14 @@ are supported: slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=1.5) slicer.slice_model() + +We also align the seams so that the start of each path is as close as possible to the start of the previous path + +.. code-block:: python + + seams_align(slicer, "next_path") + + After the model has been sliced, several post processing operations can be executed. One useful functionality is ``generate_brim``, which generates a number of layers that are offset from the bottom layer, to improve adhesion to the build plate @@ -106,7 +113,7 @@ that are offset from the bottom layer, to improve adhesion to the build plate raft_layers=1) Depending on the amount of faces that your input mesh has, a very large amount of -points can be generated. ``simplify_paths_rdp`` or ``simplify_paths_rdp_igl`` are functions that remove points +points can be generated. ``simplify_paths_rdp_igl`` removes points that do not have a high impact on the final shape of the polyline. Increase the threshold value to remove more points, decrease it to remove less. For more information on how the algorithm works see: `Ramer–Douglas–Peucker algorithm `_ @@ -118,7 +125,8 @@ information on how the algorithm works see: `Ramer–Douglas–Peucker algorithm Currently the 'seam' between different layers of our shape is a 'hard seam', the printer would move up almost vertically to move to the next layer. To make the seam more 'smooth', and less visible we can use the -``seams_smooth`` function. +``seams_smooth`` function. This function simply removes points within the specified distance to enable +a smoother motion from one layer to the next. .. code-block:: python @@ -147,7 +155,7 @@ In the next steps of the process we will use the :class:`PlanarPrintOrganizer` t make our slicing result ready for fabrication. First, we initialize the :class:`PlanarPrintOrganizer` and create :class:`PrintPoints`. The difference between :class:`PrintPoints` and the ``compas.geometry.Points`` we were using in the -previous step is that the :class:`PrintPoints` have all the necessary additional functionality that is +previous step is that the :class:`PrintPoints` have all the necessary additional information that is needed for the fabrication process. .. code-block:: python @@ -182,20 +190,13 @@ and then export it to a ``.JSON`` file. printpoints_data = print_organizer.output_printpoints_dict() save_to_json(printpoints_data, DATA, 'out_printpoints.json') -Finally, we can use the library ``compas_view2`` to visualize our results. - -.. code-block:: python - - viewer = app.App(width=1600, height=1000) - slicer.visualize_on_viewer(viewer, visualize_mesh=False, visualize_paths=True) - print_organizer.visualize_on_viewer(viewer, visualize_printpoints=True) - viewer.show() - - Once the slicing process is finished, you can use the compas_slicer grasshopper components to visualize the results, described in the :ref:`grasshopper tutorial `. +To view the results of the slicing process, open the `planar_slicing_master.gh` file in `examples/1_planar_slicing_simple`. This loads the +json and txt files that have been produced and displays them as Rhino-Grasshopper geometry. You will only be able to visualize +the results after you have run the python file that generates them. Final script ============ @@ -215,13 +216,13 @@ The completed final script can be found below: from compas_slicer.post_processing import generate_raft from compas_slicer.post_processing import simplify_paths_rdp_igl from compas_slicer.post_processing import seams_smooth + from compas_slicer.post_processing import seams_align from compas_slicer.print_organization import PlanarPrintOrganizer from compas_slicer.print_organization import set_extruder_toggle from compas_slicer.print_organization import add_safety_printpoints from compas_slicer.print_organization import set_linear_velocity_constant from compas_slicer.print_organization import set_blend_radius from compas_slicer.utilities import save_to_json - from compas_view2 import app from compas.datastructures import Mesh from compas.geometry import Point @@ -240,93 +241,94 @@ The completed final script can be found below: MODEL = 'simple_vase_open_low_res.obj' - start_time = time.time() - - # ========================================================================== - # Load mesh - # ========================================================================== - compas_mesh = Mesh.from_obj(os.path.join(DATA, MODEL)) - - # ========================================================================== - # Move to origin - # ========================================================================== - move_mesh_to_point(compas_mesh, Point(0, 0, 0)) - - # ========================================================================== - # Slicing - # options: 'default': Both for open and closed paths. But slow - # 'cgal': Very fast. Only for closed paths. - # Requires additional installation (compas_cgal). - # ========================================================================== - slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=1.5) - slicer.slice_model() - - # ========================================================================== - # Generate brim / raft - # ========================================================================== - # NOTE: Typically you would want to use either a brim OR a raft, - # however, in this example both are used to explain the functionality - generate_brim(slicer, layer_width=3.0, number_of_brim_offsets=4) - generate_raft(slicer, - raft_offset=20, - distance_between_paths=5, - direction="xy_diagonal", - raft_layers=1) - - # ========================================================================== - # Simplify the paths by removing points with a certain threshold - # change the threshold value to remove more or less points - # ========================================================================== - simplify_paths_rdp_igl(slicer, threshold=0.6) - - # ========================================================================== - # Smooth the seams between layers - # change the smooth_distance value to achieve smoother, or more abrupt seams - # ========================================================================== - seams_smooth(slicer, smooth_distance=10) - - # ========================================================================== - # Prints out the info of the slicer - # ========================================================================== - slicer.printout_info() - - # ========================================================================== - # Save slicer data to JSON - # ========================================================================== - save_to_json(slicer.to_data(), OUTPUT_DIR, 'slicer_data.json') - - # ========================================================================== - # Initializes the PlanarPrintOrganizer and creates PrintPoints - # ========================================================================== - print_organizer = PlanarPrintOrganizer(slicer) - print_organizer.create_printpoints() - - # ========================================================================== - # Set fabrication-related parameters - # ========================================================================== - set_extruder_toggle(print_organizer, slicer) - add_safety_printpoints(print_organizer, z_hop=10.0) - set_linear_velocity_constant(print_organizer, v=25.0) - set_blend_radius(print_organizer, d_fillet=10.0) - - # ========================================================================== - # Prints out the info of the PrintOrganizer - # ========================================================================== - print_organizer.printout_info() - - # ========================================================================== - # Converts the PrintPoints to data and saves to JSON - # ========================================================================= - printpoints_data = print_organizer.output_printpoints_dict() - utils.save_to_json(printpoints_data, OUTPUT_DIR, 'out_printpoints.json') - - # ========================================================================== - # Initializes the compas_viewer and visualizes results - # ========================================================================== - viewer = app.App(width=1600, height=1000) - # slicer.visualize_on_viewer(viewer, visualize_mesh=False, visualize_paths=True) - print_organizer.visualize_on_viewer(viewer, visualize_printpoints=True) - viewer.show() - - end_time = time.time() - print("Total elapsed time", round(end_time - start_time, 2), "seconds") \ No newline at end of file + def main(): + start_time = time.time() + + # ========================================================================== + # Load mesh + # ========================================================================== + compas_mesh = Mesh.from_obj(os.path.join(DATA, MODEL)) + + # ========================================================================== + # Move to origin + # ========================================================================== + move_mesh_to_point(compas_mesh, Point(0, 0, 0)) + + # ========================================================================== + # Slicing + # options: 'default': Both for open and closed paths. But slow + # 'cgal': Very fast. Only for closed paths. + # Requires additional installation (compas_cgal). + # ========================================================================== + slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=1.5) + slicer.slice_model() + + seams_align(slicer, "next_path") + + # ========================================================================== + # Generate brim / raft + # ========================================================================== + # NOTE: Typically you would want to use either a brim OR a raft, + # however, in this example both are used to explain the functionality + generate_brim(slicer, layer_width=3.0, number_of_brim_offsets=4) + generate_raft(slicer, + raft_offset=20, + distance_between_paths=5, + direction="xy_diagonal", + raft_layers=1) + + # ========================================================================== + # Simplify the paths by removing points with a certain threshold + # change the threshold value to remove more or less points + # ========================================================================== + simplify_paths_rdp_igl(slicer, threshold=0.6) + + # ========================================================================== + # Smooth the seams between layers + # change the smooth_distance value to achieve smoother, or more abrupt seams + # ========================================================================== + seams_smooth(slicer, smooth_distance=10) + + # ========================================================================== + # Prints out the info of the slicer + # ========================================================================== + slicer.printout_info() + + # ========================================================================== + # Save slicer data to JSON + # ========================================================================== + save_to_json(slicer.to_data(), OUTPUT_DIR, 'slicer_data.json') + + # ========================================================================== + # Initializes the PlanarPrintOrganizer and creates PrintPoints + # ========================================================================== + print_organizer = PlanarPrintOrganizer(slicer) + print_organizer.create_printpoints(generate_mesh_normals=False) + + # ========================================================================== + # Set fabrication-related parameters + # ========================================================================== + set_extruder_toggle(print_organizer, slicer) + add_safety_printpoints(print_organizer, z_hop=10.0) + set_linear_velocity_constant(print_organizer, v=25.0) + + # ========================================================================== + # Prints out the info of the PrintOrganizer + # ========================================================================== + print_organizer.printout_info() + + # ========================================================================== + # Converts the PrintPoints to data and saves to JSON + # ========================================================================= + printpoints_data = print_organizer.output_printpoints_dict() + utils.save_to_json(printpoints_data, OUTPUT_DIR, 'out_printpoints.json') + + printpoints_data = print_organizer.output_nested_printpoints_dict() + utils.save_to_json(printpoints_data, OUTPUT_DIR, 'out_printpoints_nested.json') + + end_time = time.time() + print("Total elapsed time", round(end_time - start_time, 2), "seconds") + + + if __name__ == "__main__": + main() \ No newline at end of file diff --git a/docs/examples/02_curved_slicing_simple.rst b/docs/examples/02_curved_slicing_simple.rst index 70d8f065..c53e6b11 100644 --- a/docs/examples/02_curved_slicing_simple.rst +++ b/docs/examples/02_curved_slicing_simple.rst @@ -1,7 +1,7 @@ .. _compas_slicer_example_2: ************************************ -Simple curved interpolation slicing +Interpolation slicing ************************************ A general introduction of the concepts organization of compas_slicer can be found in the :ref:`introduction tutorial `. @@ -12,6 +12,7 @@ as it explains the main concepts of compas_slicer. Having done that, in this example, we go through the basics of using the non-planar interpolation slicer, which generates paths by interpolating user-defined boundaries. This example uses the method described in `Print Paths KeyFraming `_. +Its files can be found in the folder `/examples/2_curved_slicing/` .. figure:: figures/02_curved_slicing.PNG :figclass: figure @@ -19,6 +20,17 @@ This example uses the method described in `Print Paths KeyFraming `. +Once the slicing process is finished, you can open the `curved_slicing_master.gh to visualize the results. More information on +this visualization is given in :ref:`grasshopper tutorial `. Final script @@ -166,7 +173,7 @@ The completed final script can be found below: import logging import compas_slicer.utilities as utils from compas_slicer.slicers import InterpolationSlicer - from compas_slicer.post_processing import simplify_paths_rdp_igl + from compas_slicer.post_processing import simplify_paths_rdp from compas_slicer.pre_processing import InterpolationSlicingPreprocessor from compas_slicer.print_organization import set_extruder_toggle, set_linear_velocity_by_range from compas_slicer.print_organization import add_safety_printpoints @@ -174,74 +181,69 @@ The completed final script can be found below: from compas_slicer.print_organization import InterpolationPrintOrganizer from compas_slicer.post_processing import seams_smooth from compas_slicer.print_organization import smooth_printpoints_up_vectors, smooth_printpoints_layer_heights - from compas_slicer.post_processing import generate_brim - from compas_view2 import app import time logger = logging.getLogger('logger') logging.basicConfig(format='%(levelname)s - %(message)s', level=logging.INFO) - DATA_PATH = os.path.join(os.path.dirname(__file__), 'data_basic_example') + DATA_PATH = os.path.join(os.path.dirname(__file__), 'data_Y_shape') OUTPUT_PATH = utils.get_output_directory(DATA_PATH) - OBJ_INPUT_NAME = os.path.join(DATA_PATH, 'vase.obj') + OBJ_INPUT_NAME = os.path.join(DATA_PATH, 'mesh.obj') - start_time = time.time() + def main(): + start_time = time.time() - # --- Load initial_mesh - mesh = Mesh.from_obj(os.path.join(DATA_PATH, OBJ_INPUT_NAME)) + # --- Load initial_mesh + mesh = Mesh.from_obj(os.path.join(DATA_PATH, OBJ_INPUT_NAME)) - # --- Load targets (boundaries) - low_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryLOW.json') - high_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryHIGH.json') - create_mesh_boundary_attributes(mesh, low_boundary_vs, high_boundary_vs) + # --- Load targets (boundaries) + low_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryLOW.json') + high_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryHIGH.json') + create_mesh_boundary_attributes(mesh, low_boundary_vs, high_boundary_vs) - avg_layer_height = 15.0 + avg_layer_height = 2.0 - parameters = { - 'avg_layer_height': avg_layer_height, # controls number of curves that will be generated - 'min_layer_height': 0.3, - 'max_layer_height': 5.0 # 2.0, - } + parameters = { + 'avg_layer_height': avg_layer_height, # controls number of curves that will be generated + } - preprocessor = InterpolationSlicingPreprocessor(mesh, parameters, DATA_PATH) - preprocessor.create_compound_targets() - g_eval = preprocessor.create_gradient_evaluation(norm_filename='gradient_norm.json', g_filename='gradient.json', - target_1=preprocessor.target_LOW, - target_2=preprocessor.target_HIGH) - preprocessor.find_critical_points(g_eval, output_filenames=['minima.json', 'maxima.json', 'saddles.json']) + preprocessor = InterpolationSlicingPreprocessor(mesh, parameters, DATA_PATH) + preprocessor.create_compound_targets() + g_eval = preprocessor.create_gradient_evaluation(norm_filename='gradient_norm.json', g_filename='gradient.json', + target_1=preprocessor.target_LOW, + target_2=preprocessor.target_HIGH) + preprocessor.find_critical_points(g_eval, output_filenames=['minima.json', 'maxima.json', 'saddles.json']) - # --- slicing - slicer = InterpolationSlicer(mesh, preprocessor, parameters) - slicer.slice_model() # compute_norm_of_gradient contours - generate_brim(slicer, layer_width=3.0, number_of_brim_offsets=5) - seams_smooth(slicer, smooth_distance=10) + # --- slicing + slicer = InterpolationSlicer(mesh, preprocessor, parameters) + slicer.slice_model() # compute_norm_of_gradient contours - simplify_paths_rdp_igl(slicer, threshold=0.5) - slicer.printout_info() - utils.save_to_json(slicer.to_data(), OUTPUT_PATH, 'curved_slicer.json') + simplify_paths_rdp(slicer, threshold=0.25) + seams_smooth(slicer, smooth_distance=3) + slicer.printout_info() + utils.save_to_json(slicer.to_data(), OUTPUT_PATH, 'curved_slicer.json') - # --- Print organizer - print_organizer = InterpolationPrintOrganizer(slicer, parameters, DATA_PATH) - print_organizer.create_printpoints() + # --- Print organizer + print_organizer = InterpolationPrintOrganizer(slicer, parameters, DATA_PATH) + print_organizer.create_printpoints() - set_linear_velocity_by_range(print_organizer, param_func=lambda ppt: ppt.layer_height, - parameter_range=[avg_layer_height*0.5, avg_layer_height*2.0], - velocity_range=[150, 70], bound_remapping=False) - set_extruder_toggle(print_organizer, slicer) - add_safety_printpoints(print_organizer, z_hop=10.0) - smooth_printpoints_up_vectors(print_organizer, strength=0.5, iterations=10) - smooth_printpoints_layer_heights(print_organizer, strength=0.5, iterations=5) + smooth_printpoints_up_vectors(print_organizer, strength=0.5, iterations=10) + smooth_printpoints_layer_heights(print_organizer, strength=0.5, iterations=5) - # --- Save printpoints dictionary to json file - printpoints_data = print_organizer.output_printpoints_dict() - utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json') + set_linear_velocity_by_range(print_organizer, param_func=lambda ppt: ppt.layer_height, + parameter_range=[avg_layer_height*0.5, avg_layer_height*2.0], + velocity_range=[150, 70], bound_remapping=False) + set_extruder_toggle(print_organizer, slicer) + add_safety_printpoints(print_organizer, z_hop=10.0) + + # --- Save printpoints dictionary to json file + printpoints_data = print_organizer.output_printpoints_dict() + utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json') + + end_time = time.time() + print("Total elapsed time", round(end_time - start_time, 2), "seconds") - # ----- Visualize - viewer = app.App(width=1600, height=1000) - # slicer.visualize_on_viewer(viewer, visualize_mesh=False, visualize_paths=True) - print_organizer.visualize_on_viewer(viewer, visualize_printpoints=True) - viewer.show() - end_time = time.time() - print("Total elapsed time", round(end_time - start_time, 2), "seconds") + if __name__ == "__main__": + main() diff --git a/docs/examples/03_planar_slicing_vertical_sorting.rst b/docs/examples/03_planar_slicing_vertical_sorting.rst index 211ab5f7..0e6909e7 100644 --- a/docs/examples/03_planar_slicing_vertical_sorting.rst +++ b/docs/examples/03_planar_slicing_vertical_sorting.rst @@ -17,7 +17,10 @@ from one path to the next, as it is shown in the illustration below. *Fabrication path using horizontal sorting (left), and vertical sorting (right). The traveling paths are shown with orange lines.* In planar slicing, horizontal ordering of paths is the default method, while in non-planar slicing vertical ordering of paths is -the default method. The example below demonstrates how planar paths can be sorted in a vertical logic. +the default method. The example below demonstrates how planar paths can be sorted in a vertical logic. Its files can be found in the folder +`/examples/3_planar_vertical_sorting/`. Once you have +run the python file to generate the results, you can visualize them by opening the grasshopper file. + .. code-block:: python @@ -64,7 +67,7 @@ the default method. The example below demonstrates how planar paths can be sorte slicer.slice_model() # Sorting into vertical layers and reordering - sort_into_vertical_layers(slicer, max_paths_per_layer=10) + sort_into_vertical_layers(slicer, max_paths_per_layer=25) reorder_vertical_layers(slicer, align_with="x_axis") # Post-processing diff --git a/docs/examples/04_gcode_generation.rst b/docs/examples/04_gcode_generation.rst index d9f40bb0..76f0f169 100644 --- a/docs/examples/04_gcode_generation.rst +++ b/docs/examples/04_gcode_generation.rst @@ -6,6 +6,7 @@ Gcode generation While compas slicer has been mostly developed for robotic printing, we can also export the gcode of the generated paths to materialize them in a typical desktop 3D printer. The gcode generation is still at a basic level and is a work in progress. +The following file can be found in `/examples/4_gcode_generation/`. The gcode file is placed in the `/output/` folder. .. code-block:: python @@ -64,4 +65,8 @@ to materialize them in a typical desktop 3D printer. The gcode generation is sti # create and output gcode gcode_parameters = {} # leave all to default gcode_text = print_organizer.output_gcode(gcode_parameters) - utils.save_to_text_file(gcode_text, OUTPUT_DIR, 'my_gcode.gcode') \ No newline at end of file + utils.save_to_text_file(gcode_text, OUTPUT_DIR, 'my_gcode.gcode') + + + if __name__ == "__main__": + main() diff --git a/docs/examples/05_non_planar_slicing_on_custom_base.rst b/docs/examples/05_non_planar_slicing_on_custom_base.rst index 81c1fc7b..36983f70 100644 --- a/docs/examples/05_non_planar_slicing_on_custom_base.rst +++ b/docs/examples/05_non_planar_slicing_on_custom_base.rst @@ -5,7 +5,7 @@ Non-planar slicing on custom base ************************************ In this example we describe the process of non-planar slicing of a mesh, generating paths that are an offset to its -custom base. We are using the ScalarFieldSlicer, which generates paths as contours of a scalar field defined on every +custom base. We are using the ``ScalarFieldSlicer`` cleass, which generates paths as contours of a scalar field defined on every vertex of the mesh. In this case we create a scalar field with the distance of each vertex from the custom base. .. figure:: figures/05_scalar_field_slicing.PNG @@ -14,6 +14,9 @@ vertex of the mesh. In this case we create a scalar field with the distance of e *Result of scalar field slicing considering the distance of each vertex from the custom base.* +The files for this example can be found on the folder `/examples/5_non_planar_slicing_on_custom_base/`. Once you have +run the python file to generate the results, you can visualize them by opening the grasshopper file +`visualization_scalar_field_slicing.gh`. .. code-block:: python @@ -57,6 +60,7 @@ vertex of the mesh. In this case we create a scalar field with the distance of e # --- Slice model by generating contours of scalar field slicer = ScalarFieldSlicer(mesh, u, no_of_isocurves=50) slicer.slice_model() + # simplify_paths_rdp(slicer, threshold=0.3) slicer_utils.save_to_json(slicer.to_data(), OUTPUT_PATH, 'isocontours.json') # save results to json # --- Print organization calculations (i.e. generation of printpoints with fabrication-related information) diff --git a/docs/examples/06_attributes_transfer.rst b/docs/examples/06_attributes_transfer.rst index e2ebfef5..2929fdaf 100644 --- a/docs/examples/06_attributes_transfer.rst +++ b/docs/examples/06_attributes_transfer.rst @@ -6,7 +6,7 @@ Transferring attributes to PrintPoints Often in 3D printing we need to transfer information from the mesh that is being sliced to the PrintPoints that are used in the fabrication process. We might want, for example, to print paths that are generated from different parts of -the geometry using different parameters. This is enabled by the *transfer_mesh_attributes_to_printpoints()* function, as +the geometry using different parameters. In compas_slicer this can be done using the *transfer_mesh_attributes_to_printpoints()* function, as shown in the example below. During the slicing process each printpoint is projected to the closest mesh face. It takes directly all the face attributes, and it takes the averaged vertex attributes of the face vertices using barycentric coordinates. diff --git a/docs/examples/figures/create_boundaries.png b/docs/examples/figures/create_boundaries.png new file mode 100644 index 00000000..b9d4a15e Binary files /dev/null and b/docs/examples/figures/create_boundaries.png differ diff --git a/docs/examples/figures/input_folder.png b/docs/examples/figures/input_folder.png new file mode 100644 index 00000000..6ffc7360 Binary files /dev/null and b/docs/examples/figures/input_folder.png differ diff --git a/docs/installation.rst b/docs/installation.rst index 62c7c3a3..0677056c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -24,7 +24,6 @@ For example, create an environment named ``my-project`` (or replace with your ow conda create -n my-project compas_slicer conda activate my-project - * Install COMPAS CGAL .. code-block:: bash @@ -75,10 +74,13 @@ Before contributing code: - Add your code in the appropriate folder. If you are not sure where to put it, contact [@ioannaMitropoulou](https://github.com/ioannaMitropoulou). + - Add an example on the examples folder that uses the new functionality. Run the example and make sure it works smoothly. + - Add your name to the authors in README.md. + - Create a pull request for the master branch, where you explain in detail what you fixed. When you create a pull request, there is an automatic action that runs the tests for your code again on the server. Make sure the pull request passes the automatic tests as well. If not, then inspect the result, find what went wrong, fix it, and push again the result on your branch. The action will run again automatically on the open pull request. @@ -90,8 +92,16 @@ Troubleshooting If you encounter a problem that is not described here, please file an issue using the `Issue Tracker `_. -Installing Planarity --------------------- +* Grasshopper components not working + +If despite completing all the compas_slicer installation steps, the compas_slicer grasshopper components still do not work, then +you can fix this by manually adding the correct folder to your paths in Rhino. +In Rhino, type "EditPythonScript", go to Tools > Options > Add to search path and add the following folder: +/compas_slicer/src/grasshopper_visualization' + + + +* Installing Planarity .. code-block:: bash @@ -104,8 +114,7 @@ In that case, install cython using pip (or conda) and then run the installation pip install cython --install-option="--no-cython-compile" -Microsoft Visual C++ Build Tools --------------------------------- +* Microsoft Visual C++ Build Tools .. code-block:: bash @@ -116,8 +125,7 @@ To install the Microsoft Visual C++ Build Tools choose one of the options provid https://www.scivision.dev/python-windows-visual-c-14-required/ and just follow the instructions. Then run the pip installation commands again. -Numpy error ------------ +* Numpy error .. code-block:: bash @@ -130,8 +138,8 @@ Make sure you are in the correct environment and type: pip install numpy==1.19.3 -Fractions error ---------------- +* Fractions error + .. code-block:: bash ImportError: cannot import name 'gcd' from 'fractions' (C:\ProgramData\Anaconda3\envs\compas_slicer\lib\fractions.py) diff --git a/environment.yml b/environment.yml index 614a8b6b..a716052e 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,7 @@ channels: dependencies: - python - pip - - compas=1.0.0 + - compas=1.16.0 - networkx - numpy - progressbar2=3.53 diff --git a/examples/1_planar_slicing_simple/example_1_planar_slicing_simple.py b/examples/1_planar_slicing_simple/example_1_planar_slicing_simple.py index 5f1a56a6..45fea673 100644 --- a/examples/1_planar_slicing_simple/example_1_planar_slicing_simple.py +++ b/examples/1_planar_slicing_simple/example_1_planar_slicing_simple.py @@ -104,7 +104,6 @@ def main(): set_extruder_toggle(print_organizer, slicer) add_safety_printpoints(print_organizer, z_hop=10.0) set_linear_velocity_constant(print_organizer, v=25.0) - set_blend_radius(print_organizer, d_fillet=10.0) # ========================================================================== # Prints out the info of the PrintOrganizer diff --git a/examples/2_curved_slicing/curved_slicing_master.gh b/examples/2_curved_slicing/curved_slicing_master.gh index b7e99e98..5d2eb448 100644 Binary files a/examples/2_curved_slicing/curved_slicing_master.gh and b/examples/2_curved_slicing/curved_slicing_master.gh differ diff --git a/examples/2_curved_slicing/ex2_curved_slicing.py b/examples/2_curved_slicing/ex2_curved_slicing.py index 925a8b9e..6ae88e35 100644 --- a/examples/2_curved_slicing/ex2_curved_slicing.py +++ b/examples/2_curved_slicing/ex2_curved_slicing.py @@ -16,7 +16,7 @@ logger = logging.getLogger('logger') logging.basicConfig(format='%(levelname)s - %(message)s', level=logging.INFO) -DATA_PATH = os.path.join(os.path.dirname(__file__), 'data_vase') +DATA_PATH = os.path.join(os.path.dirname(__file__), 'data_Y_shape') OUTPUT_PATH = utils.get_output_directory(DATA_PATH) OBJ_INPUT_NAME = os.path.join(DATA_PATH, 'mesh.obj') @@ -67,7 +67,6 @@ def main(): set_extruder_toggle(print_organizer, slicer) add_safety_printpoints(print_organizer, z_hop=10.0) - # --- Save printpoints dictionary to json file printpoints_data = print_organizer.output_printpoints_dict() utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json') diff --git a/examples/3_planar_slicing_vertical_sorting/example_3_planar_vertical_sorting.py b/examples/3_planar_slicing_vertical_sorting/example_3_planar_vertical_sorting.py index 119b95c6..454599aa 100644 --- a/examples/3_planar_slicing_vertical_sorting/example_3_planar_vertical_sorting.py +++ b/examples/3_planar_slicing_vertical_sorting/example_3_planar_vertical_sorting.py @@ -41,7 +41,7 @@ def main(): slicer.slice_model() # Sorting into vertical layers and reordering - sort_into_vertical_layers(slicer, max_paths_per_layer=10) + sort_into_vertical_layers(slicer, dist_threshold=25.0, max_paths_per_layer=25) reorder_vertical_layers(slicer, align_with="x_axis") # Post-processing diff --git a/examples/3_planar_slicing_vertical_sorting/planar_slicing.gh b/examples/3_planar_slicing_vertical_sorting/planar_slicing.gh index 4e60f697..07268ea5 100644 Binary files a/examples/3_planar_slicing_vertical_sorting/planar_slicing.gh and b/examples/3_planar_slicing_vertical_sorting/planar_slicing.gh differ diff --git a/examples/5_non_planar_slicing_on_custom_base/visualization_scalar_field_slicing.gh b/examples/5_non_planar_slicing_on_custom_base/visualization_scalar_field_slicing.gh index 0e37981d..7101fe9e 100644 Binary files a/examples/5_non_planar_slicing_on_custom_base/visualization_scalar_field_slicing.gh and b/examples/5_non_planar_slicing_on_custom_base/visualization_scalar_field_slicing.gh differ diff --git a/examples/6_attributes_transfer/slicing_with_attributes.gh b/examples/6_attributes_transfer/slicing_with_attributes.gh index e61169d3..fba15e1b 100644 Binary files a/examples/6_attributes_transfer/slicing_with_attributes.gh and b/examples/6_attributes_transfer/slicing_with_attributes.gh differ diff --git a/requirements.txt b/requirements.txt index 8819f58f..66402d05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ attrs>=19.2.0 -compas>=1.0.0 +compas==1.16.0 networkx>=2.5 numpy progressbar2>=3.53 diff --git a/src/compas_slicer/geometry/print_point.py b/src/compas_slicer/geometry/print_point.py index eda17863..913faeee 100644 --- a/src/compas_slicer/geometry/print_point.py +++ b/src/compas_slicer/geometry/print_point.py @@ -19,8 +19,6 @@ class PrintPoint(object): For planar slicing this is the vertical distance, for curved slicing this is absolute distance. mesh_normal: :class:`compas.geometry.Vector` Normal of the mesh at this PrintPoint. - For planar_slicing it is the projection of the normal on the XY plane. - # TODO: should we change that? up_vector: :class:`compas.geometry.Vector` Vector in up direction. For planar slicing this corresponds to the z axis, for curved slicing it varies. frame: :class:`compas.geometry.Frame` diff --git a/src/compas_slicer/parameters/defaults_layers.py b/src/compas_slicer/parameters/defaults_layers.py index 3fe90ca0..c09c6120 100644 --- a/src/compas_slicer/parameters/defaults_layers.py +++ b/src/compas_slicer/parameters/defaults_layers.py @@ -13,7 +13,5 @@ def layers_default_param(key): default_parameters = \ { 'avg_layer_height': 5.0, - 'max_layer_height': 50.0, - 'min_layer_height': 0.1, - # 'vertical_layers_max_centroid_dist': 25.0 + 'vertical_layers_max_centroid_dist': 25.0 } diff --git a/src/compas_slicer/print_organization/interpolation_print_organizer.py b/src/compas_slicer/print_organization/interpolation_print_organizer.py index 8e09869d..e0aad4a6 100644 --- a/src/compas_slicer/print_organization/interpolation_print_organizer.py +++ b/src/compas_slicer/print_organization/interpolation_print_organizer.py @@ -73,27 +73,22 @@ def create_base_boundaries(self): root_vs = utils.get_mesh_vertex_coords_with_attribute(self.slicer.mesh, 'boundary', 1) root_boundary = BaseBoundary(self.slicer.mesh, [Point(*v) for v in root_vs]) - if self.topo_sort_graph: - if len(self.vertical_layers) > 1: - for i, vertical_layer in enumerate(self.vertical_layers): - parents_of_current_node = self.topo_sort_graph.get_parents_of_node(i) - if len(parents_of_current_node) == 0: - boundary = root_boundary - else: - boundary_pts = [] - for parent_index in parents_of_current_node: - parent = self.vertical_layers[parent_index] - boundary_pts.extend(parent.paths[-1].points) - boundary = BaseBoundary(self.slicer.mesh, boundary_pts) - bs.append(boundary) - else: - bs.append(root_boundary) + if len(self.vertical_layers) > 1: + for i, vertical_layer in enumerate(self.vertical_layers): + parents_of_current_node = self.topo_sort_graph.get_parents_of_node(i) + if len(parents_of_current_node) == 0: + boundary = root_boundary + else: + boundary_pts = [] + for parent_index in parents_of_current_node: + parent = self.vertical_layers[parent_index] + boundary_pts.extend(parent.paths[-1].points) + boundary = BaseBoundary(self.slicer.mesh, boundary_pts) + bs.append(boundary) else: - logger.critical("""no topology graph was created, no base boundaries created, - output will be degenerated. A likely cause for topology sorting to fail is that - non-continuous paths were created. When creating paths with variable - layer heights, it may very well be that the non-continuous paths are - created, while this is not yet a supported feature""") + bs.append(root_boundary) + + assert(len(bs) == len(self.vertical_layers)) # save intermediary outputs b_data = {i: b.to_data() for i, b in enumerate(bs)} @@ -121,7 +116,7 @@ def create_printpoints(self): current_layer_index += 1 # (2) --- Select order of vertical layers - if len(self.vertical_layers) > 1: # the you need to select one topological order + if len(self.vertical_layers) > 1: # then you need to select one topological order if not self.topo_sort_graph: logger.error("no topology graph found, cannnot set the order of vertical layers") @@ -135,11 +130,7 @@ def create_printpoints(self): # (3) --- Then create the printpoints of all the vertical layers in the selected order for index, i in enumerate(self.selected_order): layer = self.vertical_layers[i] - self.printpoints_dict['layer_%d' % current_layer_index] = {} - try: - self.printpoints_dict['layer_%d' % current_layer_index] = self.get_layer_ppts(layer, self.base_boundaries[i]) - except IndexError: - logging.exception("no layer print points found for layer %d" % current_layer_index) + self.printpoints_dict['layer_%d' % current_layer_index] = self.get_layer_ppts(layer, self.base_boundaries[i]) current_layer_index += 1 def get_layer_ppts(self, layer, base_boundary):