Skip to content

Commit

Permalink
Merge pull request #151 from compas-dev/bug_fixes_joris
Browse files Browse the repository at this point in the history
Added sort_paths_minimum_travel_time, some bug fixes and documentation updates. Should bump to 0.4.0
  • Loading branch information
joburger authored Nov 18, 2021
2 parents 2f46d42 + 1509d32 commit 145ee0a
Show file tree
Hide file tree
Showing 24 changed files with 315 additions and 98 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,25 @@ Unreleased

**Added**
* Documentation updates
* rdp libigl function
* rdp libigl function (faster than the regular rdp)
* sort_paths_minimum_travel_time: Function to sort paths by least travel time between contours in a layer

**Changed**
* Changed the blend radius to add a blend radius of 0 for the first and last point of a path
* Changed planar_slicing_cgal to add the possibility of slicing open paths with planar_slicing_cgal
* Added the option to toggle generation of mesh normals on/off in create_printpoints
* Added the possibility to slice only a certain section of the geometry by using slice_height_range in the PlanarSlicer

**Fixed**
* Fixed some bugs in seams_align
* Small bug in extruder_toggle
* Small bug in simplify_paths_rdp_igl with printing remaining no of points
* Bug in seams_smooth

**Deprecated**

* close_paths in the BaseSlicer is unused for now, as it should not be necessary

**Removed**

0.3.5
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/06_attributes_transfer.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.. _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
Expand Down
6 changes: 3 additions & 3 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Basic installation steps
========================

Step 1: Install compas slicer
------------------------------
-----------------------------


