This project is a part of my Master's thesis: Software for efficient use of material in 2D machining.
It implements a shape nesting algorithm and can be used as a GUI app, or you can use the underlying API to leverage the algorithms themselves.
required dependencies:
- Shapely library
- numpy
- pybind11 (!required as of now, even if not using libnfporb, TODO: make optional)
optional dependencies:
- PyQt5, and therefore:
- matplotlib
- libnfporb_interface (c++), and therefore:
- Boost library (<= 1.65),
- cmake
- pybind11
While the app itself is written in Python, a part of it relies on the libnfporb library by Amir Hassan (kalabala) written in c++, which must be built before (see Building libnfporb), but it can be used without it, as there is a faster but way less accurate fallback algorithm in place.
The program also requires you to install the Shapely Python library, which is available through PyPi, but still has to be built from source, or the binary can be downloaded from here (Windows only) and installed manually. If you use Anaconda, it can be also easily installed using conda install shapely
.
You will need the Boost library (or at least Boost.geometry that it includes) IMPORTANT! Make sure to install version <= 1.65, as there is a bug (or a fix) in Boost.geometry that breaks the libnfporb library's functionality.
If you decide to go with the recommended way, you will need to define two system environment variables: %PYTHONPATH%
and %BOOST_ROOT%
.
For Windows users, there is an auto build script provided inside wasteoptimiser/nfp_interface
. It requires you to have cmake and Visual Studio 2017/2019 (for other versions, a slight modification of the script is needed).
In command prompt just cd
to the wasteoptimiser/nfp_interface
folder and then run it with ./build
. It should automatically load the Developer Command Prompt for Visual Studio. If it does not, you have to open it manually (Just start typing "Developer C..." into the search field and it should come right up, then proceed to launch the script from that). Then it will detect the Python version (64/32bit) and build the corresponding library for you.
When you see no errors in the output, you can assume the library has been correctly built and a new file named libnfporb_interface.pyd will appear in the folder.
Use cmake
to generate the project for your favourite compiler with the CmakeLists.txt
file provided. In the wasteoptimiser/nfp_interface
folder, run:
mkdir build
cd build
cmake .. -G <your favourite generator>
Then, build it with your favourite compiler. After that, you need to place the build libnfporb_interface.dll
into the wasteoptimiser/nfp_interface
folder and rename it to libnfporb_interface.pyd
.
If you have installed PyQt5 and matplotlib, you can launch the GUI from the parent folder of wasteoptimiser
with
# by running the module as a script
python -m wasteoptimiser
# or simply by using the provided run script
python run.py
In the Input section, you can then browse to a folder with your g-codes, the parser will then try to parse each file as g-code and display the valid ones in the list. You can then select how much of each shape you want to place and whether to use the shape's convex hull as the reference (this is very much preferred for complicated shapes when using the "Use NFP" option in Optimiser, as it may take a loooong time to find a NFP for them).
In Settings you can select the dimensions of your workspace, clearances between objects and the edge and the preferred location at which the shapes should be placed. "Small holes first" ensures that shapes are first tested for placement in the smallest regions found, before conforming to the "Preferred location" option.
In the actual Workspace, you can use the buttons "Add", "Subtract" and "Remove" to draw forbidden areas (i.e. holes) where the shapes cannot be placed.
In the Optimiser section, you have the option to "Use NFP" (which leverages the libnfporb functionality) along with the number of rotations to test for. By disabling this option a fallback algorithm that uses the smallest enclosing circle will be used.
The "Local optimisation" which tries to place the shape as closely to other shapes by minimising the open area around it - it can achieve better results with irregular geometry, but slows down the process a bit.
The "Start" button starts the process of placing all the selected shapes, "Stop" can be used to stop it.
The top menu item "Workspace" lets you import and export created workspaces.
If you only need to use the API, import it in your Python script:
from wasteoptimiser.api import api
Then, you can create an instance of it with
my_api = api.Api()
By default, all the log messages will be printed to the console, if you want, you can use the built-in logger where you can select the logging and printing level (e.g. do not print debug messages and stuff).
from wasteoptimiser.logger import logger
my_logger = logger.Logger("path/to/log", ...) # for more options see API reference
and then supply it to the API constructor my_api = api.Api(logger)
.
First, construct the shape dictionary using (note that an absolute path is needed as of now)
my_api.constructShapeDict("full/path/to/gcodes/")
This automatically parses the G-Codes from the given folder and constructs a dictionary of shapes in my_api.shape_dict
. The keys are the file names and the values are dictionaries of the form {'count': int, 'shape': shape, 'convex': bool}
, where shape
is a list of polygons.
The count and whether to use the shape's convex hull (default) as its representation, can be set using
my_api.setShapeCount('shape.gcode', 3)
my_api.setShapeConvex('shape.gcode', False)
The workspace with holes can be imported using
my_api.loadWorkspace('path/to/workspace.json')
The settings are currently a bit all over the place, but they can be set using
my_api.optimiser.hole_offset = 5 # minimual clearance between shapes and holes
my_api.optimiser.edge_offset = 5 # minimal clearance between shapes and boundary
my_api.optimiser.preffered_pos = 1 # 0-6 (top left, top right, bottom left..)
my_api.settings.use_nfp = True # False to use smallest enclosing circle method
my_api.settings.nfp_rotations = 4 # number of shape rotations for NFP construction
my_api.settings.local_optimisation = True
To start the optimisation, run
my_api.placeAllSelectedShapes()
For drawing purposes, the geometry can be extracted as list of polygons
board_polygon = my_api.optimiser.getBoardShape()
holes_polygons = my_api.optimiser.getHoles(htype='holes')
shape_polygons = my_a0pi.optimiser.getHoles(htype='shapes')
# without htype returns both holes and shapes as holes
To get the list of filenames, locations and angles for export, use
my_api.optimiser.getShapeNamesPositions()
The API is currently still under development and many things can change, please refer to the docstrings of functions in wasteoptimiser/api/api.py