Skip to content

Commit

Permalink
Merge pull request #138 from compas-dev/documentation_updates
Browse files Browse the repository at this point in the history
Documentation updates
  • Loading branch information
ioannaMitropoulou authored Jul 28, 2021
2 parents dde7a08 + 8e12e9c commit e44a382
Show file tree
Hide file tree
Showing 26 changed files with 4,705 additions and 8,092 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Unreleased
----------

**Added**
* Documentation updates

**Changed**

Expand Down
4 changes: 4 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ Here you can find some examples files for compas_slicer

examples/01_planar_slicing_simple
examples/02_curved_slicing_simple
examples/03_planar_slicing_vertical_sorting
examples/04_gcode_generation
examples/05_non_planar_slicing_on_custom_base
examples/06_attributes_transfer
6 changes: 3 additions & 3 deletions docs/examples/02_curved_slicing_simple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ A general introduction of the concepts organization of compas_slicer can be foun

Make sure to read the :ref:`example on simple planar slicing <compas_slicer_example_1>` before reading this example,
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.
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 <https://dl.acm.org/doi/fullHtml/10.1145/3424630.3425408>`_.


.. figure:: figures/curved_slicing.PNG
.. figure:: figures/02_curved_slicing.PNG
:figclass: figure
:class: figure-img img-fluid

Expand Down
93 changes: 93 additions & 0 deletions docs/examples/03_planar_slicing_vertical_sorting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.. _compas_slicer_example_3:

************************************
Planar slicing with vertical sorting
************************************

During the print of branching shell shapes, the layers can be sorted;
- following a horizontal logic, with all paths that are on the same height being adjacent.
- following a vertical logic, with all paths that are on the same branch being adjacent.
The vertical sorting can enable significant reduction of the interruptions of the print and the traveling of the tool
from one path to the next, as it is shown in the illustration below.

.. figure:: figures/03_print_organization_planar_slicing_vertical_sorting.png
:figclass: figure
:class: figure-img img-fluid

*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.

.. code-block:: python
import os
import logging
import compas_slicer.utilities as utils
from compas_slicer.pre_processing import move_mesh_to_point
from compas_slicer.slicers import PlanarSlicer
from compas_slicer.post_processing import generate_brim
from compas_slicer.post_processing import simplify_paths_rdp
from compas_slicer.post_processing import sort_into_vertical_layers
from compas_slicer.post_processing import reorder_vertical_layers
from compas_slicer.post_processing import seams_smooth
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.datastructures import Mesh
from compas.geometry import Point
# ==============================================================================
# Logging
# ==============================================================================
logger = logging.getLogger('logger')
logging.basicConfig(format='%(levelname)s-%(message)s', level=logging.INFO)
# ==============================================================================
# Select location of data folder and specify model to slice
# ==============================================================================
DATA = os.path.join(os.path.dirname(__file__), 'data')
OUTPUT_DIR = utils.get_output_directory(DATA) # creates 'output' folder if it doesn't already exist
MODEL = 'distorted_v_closed_mid_res.obj'
def main():
compas_mesh = Mesh.from_obj(os.path.join(DATA, MODEL))
move_mesh_to_point(compas_mesh, Point(0, 0, 0))
# Slicing
slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=5.0)
slicer.slice_model()
# Sorting into vertical layers and reordering
sort_into_vertical_layers(slicer, max_paths_per_layer=10)
reorder_vertical_layers(slicer, align_with="x_axis")
# Post-processing
generate_brim(slicer, layer_width=3.0, number_of_brim_offsets=5)
simplify_paths_rdp(slicer, threshold=0.7)
seams_smooth(slicer, smooth_distance=10)
slicer.printout_info()
save_to_json(slicer.to_data(), OUTPUT_DIR, 'slicer_data.json')
# PlanarPrintOrganization
print_organizer = PlanarPrintOrganizer(slicer)
print_organizer.create_printpoints()
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)
print_organizer.printout_info()
printpoints_data = print_organizer.output_printpoints_dict()
utils.save_to_json(printpoints_data, OUTPUT_DIR, 'out_printpoints.json')
if __name__ == "__main__":
main()
67 changes: 67 additions & 0 deletions docs/examples/04_gcode_generation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.. _compas_slicer_example_4:

************************************
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.