The recommended way to install `compas_slicer` is with `conda <https://conda.io/docs/>`_.
Expand All @@ -25,7 +25,7 @@ For example, create an environment named ``my-project`` (or replace with your ow
Step 2: Optional installation steps
------------------------------------
-----------------------------------

* COMPAS Viewers

Expand Down Expand Up @@ -109,7 +109,7 @@ Make sure you are in the correct environment and type:
pip install numpy==1.19.3
Fractions error
-----------
---------------
.. code-block:: bash
ImportError: cannot import name 'gcd' from 'fractions' (C:\ProgramData\Anaconda3\envs\compas_slicer\lib\fractions.py)
Expand Down
66 changes: 59 additions & 7 deletions docs/tutorials/01_introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,68 @@
Introduction
****************************

* General overview of the pipeline
Overview
========

* Geometry classes organization
The COMPAS SLICER package broadly contains four blocks of code:

* Brief explanation of the organization of the remaining functionality (ex. what is pre-processing, what is post-processing ..)
* **Geometry:** contains all geometrical entities
* **Slicers:** provides the functionality that generates the sliced model
* **Print organizers:** provides the functionality for embedding fabrication related information to the sliced model
* **Visualization:** visualizes the results

* Folder structure of examples
Geometry
--------

* overview of slicers
The geometry part of COMPAS SLICER contains all of the geometrical entities that are specific to COMPAS SLICER:
The ``Layer``, ``VerticalLayer``, ``Path``, and ``PrintPoint``. A ``Layer`` is essentially a single slice of the model.
In case of planar slicing, all points on one ``Layer`` are at the same height, however, this is not the case for non-planar slicing.
A ``VerticalLayer`` is a special method for organizing layers, in this case several ``Layers`` are grouped together to form a
group of layers. The ``VerticalLayer`` is only relevant in case a model consists out of multiple ``Paths``.
A ``Layer`` contains a list of one or mulitple ``Paths``. Multiple ``Paths`` occur when a model has multiple branches (see image).
Each ``Path`` is a closed or open contour and contains a list of ``compas.geometry.Point`` objects.
Lastly, the ``PrintPoint`` is essentially the same as the Points in the ``Path``, however, it contains additional fabrication parameters.

* Reference to the 2 examples for further info
.. figure:: intro_figures/01_layer.png
:figclass: figure
:class: figure-img img-fluid

* Reference to gh visualization tutorial for further info.
.. figure:: intro_figures/02_vert_layer.png
:figclass: figure
:class: figure-img img-fluid

Slicers
--------

Different slicers are integrated into COMPAS SLICER.

* **Planar slicer:** Most basic slicer, slices the model through an intersection of a plane parallel to the XY plane.
* **Interpolation slicer:** Creates non-planar slices by interpolating between boundary curves.
* **Scalar field slicer:** Uses a scalar field to create non-planar slices.
* **UV slicer:** TBA.

Print organizers
----------------

After the model is sliced using one of the available slicers, the toolpath can be generated using the PrintOrganizers.
The ``PrintOrganizer`` creates ``PrintPoint`` objects out of the ``compas.geometry.Point`` objects that are used throughout the slicing process.
Using different functions, fabrication parameters can be added or modified to the ``PrintPoints``. Examples of this include:
setting the velocity, setting the blend radius, or adding safety printpoints (z-hop).

Visualization
-------------

Both the Slicer output and the PrintOrganizer output can be visualized in different ways. These are described in Tutorial 2.

Folder structure
================

The functions of COMPAS SLICER are grouped into different folders:

* **geometry:**
* **parameters:** Contains functions for interacting with the parameters of the slicing and print organization process.
* **post_processing:** Various functions that are used *after* a model is sliced, such as different methods of sorting, or adding a brim/raft.
* **pre_processing:** Various functions that are used *before slicing a model*, such as repositioning, or various operations for curved slicing.
* **print_organization:**
* **slicers:**
* **utilities:**
Binary file added docs/tutorials/intro_figures/01_layer.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 added docs/tutorials/intro_figures/02_vert_layer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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
Expand Down Expand Up @@ -45,7 +46,7 @@ def main():
# ==========================================================================
# Move to origin
# ==========================================================================
move_mesh_to_point(compas_mesh, Point(0, 0, 0))
# move_mesh_to_point(compas_mesh, Point(0, 0, 0))

# ==========================================================================
# Slicing
Expand All @@ -56,6 +57,8 @@ def main():
slicer = PlanarSlicer(compas_mesh, slicer_type="cgal", layer_height=1.5)
slicer.slice_model()

seams_align(slicer, "next_path")

# ==========================================================================
# Generate brim / raft
# ==========================================================================
Expand Down
Binary file modified examples/1_planar_slicing_simple/planar_slicing_master.gh
Binary file not shown.
Binary file modified examples/2_curved_slicing/curved_slicing_master.gh
Binary file not shown.
Binary file modified examples/3_planar_slicing_vertical_sorting/planar_slicing.gh
Binary file not shown.
Binary file modified examples/6_attributes_transfer/slicing_with_attributes.gh
Binary file not shown.
3 changes: 3 additions & 0 deletions src/compas_slicer/post_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
seams_smooth
sort_into_vertical_layers
reorder_vertical_layers
sort_paths_minimum_travel_time
zig_zag_open_paths
Additional
==========
Expand All @@ -49,6 +51,7 @@
from .seams_smooth import * # noqa: F401 E402 F403
from .sort_into_vertical_layers import * # noqa: F401 E402 F403
from .reorder_vertical_layers import * # noqa: F401 E402 F403
from .sort_paths_minimum_travel_time import * # noqa: F401 E402 F403

# Orienting
from .unify_paths_orientation import * # noqa: F401 E402 F403
Expand Down
95 changes: 54 additions & 41 deletions src/compas_slicer/post_processing/seams_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,46 @@ def seams_align(slicer, align_with="next_path"):
for i, layer in enumerate(slicer.layers):
for j, path in enumerate(layer.paths):

align_seams_for_current_path = path.is_closed # should not happen if path is open

if align_seams_for_current_path:
if align_with == "next_path":
pt_to_align_with = None # make sure aligning point is cleared

# determines the correct point to align the current path with
if len(layer.paths) == 1 and i == 0:
# if ONE PATH and FIRST LAYER
# >>> align with second layer
pt_to_align_with = slicer.layers[i + 1].paths[0].points[0]
if len(layer.paths) == 1 and i != 0:
last_path_index = len(slicer.layers[i - 1].paths) - 1
# if ONE PATH and NOT FIRST LAYER
# >>> align with previous layer
pt_to_align_with = slicer.layers[i - 1].paths[last_path_index].points[-1]
if len(layer.paths) != 1 and i == 0 and j == 0:
# if MULTIPLE PATHS and FIRST LAYER and FIRST PATH
# >>> align with second path of first layer
pt_to_align_with = slicer.layers[i].paths[i + 1].points[-1]
if len(layer.paths) != 1 and j != 0:
# if MULTIPLE PATHS and NOT FIRST PATH
# >>> align with previous path
pt_to_align_with = slicer.layers[i].paths[j - 1].points[-1]
if len(layer.paths) != 1 and i != 0 and j == 0:
# if MULTIPLE PATHS and NOT FIRST LAYER and FIRST PATH
# >>> align with first path of previous layer
last_path_index = len(slicer.layers[i - 1].paths) - 1
pt_to_align_with = slicer.layers[i - 1].paths[last_path_index].points[-1]

elif align_with == "origin":
pt_to_align_with = Point(0, 0, 0)
elif align_with == "x_axis":
pt_to_align_with = Point(2 ** 32, 0, 0)
elif align_with == "y_axis":
pt_to_align_with = Point(0, 2 ** 32, 0)
elif isinstance(align_with, Point):
pt_to_align_with = align_with
else:
raise NameError("Unknown align_with : " + str(align_with))

# CLOSED PATHS
if path.is_closed:
# get the points of the current layer and path
path_to_change = layer.paths[j].points

Expand All @@ -47,56 +84,32 @@ def seams_align(slicer, align_with="next_path"):
else:
first_last_point_the_same = False

if align_with == "next_path":
pt_to_align_with = None # make sure aligning point is cleared

# determines the correct point to align the current layer with
if len(layer.paths) == 1 and i == 0:
# if ONE PATH and FIRST LAYER
# >>> align with second layer
pt_to_align_with = slicer.layers[i + 1].paths[0].points[0]
if len(layer.paths) == 1 and i != 0:
# if ONE PATH and NOT FIRST LAYER
# >>> align with previous layer
pt_to_align_with = slicer.layers[i - 1].paths[0].points[0]
if len(layer.paths) != 1 and i == 0 and j == 0:
# if MULTIPLE PATHS and FIRST LAYER and FIRST PATH
# >>> align with second path of first layer
pt_to_align_with = slicer.layers[i].paths[i + 1].points[0]
if len(layer.paths) != 1 and j != 0:
# if MULTIPLE PATHS and NOT FIRST PATH
# >>> align with previous path
pt_to_align_with = slicer.layers[i].paths[j - 1].points[0]
if len(layer.paths) != 1 and i != 0 and j == 0:
# if MULTIPLE PATHS and NOT FIRST LAYER and FIRST PATH
# >>> align with first path of previous layer
pt_to_align_with = slicer.layers[i - 1].paths[j].points[0]

elif align_with == "origin":
pt_to_align_with = Point(0, 0, 0)
elif align_with == "x_axis":
pt_to_align_with = Point(2 ** 32, 0, 0)
elif align_with == "y_axis":
pt_to_align_with = Point(0, 2 ** 32, 0)
elif isinstance(align_with, Point):
pt_to_align_with = align_with
else:
raise NameError("Unknown align_with : " + str(align_with))

# computes distance between pt_to_align_with and the current path points
distance_current_pt_align_pt = [distance_point_point(pt_to_align_with, pt) for pt in path_to_change]
# gets the index of the closest point by looking for the minimum
new_start_index = distance_current_pt_align_pt.index(min(distance_current_pt_align_pt))
# shifts the list by the distance determined
shift_list = path_to_change[new_start_index:] + path_to_change[:new_start_index]
# shifts the list by the distance determined
# layer.paths[j].points = shift_list

if first_last_point_the_same:
shift_list = shift_list + [shift_list[0]]

layer.paths[j].points = shift_list

else:
# OPEN PATHS
path_to_change = layer.paths[j].points

# get the distance between the align point and the start/end point
start = path_to_change[0]
end = path_to_change[-1]
d_start = distance_point_point(start, pt_to_align_with)
d_end = distance_point_point(end, pt_to_align_with)

# if closer to end point > reverse list
if d_start > d_end:
layer.paths[j].points.reverse()


if __name__ == "__main__":
pass
36 changes: 19 additions & 17 deletions src/compas_slicer/post_processing/seams_smooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,25 @@ def seams_smooth(slicer, smooth_distance):

for i, layer in enumerate(slicer.layers):
if len(layer.paths) == 1 or isinstance(layer, compas_slicer.geometry.VerticalLayer):
for path in layer.paths:
pt0 = path.points[0]
# only points in the first half of a path should be evaluated
half_of_path = path.points[:int(len(path.points)/2)]
for point in half_of_path:
if distance_point_point(pt0, point) < smooth_distance:
# remove points if within smooth_distance
path.points.pop(0)
else:
# create new point at a distance of the
# 'smooth_distance' from the first point,
# so that all seams are of equal length
vect = Vector.from_start_end(pt0, point)
vect.unitize()
new_pt = pt0 + (vect * smooth_distance)
path.points.insert(0, new_pt)
break
for j, path in enumerate(layer.paths):
if path.is_closed: # only for closed paths
pt0 = path.points[0]
# only points in the first half of a path should be evaluated
half_of_path = path.points[:int(len(path.points)/2)]
for point in half_of_path:
if distance_point_point(pt0, point) < smooth_distance:
# remove points if within smooth_distance
path.points.pop(0)
else:
# create new point at a distance of the
# 'smooth_distance' from the first point,
# so that all seams are of equal length
vect = Vector.from_start_end(pt0, point)
vect.unitize()
new_pt = pt0 + (vect * smooth_distance)
path.points.insert(0, new_pt)
path.points.pop(-1) # remove last point
break
else:
logger.warning("Smooth seams only works for layers consisting out of a single path, or for vertical layers."
"\nPaths were not changed, seam smoothing skipped for layer %i" % i)
Expand Down
1 change: 1 addition & 0 deletions src/compas_slicer/post_processing/simplify_paths_rdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def simplify_paths_rdp_igl(slicer, threshold):
pts = np.array([[pt[0], pt[1], pt[2]] for pt in path.points])
S, J, Q = igl.ramer_douglas_peucker(pts, threshold)
path.points = [Point(pt[0], pt[1], pt[2]) for pt in S]
remaining_pts_num += len(path.points)
logger.info('%d Points remaining after rdp simplification' % remaining_pts_num)

except PluginNotInstalledError:
Expand Down
Loading

0 comments on commit 145ee0a

Please sign in to comment.