-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #138 from compas-dev/documentation_updates
Documentation updates
- Loading branch information
Showing
26 changed files
with
4,705 additions
and
8,092 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ Unreleased | |
---------- | ||
|
||
**Added** | ||
* Documentation updates | ||
|
||
**Changed** | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
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
BIN
+656 KB
docs/examples/figures/03_print_organization_planar_slicing_vertical_sorting.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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.