.. code-block:: python
import os
import logging
import compas_slicer.utilities as utils
from compas_slicer.pre_processing import move_mesh_to_point
from compas_slicer.slicers import PlanarSlicer
from compas_slicer.post_processing import generate_brim
from compas_slicer.post_processing import simplify_paths_rdp
from compas_slicer.post_processing import seams_smooth
from compas_slicer.print_organization import PlanarPrintOrganizer
from compas_slicer.print_organization import set_extruder_toggle
from compas_slicer.utilities import save_to_json
from compas_slicer.parameters import get_param
from compas.datastructures import Mesh
from compas.geometry import Point
logger = logging.getLogger('logger')
logging.basicConfig(format='%(levelname)s-%(message)s', level=logging.INFO)
DATA = os.path.join(os.path.dirname(__file__), 'data')
OUTPUT_DIR = utils.get_output_directory(DATA) # creates 'output' folder if it doesn't already exist
MODEL = 'simple_vase_open_low_res.obj'
def main():
compas_mesh = Mesh.from_obj(os.path.join(DATA, MODEL))
delta = get_param({}, key='delta', defaults_type='gcode') # boolean for delta printers
print_volume_x = get_param({}, key='print_volume_x', defaults_type='gcode') # in mm
print_volume_y = get_param({}, key='print_volume_y', defaults_type='gcode') # in mm
if delta:
move_mesh_to_point(compas_mesh, Point(0, 0, 0))
else:
move_mesh_to_point(compas_mesh, Point(print_volume_x/2, print_volume_y/2, 0))
# ----- slicing
slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=4.5)
slicer.slice_model()
generate_brim(slicer, layer_width=3.0, number_of_brim_offsets=4)
simplify_paths_rdp(slicer, threshold=0.6)
seams_smooth(slicer, smooth_distance=10)
slicer.printout_info()
save_to_json(slicer.to_data(), OUTPUT_DIR, 'slicer_data.json')
# ----- print organization
print_organizer = PlanarPrintOrganizer(slicer)
print_organizer.create_printpoints()
# Set fabrication-related parameters
set_extruder_toggle(print_organizer, slicer)
print_organizer.printout_info()
# 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')
70 changes: 70 additions & 0 deletions docs/examples/05_non_planar_slicing_on_custom_base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
.. _compas_slicer_example_5:

************************************
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
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
:figclass: figure
:class: figure-img img-fluid

*Result of scalar field slicing considering the distance of each vertex from the custom base.*


.. code-block:: python
import logging
from compas.geometry import distance_point_point
from compas.datastructures import Mesh
import os
import compas_slicer.utilities as slicer_utils
from compas_slicer.post_processing import simplify_paths_rdp
from compas_slicer.slicers import ScalarFieldSlicer
import compas_slicer.utilities as utils
from compas_slicer.print_organization import ScalarFieldPrintOrganizer
logger = logging.getLogger('logger')
logging.basicConfig(format='%(levelname)s-%(message)s', level=logging.INFO)
DATA_PATH = os.path.join(os.path.dirname(__file__), 'data')
OUTPUT_PATH = slicer_utils.get_output_directory(DATA_PATH)
MODEL = 'geom_to_slice.obj'
BASE = 'custom_base.obj'
if __name__ == '__main__':
# --- load meshes
mesh = Mesh.from_obj(os.path.join(DATA_PATH, MODEL))
base = Mesh.from_obj(os.path.join(DATA_PATH, BASE))
# --- Create per-vertex scalar field (distance of every vertex from the custom base)
pts = [mesh.vertex_coordinates(v_key, axes='xyz') for v_key in
mesh.vertices()] # list of the vertex coordinates of the mesh as compas.geometry.Point instances
_, projected_pts = utils.pull_pts_to_mesh_faces(base, pts) # list with projections of all mesh vertices on the mesh
u = [distance_point_point(pt, proj_pt) for pt, proj_pt in
zip(pts, projected_pts)] # list with distance between initial+projected pts (one per vertex)
utils.save_to_json(u, OUTPUT_PATH, 'distance_field.json') # save distance field to json for visualization
# --- assign the scalar field to the mesh's attributes, under the name 'scalar_field'
mesh.update_default_vertex_attributes({'scalar_field': 0.0})
for i, (v_key, data) in enumerate(mesh.vertices(data=True)):
data['scalar_field'] = u[i]
# --- 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)
simplify_paths_rdp(slicer, threshold=0.3)
print_organizer = ScalarFieldPrintOrganizer(slicer, parameters={}, DATA_PATH=DATA_PATH)
print_organizer.create_printpoints()
print_organizer.printout_info()
printpoints_data = print_organizer.output_printpoints_dict()
utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json') # save results to json
106 changes: 106 additions & 0 deletions docs/examples/06_attributes_transfer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.. _compas_slicer_example_6:

************************************
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
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.

.. figure:: figures/06_attributes.png
:figclass: figure
:class: figure-img img-fluid

*PrintPoints with visualization of the attribute: overhang angle of the underlying mesh.*


.. code-block:: python
import logging
import os
from compas.geometry import Point, Vector, distance_point_plane, normalize_vector
from compas.datastructures import Mesh
import compas_slicer.utilities as slicer_utils
from compas_slicer.post_processing import simplify_paths_rdp
from compas_slicer.slicers import PlanarSlicer
import compas_slicer.utilities.utils as utils
from compas_slicer.utilities.attributes_transfer import transfer_mesh_attributes_to_printpoints
from compas_slicer.print_organization import PlanarPrintOrganizer
import numpy as np
logger = logging.getLogger('logger')
logging.basicConfig(format='%(levelname)s-%(message)s', level=logging.INFO)
DATA_PATH = os.path.join(os.path.dirname(__file__), 'data')
OUTPUT_PATH = slicer_utils.get_output_directory(DATA_PATH)
MODEL = 'distorted_v_closed_low_res.obj'
if __name__ == '__main__':
# load mesh
mesh = Mesh.from_obj(os.path.join(DATA_PATH, MODEL))
# --------------- Add attributes to mesh
# Face attributes can be anything (ex. float, bool, array, text ...)
# Vertex attributes can only be entities that can be meaningfully multiplied with a float (ex. float, np.array ...)
# overhand attribute - Scalar value (per face)
mesh.update_default_face_attributes({'overhang': 0.0})
for f_key, data in mesh.faces(data=True):
face_normal = mesh.face_normal(f_key, unitized=True)
data['overhang'] = Vector(0.0, 0.0, 1.0).dot(face_normal)
# face looking towards the positive y axis - Boolean value (per face)
mesh.update_default_face_attributes({'positive_y_axis': False})
for f_key, data in mesh.faces(data=True):
face_normal = mesh.face_normal(f_key, unitized=True)
is_positive_y = Vector(0.0, 1.0, 0.0).dot(face_normal) > 0 # boolean value
data['positive_y_axis'] = is_positive_y
# distance from plane - Scalar value (per vertex)
mesh.update_default_vertex_attributes({'dist_from_plane': 0.0})
plane = (Point(0.0, 0.0, -30.0), Vector(0.0, 0.5, 0.5))
for v_key, data in mesh.vertices(data=True):
v_coord = mesh.vertex_coordinates(v_key, axes='xyz')
data['dist_from_plane'] = distance_point_plane(v_coord, plane)
# direction towards point - Vector value (per vertex)
mesh.update_default_vertex_attributes({'direction_to_pt': 0.0})
pt = Point(4.0, 1.0, 0.0)
for v_key, data in mesh.vertices(data=True):
v_coord = mesh.vertex_coordinates(v_key, axes='xyz')
data['direction_to_pt'] = np.array(normalize_vector(Vector.from_start_end(v_coord, pt)))
# --------------- Slice mesh
slicer = PlanarSlicer(mesh, slicer_type="default", layer_height=5.0)
slicer.slice_model()
simplify_paths_rdp(slicer, threshold=1.0)
slicer_utils.save_to_json(slicer.to_data(), OUTPUT_PATH, 'slicer_data.json')
# --------------- Create printpoints
print_organizer = PlanarPrintOrganizer(slicer)
print_organizer.create_printpoints()
# --------------- Transfer mesh attributes to printpoints
transfer_mesh_attributes_to_printpoints(mesh, print_organizer.printpoints_dict)
# --------------- Save printpoints to json (only json-serializable attributes are saved)
printpoints_data = print_organizer.output_printpoints_dict()
utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json')
# --------------- Print the info to see the attributes of the printpoints (you can also visualize them on gh)
print_organizer.printout_info()
# --------------- Save printpoints attributes for visualization
overhangs_list = print_organizer.get_printpoints_attribute(attr_name='overhang')
positive_y_axis_list = print_organizer.get_printpoints_attribute(attr_name='positive_y_axis')
dist_from_plane_list = print_organizer.get_printpoints_attribute(attr_name='dist_from_plane')
direction_to_pt_list = print_organizer.get_printpoints_attribute(attr_name='direction_to_pt')
utils.save_to_json(overhangs_list, OUTPUT_PATH, 'overhangs_list.json')
utils.save_to_json(positive_y_axis_list, OUTPUT_PATH, 'positive_y_axis_list.json')
utils.save_to_json(dist_from_plane_list, OUTPUT_PATH, 'dist_from_plane_list.json')
utils.save_to_json(utils.point_list_to_dict(direction_to_pt_list), OUTPUT_PATH, 'direction_to_pt_list.json')
Binary file added docs/examples/figures/02_curved_slicing.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/examples/figures/06_attributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/examples/figures/curved_slicing.PNG
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
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

Expand Down Expand Up @@ -67,14 +65,6 @@ def main():
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()


if __name__ == "__main__":
main()
Loading

0 comments on commit e44a382

Please sign in to comment.