├── .gitignore
├── COPYING.TXT
├── Changelog
├── INSTALL.TXT
├── LICENSE.TXT
├── MANIFEST.in
├── Makefile
├── README.TXT
├── Tests
├── CylindricalCutterTest.py
├── DropCutterTest.py
├── PolygonExtractorTest.py
├── PushCutterTest.py
├── STLImporterTest.py
├── SimulationTest.py
├── SphericalCutterTest.py
├── ToroidalCutterTest.py
├── TriangleKdtreeTest.py
├── TriangleTest.py
└── VisualizationTest.py
├── debian
├── changelog
├── compat
├── control
├── copyright
├── dirs
├── docs
├── manpages
├── patches
│ └── series
├── pycam.mime
├── pycam.sharedmimeinfo
├── pycompat
├── pyversions
├── rules
├── source
│ └── format
└── watch
├── doc
└── ParameterGroup.dia
├── man
├── Makefile
└── pycam.1.inc
├── pycam
├── Cutters
│ ├── BaseCutter.py
│ ├── CylindricalCutter.py
│ ├── SphericalCutter.py
│ ├── ToroidalCutter.py
│ └── __init__.py
├── Exporters
│ ├── EMCToolExporter.py
│ ├── GCodeExporter.py
│ ├── STLExporter.py
│ ├── SVGExporter.py
│ └── __init__.py
├── Geometry
│ ├── Letters.py
│ ├── Line.py
│ ├── Matrix.py
│ ├── Model.py
│ ├── Path.py
│ ├── Plane.py
│ ├── Point.py
│ ├── PointKdtree.py
│ ├── Polygon.py
│ ├── PolygonExtractor.py
│ ├── Triangle.py
│ ├── TriangleKdtree.py
│ ├── __init__.py
│ ├── intersection.py
│ ├── kdtree.py
│ └── utils.py
├── Gui
│ ├── Console.py
│ ├── ControlsGTK.py
│ ├── OpenGLTools.py
│ ├── Project.py
│ ├── Settings.py
│ ├── Visualization.py
│ ├── __init__.py
│ └── common.py
├── Importers
│ ├── CXFImporter.py
│ ├── DXFImporter.py
│ ├── PSImporter.py
│ ├── STLImporter.py
│ ├── SVGImporter.py
│ ├── TestModel.py
│ ├── ToolpathSettingsParser.py
│ └── __init__.py
├── PathGenerators
│ ├── ContourFollow.py
│ ├── DropCutter.py
│ ├── EngraveCutter.py
│ ├── PushCutter.py
│ └── __init__.py
├── PathProcessors
│ ├── ContourCutter.py
│ ├── PathAccumulator.py
│ ├── PolygonCutter.py
│ ├── SimpleCutter.py
│ ├── ZigZagCutter.py
│ └── __init__.py
├── Physics
│ ├── __init__.py
│ └── ode_physics.py
├── Plugins
│ ├── Bounds.py
│ ├── Clipboard.py
│ ├── EMCToolExport.py
│ ├── FilenameDialog.py
│ ├── Fonts.py
│ ├── GCodeParameters.py
│ ├── GCodeTouchOff.py
│ ├── GtkConsole.py
│ ├── Log.py
│ ├── MemoryAnalyzer.py
│ ├── ModelExport.py
│ ├── ModelExtrusion.py
│ ├── ModelPlaneMirror.py
│ ├── ModelPolygons.py
│ ├── ModelPosition.py
│ ├── ModelProjection.py
│ ├── ModelRotation.py
│ ├── ModelScaling.py
│ ├── ModelSupport.py
│ ├── ModelSupportDistributed.py
│ ├── ModelSupportGrid.py
│ ├── ModelSwapAxes.py
│ ├── Models.py
│ ├── OpenGLViewAxes.py
│ ├── OpenGLViewBounds.py
│ ├── OpenGLViewDimension.py
│ ├── OpenGLViewGrid.py
│ ├── OpenGLViewModel.py
│ ├── OpenGLViewSupportModelPreview.py
│ ├── OpenGLViewToolpath.py
│ ├── OpenGLWindow.py
│ ├── ParallelProcessing.py
│ ├── ParameterGroupManager.py
│ ├── PathParameters.py
│ ├── PathPatterns.py
│ ├── PluginSelector.py
│ ├── PostprocessorEMC2.py
│ ├── ProcessStrategies.py
│ ├── Processes.py
│ ├── ProgressBar.py
│ ├── TaskParameters.py
│ ├── TaskTypes.py
│ ├── Tasks.py
│ ├── ToolParameters.py
│ ├── ToolTypes.py
│ ├── ToolpathCrop.py
│ ├── ToolpathExport.py
│ ├── ToolpathGrid.py
│ ├── ToolpathSimulation.py
│ ├── Toolpaths.py
│ ├── Tools.py
│ ├── Units.py
│ └── __init__.py
├── Simulation
│ ├── ODEBlocks.py
│ ├── ZBuffer.py
│ └── __init__.py
├── Toolpath
│ ├── Generator.py
│ ├── MotionGrid.py
│ ├── SupportGrid.py
│ └── __init__.py
├── Utils
│ ├── FontCache.py
│ ├── __init__.py
│ ├── iterators.py
│ ├── locations.py
│ ├── log.py
│ ├── polynomials.py
│ ├── rootsolver.py
│ ├── threading.py
│ └── xml_handling.py
└── __init__.py
├── pyinstaller
├── hooks
│ └── hook-pycam.py
├── pycam.spec
├── pyinstaller_fix_module_exception.patch
└── pyinstaller_info.txt
├── pylint.sh
├── release_info.txt
├── samples
├── Box0+1.stl
├── Box0.stl
├── Box1.stl
├── Box2.stl
├── Box3.stl
├── SampleScene.stl
├── SampleScene2.scad
├── SampleScene2.stl
├── SampleScene3.scad
├── SampleScene3.stl
├── Sphere0.stl
├── Sphere_cut.scad
├── Sphere_cut.stl
├── TestModel.stl
├── multilayer_engrave.svg
├── polygon2.svg
├── polygon3.svg
├── polygon4.svg
├── polygons.svg
├── problem.conf
├── problem_1_triangle.stl
├── pycam-text.dxf
├── pycam-textbox.scad
├── pycam-textbox.stl
├── pycam.stl
├── pycam_text_2d.svg
├── rectangle.svg
└── sled.stl
├── scripts
├── profile_pycam.py
├── pycam
└── pycam_win32_postinstall.py
├── setup.cfg
├── setup.py
├── share
├── desktop
│ └── pycam.desktop
├── fonts
│ ├── README
│ ├── courier.cxf
│ ├── cursive.cxf
│ ├── cyrillic_ii.cxf
│ ├── gothgbt.cxf
│ ├── gothgrt.cxf
│ ├── gothitt.cxf
│ ├── greek_ol.cxf
│ ├── greekc.cxf
│ ├── greekcs.cxf
│ ├── greekp.cxf
│ ├── greeks.cxf
│ ├── hershey.readme
│ ├── iso8859-11.cxf
│ ├── italicc.cxf
│ ├── italiccs.cxf
│ ├── italict.cxf
│ ├── kochigothic.cxf
│ ├── kochimincho.cxf
│ ├── normallatin1.cxf
│ ├── normallatin1.readme
│ ├── normallatin2.cxf
│ ├── romanc.cxf
│ ├── romancs.cxf
│ ├── romand.cxf
│ ├── romanp.cxf
│ ├── romans.cxf
│ ├── romans2.cxf
│ ├── romant.cxf
│ ├── scriptc.cxf
│ ├── scripts.cxf
│ ├── standard.cxf
│ ├── symbol.cxf
│ ├── symbol_astro.cxf
│ ├── symbol_misc1.cxf
│ ├── symbol_misc2.cxf
│ └── unicode.cxf
├── mime
│ ├── application-sla.svg
│ ├── icons
│ │ ├── 128x128
│ │ │ └── application-sla.png
│ │ ├── 32x32
│ │ │ └── application-sla.png
│ │ └── 64x64
│ │ │ └── application-sla.png
│ ├── pycam.mime
│ └── pycam.xml
├── misc
│ └── DXF.gpl
├── pycam.ico
└── ui
│ ├── bounds.ui
│ ├── clipboard.ui
│ ├── emc_tool_export.ui
│ ├── extrusion_chamfer.png
│ ├── extrusion_radius_down.png
│ ├── extrusion_radius_up.png
│ ├── extrusion_sigmoidal.png
│ ├── extrusion_sine.png
│ ├── fonts.ui
│ ├── gcode_preferences.ui
│ ├── gcode_touch_off.ui
│ ├── gtk_console.ui
│ ├── gtkrc_windows
│ ├── log.ui
│ ├── logo_128px.png
│ ├── logo_16px.png
│ ├── logo_32px.png
│ ├── logo_48px.png
│ ├── logo_64px.png
│ ├── logo_gui.bmp
│ ├── logo_gui.png
│ ├── logo_gui_vertical.bmp
│ ├── logo_scalable.svg
│ ├── memory_analyzer.ui
│ ├── menubar.xml
│ ├── model_export.ui
│ ├── model_extrusion.ui
│ ├── model_plane_mirror.ui
│ ├── model_polygons.ui
│ ├── model_position.ui
│ ├── model_projection.ui
│ ├── model_rotation.ui
│ ├── model_scaling.ui
│ ├── model_support.ui
│ ├── model_support_distributed.ui
│ ├── model_support_grid.ui
│ ├── model_swap_axes.ui
│ ├── models.ui
│ ├── opengl.ui
│ ├── opengl_view_dimension.ui
│ ├── opengl_view_grid.ui
│ ├── parallel_processing.ui
│ ├── plugin_selector.ui
│ ├── processes.ui
│ ├── progress_bar.ui
│ ├── pycam-project.ui
│ ├── tasks.ui
│ ├── toolpath_crop.ui
│ ├── toolpath_export.ui
│ ├── toolpath_grid.ui
│ ├── toolpath_simulation.ui
│ ├── toolpaths.ui
│ ├── tools.ui
│ ├── units.ui
│ ├── visible.svg
│ └── visible_off.svg
└── technical_details.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | *.py[cod]
4 |
5 | dist
6 | build
7 | eggs
8 | *.egg-info
9 | .svn
10 |
11 | .DS_Store*
12 | ehthumbs.db
13 | Icon?
14 | Thumbs.db
15 |
16 | *~
17 | .*.swp
18 |
19 |
--------------------------------------------------------------------------------
/INSTALL.TXT:
--------------------------------------------------------------------------------
1 | Dependencies of the graphical interface:
2 | ========================================
3 |
4 | Windows:
5 | --------
6 |
7 | Please note that there are two ways of using PyCAM on Windows:
8 | 1) use the standalone executable (experimental - but it does not require further dependencies)
9 | http://sourceforge.net/projects/pycam/files/
10 |
11 | 2) the installer package - it requires the following dependencies:
12 |
13 | BEWARE: this currently (as of 2010/10/20) does not work due to a problem with
14 | the gtk-installer. I guess, it will be fixed in the next weeks. Until then you
15 | should use the standalone executable for Windows (see above).
16 |
17 | Python 2.6:
18 | http://www.python.org/download/releases/2.6.6/
19 | Please note that some of the GTK packages below require
20 | specifically v2.6 (thus v2.7 will not help).
21 |
22 | GTK v2.16 and GtkGlExt and PyGTK:
23 | http://www.bonifazi.eu/appunti/ (Blog)
24 | http://www.bonifazi.eu/appunti/pygtk_windows_installer.exe
25 |
26 | PyODE (optional):
27 | http://sourceforge.net/projects/pyode/files/pyode/snapshot-2010-03-22/PyODE-snapshot-2010-03-22.win32-py2.6.exe/download
28 |
29 |
30 | Notes:
31 | - you will probably need to add some directories manually to your search PATH setting:
32 | C:\Programs\Common Files\GTK\bin;C:\Programs\Python25;C:\Programs\GtkGLExt\1.0\bin
33 | (this is just an example - adjust it to your specific installation paths)
34 | - run a python console and check if the following commands work:
35 | import gtk
36 | import gtk.gtkgl
37 | import OpenGL
38 | import ode # this is optional
39 |
40 |
41 | Unix:
42 | -----
43 |
44 | Install the following packages with your package manager:
45 | python
46 | python-gtk2
47 | python-opengl
48 | python-gtkglext1
49 | python-pyode (optional)
50 | python-setproctitle (optional)
51 | python-psyco (optional)
52 |
53 | On a debian or ubuntu system you would just type the following in a root console:
54 | apt-get install python-gtkglext1 python-opengl python-gtk2 python-pyode python-setproctitle python-psyco
55 | Please note that you need to enable the "universe" repository in Ubuntu.
56 |
57 | BEWARE: Debian "Lenny" and Ubuntu "Jaunty" (maybe also Dapper/Hardy/Intrepid)
58 | contain older "python-opengl" and "python-pyode" packages, that expose problems
59 | with PyCAM.
60 | You need to add "Squeeze" (for Debian) or "Karmic/Lucid" (for Ubuntu) to your
61 | package repository list and run the following:
62 | apt-get update
63 | apt-get install python-pyode python-opengl
64 | Afterwards you can remove the new package repository again.
65 |
66 | Users of Python 2.5 with multiple CPU cores may want to install the package
67 | "python-processing". This enables multi-threading for Python before v2.6.
68 |
69 |
70 | MacOS
71 | -----
72 |
73 | Please read http://sourceforge.net/projects/pycam/forums/forum/860183/topic/3800091
74 | for the issues involved with installing PyCAM's dependencies via MacPorts.
75 | (thanks to lilalinux for this description)
76 |
77 |
78 | Minimal requirements for non-GUI mode
79 | =====================================
80 | If you plan to use PyCAM only in batch mode (without a graphical user
81 | interface), then you just need to install Python.
82 | See the manpage (man pycam) or the output of "pycam --help" for further defails.
83 |
84 |
--------------------------------------------------------------------------------
/LICENSE.TXT:
--------------------------------------------------------------------------------
1 | pycam - CNC Toolpath Generation in python
2 | =========================================
3 |
4 | Copyright (C) 2008 Lode Leroy
5 | -----------------------------
6 |
7 | This program is free software; you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation; either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program; if not, write to the Free Software
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 |
21 | See COPYING
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE.TXT
2 | include technical_details.txt
3 | include INSTALL.TXT
4 | include COPYING.TXT
5 | include README.TXT
6 | include Changelog
7 | include release_info.txt
8 | include pycam
9 | recursive-include desktop *
10 | recursive-include man *
11 | recursive-include src *.py
12 | recursive-include share *
13 | recursive-include samples *
14 | recursive-include Tests *
15 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # export SVN_REPO_BASE=. if you want to use the local version instead of trunk
2 | # from the subversion repository.
3 |
4 | # use something like "VERSION=0.2 make" to override the VERSION on the command line
5 | VERSION ?= $(shell sed -n "s/^.*[\t ]*VERSION[\t ]*=[\t ]*[\"']\([^\"']*\)[\"'].*/\1/gp" pycam/__init__.py)
6 | REPO_TAGS ?= https://pycam.svn.sourceforge.net/svnroot/pycam/tags
7 | RELEASE_PREFIX ?= pycam-
8 | ARCHIVE_DIR_RELATIVE ?= release-archives
9 | EXPORT_DIR = $(RELEASE_PREFIX)$(VERSION)
10 | EXPORT_FILE_PREFIX = $(EXPORT_DIR)
11 | EXPORT_ZIP = $(EXPORT_FILE_PREFIX).zip
12 | EXPORT_TGZ = $(EXPORT_FILE_PREFIX).tar.gz
13 | EXPORT_WIN32 = $(EXPORT_FILE_PREFIX).win32.exe
14 | PYTHON_EXE ?= python
15 | # check if the local version of python's distutils support "--plat-name"
16 | # (introduced in python 2.6)
17 | DISTUTILS_PLAT_NAME = $(shell $(PYTHON_EXE) setup.py --help build_ext | grep -q -- "--plat-name" && echo "--plat-name win32")
18 |
19 | # turn the destination directory into an absolute path
20 | ARCHIVE_DIR := $(shell pwd)/$(ARCHIVE_DIR_RELATIVE)
21 |
22 | .PHONY: zip tgz win32 clean dist git_export upload create_archive_dir man
23 |
24 | dist: zip tgz win32
25 | @# remove the tmp directory when everything is done
26 | @rm -rf "$(EXPORT_DIR)"
27 |
28 | clean:
29 | @rm -rf "$(EXPORT_DIR)"
30 |
31 | man: git_export
32 | @make -C "$(EXPORT_DIR)/man"
33 |
34 | git_export: clean
35 | @if git status 2>/dev/null >&2;\
36 | then git clone . "$(EXPORT_DIR)";\
37 | else echo "No git repo found."; exit 1;\
38 | fi
39 | # Windows needs a different name for the startup script - due to process creation (no fork/exec)
40 | @cp "$(EXPORT_DIR)/scripts/pycam" "$(EXPORT_DIR)/scripts/pycam-loader.py"
41 |
42 | create_archive_dir:
43 | @mkdir -p "$(ARCHIVE_DIR)"
44 |
45 | zip: create_archive_dir man git_export
46 | cd "$(EXPORT_DIR)"; $(PYTHON_EXE) setup.py sdist --format zip --dist-dir "$(ARCHIVE_DIR)"
47 |
48 | tgz: create_archive_dir man git_export
49 | cd "$(EXPORT_DIR)"; $(PYTHON_EXE) setup.py sdist --format gztar --dist-dir "$(ARCHIVE_DIR)"
50 |
51 | win32: create_archive_dir man git_export
52 | # this is a binary release
53 | cd "$(EXPORT_DIR)"; $(PYTHON_EXE) setup.py bdist_wininst --user-access-control force --dist-dir "$(ARCHIVE_DIR)" $(DISTUTILS_PLAT_NAME)
54 |
55 | upload:
56 | svn cp "$(SVN_REPO_BASE)" "$(REPO_TAGS)/release-$(VERSION)" -m "tag release $(VERSION)"
57 | svn import "$(ARCHIVE_DIR)/$(EXPORT_ZIP)" "$(REPO_TAGS)/archives/$(EXPORT_ZIP)" -m "added released zip file for version $(VERSION)"
58 | svn import "$(ARCHIVE_DIR)/$(EXPORT_TGZ)" "$(REPO_TAGS)/archives/$(EXPORT_TGZ)" -m "added released tgz file for version $(VERSION)"
59 | svn import "$(ARCHIVE_DIR)/$(EXPORT_WIN32)" "$(REPO_TAGS)/archives/$(EXPORT_WIN32)" -m "added released win32 installer for version $(VERSION)"
60 |
61 |
--------------------------------------------------------------------------------
/README.TXT:
--------------------------------------------------------------------------------
1 | pycam : Python CAM
2 | ------------------
3 |
4 | Toolpath Generation for 3-Axis CNC machining
5 |
6 | Read the wiki for details:
7 | http://sourceforge.net/apps/mediawiki/pycam/index.php?title=User_Manual
8 |
9 |
10 | RUNNING:
11 |
12 | extract the archive and run "python pycam"
13 |
14 |
15 |
16 | USAGE:
17 |
18 | as a practical approach, you would probably:
19 |
20 | 1) for "rough" cutting,
21 | use the Cylindrical cutter
22 | with the PushCutter Pathgenerator
23 | and the Polygon PostProcessor in "x" or "y" mode
24 |
25 |
26 | 2) for "semifinish" cutting,
27 | use the Cylindrical/Toroidal cutter
28 | with the PushCutter Pathgenerator
29 | and the Contour PostProcessor in "xy" mode
30 |
31 | 3) "finish" cutting
32 | use the Spherical cutter
33 | with the DropCutter Pathgenerator
34 | and the ZigZag PostProcessor in "x" or "y" mode
35 |
36 |
37 |
38 |
39 | KNOWN BUGS:
40 |
41 | http://sourceforge.net/tracker/?group_id=237831&atid=1104176
42 |
43 |
44 | CONTRIBUTORS:
45 |
46 | * Lode Leroy: initiated the project; developed the toolpath generation,
47 | collision detection, geometry, Tk interface, ...
48 |
49 | * Lars Kruse: GTK interface and many features
50 |
51 | * Paul: GCode stepping precision
52 |
53 | * Arthur Magill: distutils packaging
54 |
55 | * Sebastian Kuzminsky: debian packaging
56 |
57 |
--------------------------------------------------------------------------------
/Tests/DropCutterTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | from pycam.Geometry import *
28 | from pycam.Cutters.SphericalCutter import *
29 | from pycam.Cutters.CylindricalCutter import *
30 | from pycam.Cutters.ToroidalCutter import *
31 |
32 | from pycam.Gui.Visualization import ShowTestScene
33 |
34 | from pycam.Importers.TestModel import TestModel
35 |
36 |
37 | from pycam.PathGenerators.DropCutter import DropCutter
38 | from pycam.Exporters.SimpleGCodeExporter import SimpleGCodeExporter
39 |
40 | if __name__ == "__main__":
41 |
42 | #c = SphericalCutter(1, Point(0,0,7))
43 | #c = CylindricalCutter(1, Point(0,0,7))
44 | c = ToroidalCutter(1, 0.1, Point(0,0,7))
45 | print "c=", c
46 |
47 | #model = TestModel()
48 | model = Model()
49 | model.append(Triangle(Point(-3,-4,1),Point(-3,4,1),Point(3,0,1)))
50 |
51 |
52 | if True:
53 | samples = 50
54 | lines = 50
55 | x0 = -7.0
56 | x1 = +7.0
57 | y0 = -7.0
58 | y1 = +7.0
59 | z0 = 0
60 | z1 = 4
61 | dx = (x1-x0)/samples
62 | dy = (y1-y0)/lines
63 | pg = DropCutter(c, model)
64 |
65 | pathlist = pg.GenerateToolPath(x0, x1, y0, y1, z0, z1, dx, dy, 0)
66 |
67 | ShowTestScene(model, c, pathlist)
68 |
69 |
--------------------------------------------------------------------------------
/Tests/PushCutterTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2008 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | from pycam.Geometry import *
28 | from pycam.Cutters.SphericalCutter import *
29 | from pycam.Cutters.CylindricalCutter import *
30 | from pycam.Cutters.ToroidalCutter import *
31 |
32 | from pycam.Gui.Visualization import ShowTestScene
33 |
34 | from pycam.Importers import STLImporter
35 |
36 | from pycam.PathGenerators.PushCutter import PushCutter
37 | from pycam.PathProcessors import *
38 | from pycam.Exporters.SimpleGCodeExporter import SimpleGCodeExporter
39 |
40 | if __name__ == "__main__":
41 |
42 | c = SphericalCutter(0.1, Point(0,0,7))
43 | #c = CylindricalCutter(1, Point(0,0,7))
44 | #c = ToroidalCutter(1, 0.25, Point(0,0,7))
45 | print "c=", c
46 |
47 | #model = TestModel()
48 | #model = STLImporter.ImportModel("Samples/STL/Box0.stl")
49 | #model = STLImporter.ImportModel("Samples/STL/Box1.stl")
50 | model = STLImporter.ImportModel("Samples/STL/Box0+1.stl")
51 | #model = Model()
52 | #model.append(Triangle(Point(0,0,0),Point(0,5,4),Point(0,-5,4)))
53 | #model.append(Triangle(Point(2,0,0),Point(2,-5,4),Point(2,5,4)))
54 |
55 | if True:
56 | lines = 20
57 | layers = 4
58 | x0 = -7.0
59 | x1 = +7.0
60 | y0 = -7.0
61 | y1 = +7.0
62 | z0 = 2.0
63 | z1 = 4.0
64 | pc = PushCutter(c, model, SimpleCutter())
65 | #pc = PushCutter(c, model, ZigZagCutter())
66 | #pc = PushCutter(c, model, PolygonCutter())
67 |
68 | dx = 0
69 | if lines>1:
70 | dy = float(y1-y0)/(lines-1)
71 | else:
72 | dy = INFINITE
73 | if layers>1:
74 | dz = float(z1-z0)/(layers-1)
75 | else:
76 | dz = INFINITE
77 |
78 | pathlist = pc.GenerateToolPath(x0,x1,y0,y1,z0,z1,dx,dy,dz)
79 | c.moveto(Point(x0,y0,z0))
80 | ShowTestScene(model, c, pathlist)
81 |
--------------------------------------------------------------------------------
/Tests/STLImporterTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2010 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys, time
25 | sys.path.insert(0,'.')
26 |
27 |
28 | from pycam.Importers import STLImporter
29 | from pycam.Gui.Visualization import ShowTestScene
30 |
31 |
32 | if len(sys.argv)>1:
33 | filename = sys.argv[1]
34 | else:
35 | filename = "Samples/STL/TestModel.stl"
36 |
37 | start = time.clock()
38 | model = STLImporter.ImportModel(filename)
39 | end = time.clock()
40 |
41 | print "time=", (end-start)
42 |
43 | #ShowTestScene(model)
44 |
45 |
--------------------------------------------------------------------------------
/Tests/SimulationTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | from pycam.Gui.Visualization import Visualization
28 | from pycam.Simulation.ZBuffer import ZBuffer
29 | from pycam.Importers.TestModel import TestModel
30 | from pycam.Geometry.Triangle import Triangle
31 | from pycam.Geometry.Point import Point
32 | from pycam.Cutters.SphericalCutter import SphericalCutter
33 |
34 | from OpenGL.GL import *
35 |
36 | model = TestModel()
37 |
38 | zbuffer = ZBuffer(-5,+5,150, -5,+5,150, 1,5)
39 |
40 | #zbuffer.add_wave()
41 |
42 | #zbuffer.add_triangle(Triangle(Point(-4,0,0),Point(3,5,2),Point(4,-3,4)))
43 |
44 | c = SphericalCutter(0.25)
45 |
46 | p = Point(-5,-5,2)
47 | c.moveto(p)
48 |
49 | zbuffer.add_triangles(model.triangles())
50 |
51 | #zbuffer.add_cutter(c)
52 |
53 |
54 | def DrawScene():
55 | size=1
56 | # axes
57 | glBegin(GL_LINES)
58 | glColor3f(1,0,0)
59 | glVertex3f(0,0,0)
60 | glVertex3f(size,0,0)
61 | glEnd()
62 | glBegin(GL_LINES)
63 | glColor3f(0,1,0)
64 | glVertex3f(0,0,0)
65 | glVertex3f(0,size,0)
66 | glEnd()
67 | glBegin(GL_LINES)
68 | glColor3f(0,0,1)
69 | glVertex3f(0,0,0)
70 | glVertex3f(0,0,size)
71 | glEnd()
72 |
73 | glColor3f(1,1,1)
74 | c.to_OpenGL()
75 |
76 |
77 | glColor3f(0.9,0.8,0.7)
78 | # glMaterial(GL_FRONT_AND_BACK, GL_AMBIENT, (0.9, 0.8, 0.7, 0.2))
79 | # glMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE, (0.8, 0.8, 0.8, 0.2))
80 | glMaterial(GL_FRONT_AND_BACK, GL_SPECULAR, (1.0, 1.0, 1.0, 1.0))
81 | glMaterial(GL_FRONT_AND_BACK, GL_SHININESS, (0.5))
82 | zbuffer.to_OpenGL()
83 |
84 | dy = 0.1
85 | dx = 0.23
86 | dz = -0.01
87 |
88 | def HandleKey(key, x, y):
89 | global dx,dy,dz
90 | p.x += dx
91 | if p.x>5 or p.x<-5:
92 | dx = -dx
93 | p.x += dx * 2
94 | p.y += dy
95 | if p.y>5 or p.y<-5:
96 | dy = -dy
97 | p.y += dy * 2
98 | p.z += dz
99 |
100 | c.moveto(p)
101 | zbuffer.add_cutter(c)
102 |
103 | Visualization("VisualizationTest", DrawScene, handleKey = HandleKey)
104 |
105 |
--------------------------------------------------------------------------------
/Tests/TriangleKdtreeTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | from pycam.Geometry.TriangleKdtree import *
28 | from pycam.Geometry.Model import Model
29 | from pycam.Importers.TestModel import TestModel
30 |
31 | print "# get model"
32 | testmodel = TestModel()
33 | print "# subdivide"
34 | model = testmodel.subdivide(5)
35 | print "# build kdtree"
36 | kdtree = BuildKdtree2d(model.triangles(), 2, 0.1)
37 | #print "#kdtree=",kdtree
38 |
39 | x = 2
40 | y = 2
41 | r = 0.1
42 |
43 | minx = x-r
44 | miny = y-r
45 | maxx = x+r
46 | maxy = y+r
47 |
48 |
49 | print "# query kdtree"
50 | ResetKdtree2dStats(False)
51 | tests = SearchKdtree2d(kdtree, minx, maxx, miny, maxy)
52 | print "# query kdtree"
53 | ResetKdtree2dStats(True)
54 | hits = SearchKdtree2d(kdtree, minx, maxx, miny, maxy)
55 | #print "# hits=%d / tests=%d" % GetKdtree2dStats(), "/ triangles=%d" % len(model.triangles())
56 | print "# hits=%d " % len(hits), "/ tests=%d" % len(tests), "/ triangles=%d" % len(model.triangles())
57 |
58 |
--------------------------------------------------------------------------------
/Tests/TriangleTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2010 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | import math
28 |
29 | from pycam.Geometry import *
30 | from pycam.Gui.Visualization import Visualization
31 |
32 |
33 | p1 = Point(1,0,0)
34 | p2 = Point(0,1,0)
35 | p3 = Point(0,0,1)
36 | t = Triangle(p1,p2,p3)
37 | t.id=1
38 | t.calc_circumcircle()
39 |
40 | def DrawScene():
41 | t.to_OpenGL()
42 |
43 | if __name__ == "__main__":
44 |
45 | print "p1=" + str(p1);
46 | print "p2=" + str(p2);
47 | print "p3=" + str(p3);
48 |
49 | print "p2-p1=" + str(p2.sub(p1))
50 | print "p3-p2=" + str(p3.sub(p2))
51 | print "p1-p3=" + str(p1.sub(p3))
52 |
53 | print "p2.p1=" + str(p2.dot(p1))
54 | print "p3.p2=" + str(p3.dot(p2))
55 | print "p1.p3=" + str(p1.dot(p3))
56 |
57 | print "p1xp2=" + str(p1.cross(p2))
58 | print "p2xp3=" + str(p2.cross(p3))
59 | print "p3xp1=" + str(p3.cross(p1))
60 |
61 | print t
62 |
63 | print "circ(t) = %s@%s" % (t.radius(),t.center())
64 |
65 |
66 | Visualization("VisualizationTest", DrawScene)
67 |
--------------------------------------------------------------------------------
/Tests/VisualizationTest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2008 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import sys
25 | sys.path.insert(0,'.')
26 |
27 | from pycam.Gui.Visualization import Visualization
28 | from pycam.Importers.TestModel import TestModel
29 |
30 | model = TestModel()
31 |
32 | def DrawScene():
33 | model.to_OpenGL()
34 |
35 | Visualization("VisualizationTest", DrawScene)
36 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | pycam (0.5.1-1) unstable; urgency=low
2 |
3 | * initial debian package
4 | * Closes: #600779
5 |
6 | -- Lars Kruse Mon, 13 Jun 2011 02:04:59 +0200
7 |
8 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 7
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: pycam
2 | Section: python
3 | Priority: extra
4 | Maintainer: Lars Kruse
5 | Build-Depends: python, debhelper (>= 7), cdbs, patchutils
6 | Build-Depends-Indep: python-support, help2man
7 | Standards-Version: 3.9.2
8 | Homepage: http://sourceforge.net/projects/pycam/
9 |
10 | Package: pycam
11 | Architecture: all
12 | Depends: python-gtk2, python-opengl (>>3.0.0~b6-3), python-gtkglext1,
13 | python-rsvg, ${misc:Depends}, ${python:Depends}
14 | Recommends: python-pyode (>>1.2.0-3), python-psyco, python-setproctitle,
15 | python-guppy, inkscape, pstoedit
16 | Suggests: qcad-data | librecad-data
17 | Description: CAM program & Python library for generating toolpaths
18 | PyCAM is a toolpath generator for 3 axis machines. The generated
19 | GCode can be used with EMC2 and other machine controllers.
20 | The included Python library can be used independently from the GUI.
21 | .
22 | Features:
23 | * read and write STL model files (3D)
24 | * support for 2D models (DXF/SVG/PS)
25 | * generate toolpaths (GCode) for various strategies and drill
26 | definitions
27 | * manage and store processing templates
28 | * scale, move, rotate, flip and transform the model
29 | * interactive 3D model view based on OpenGL
30 | * non-interactive generation of GCode via commandline
31 | * render single-line fonts (provided by QCAD)
32 |
33 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
2 | Name: PyCAM
3 | Maintainer: Lars Kruse
4 | Source: http://pycam.sourceforge.net
5 |
6 | Files: *
7 | Copyright: 2010-2011, Lars Kruse
8 | 2006-2010, Lode Leroy
9 | License: GPL-3+
10 |
11 | Files: debian/*
12 | Copyright: 2010-2011, Lars Kruse
13 | 2010, Sebastian Kuzminsky
14 | License: GPL-3+
15 |
16 | License: GPL-3+
17 | This program is free software: you can redistribute it and/or modify
18 | it under the terms of the GNU General Public License as published by
19 | the Free Software Foundation, either version 3 of the License, or
20 | (at your option) any later version.
21 | .
22 | This package is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
26 | .
27 | You should have received a copy of the GNU General Public License
28 | along with this program. If not, see .
29 | .
30 | On Debian systems, the full text of the GNU General Public License
31 | version 3 can be found in the file `/usr/share/common-licenses/GPL-3'.
32 |
33 |
--------------------------------------------------------------------------------
/debian/dirs:
--------------------------------------------------------------------------------
1 | usr/share/applications
2 | usr/share/icons/hicolor/scalable/apps
3 |
--------------------------------------------------------------------------------
/debian/docs:
--------------------------------------------------------------------------------
1 | technical_details.txt
2 | README.TXT
3 |
4 |
--------------------------------------------------------------------------------
/debian/manpages:
--------------------------------------------------------------------------------
1 | man/pycam.1
2 |
--------------------------------------------------------------------------------
/debian/patches/series:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/debian/patches/series
--------------------------------------------------------------------------------
/debian/pycam.mime:
--------------------------------------------------------------------------------
1 | ../share/mime/pycam.mime
--------------------------------------------------------------------------------
/debian/pycam.sharedmimeinfo:
--------------------------------------------------------------------------------
1 | ../share/mime/pycam.xml
--------------------------------------------------------------------------------
/debian/pycompat:
--------------------------------------------------------------------------------
1 | 2
2 |
--------------------------------------------------------------------------------
/debian/pyversions:
--------------------------------------------------------------------------------
1 | 2.5-
2 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | # -*- makefile -*-
3 | # Sample debian/rules that uses debhelper.
4 | # This file was originally written by Joey Hess and Craig Small.
5 | # As a special exception, when this file is copied by dh-make into a
6 | # dh-make output file, you may use that output file without restriction.
7 | # This special exception was added by Craig Small in version 0.37 of dh-make.
8 |
9 | # Uncomment this to turn on verbose mode.
10 | #export DH_VERBOSE=1
11 |
12 |
13 | export DEB_PYTHON_SYSTEM=pysupport
14 |
15 | # Debhelper must be included before python-distutils to use
16 | # dh_python / dh_pycentral / dh_pysupport
17 | include /usr/share/cdbs/1/rules/debhelper.mk
18 | include /usr/share/cdbs/1/class/python-distutils.mk
19 |
20 | # clean the manpage
21 | clean::
22 | make -C man clean
23 |
24 | # build the manpage
25 | build/pycam::
26 | make -C man
27 |
28 | # install the .desktop file
29 | install/pycam::
30 | # "desktop" file
31 | cp -v share/desktop/pycam.desktop `pwd`/debian/pycam/usr/share/applications/
32 | # application logo for the menu entry
33 | cp -v share/ui/logo_scalable.svg `pwd`/debian/pycam/usr/share/icons/hicolor/scalable/apps/pycam.svg
34 | # remove "doc" directory from /usr/share/pycam/
35 | rm -rf `pwd`/debian/pycam/usr/share/pycam/doc
36 | # the CXF fonts are distributed by QCAD - use them instead of the embedded ones
37 | rm -rf `pwd`/debian/pycam/usr/share/pycam/fonts
38 | # the gtkrc file for Windows is useless
39 | rm `pwd`/debian/pycam/usr/share/pycam/ui/gtkrc_windows
40 |
41 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (quilt)
2 |
--------------------------------------------------------------------------------
/debian/watch:
--------------------------------------------------------------------------------
1 | version=3
2 | http://sf.net/pycam/pycam-(.+)\.tar\.gz
3 |
--------------------------------------------------------------------------------
/man/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean
2 |
3 | pycam.1: ../pycam pycam.1.inc
4 | help2man --no-info --name="Toolpath Generation for 3-Axis CNC machining" \
5 | --section=1 --manual="PyCAM manual" --include=pycam.1.inc --output=pycam.1 ../scripts/pycam
6 |
7 | clean:
8 | @rm -f pycam.1
9 |
10 |
--------------------------------------------------------------------------------
/man/pycam.1.inc:
--------------------------------------------------------------------------------
1 | [EXAMPLES]
2 | .nf
3 | .B pycam \-\-export\-gcode=output.ngc \-\-bounds\-type=relative\-margin \-\-bounds-lower=0.1,0.05,-0.1 foo.stl
4 |
5 | .fi
6 | Use the default settings to process the model \fBfoo.stl\fR with an adjusted
7 | lower margin (minx, miny, minz) of 10% (for x), 5% (for y) and \-10% (for z).
8 |
9 | [ENVIRONMENT]
10 | .IP PYCAM_DATA_DIR
11 | Override the default data directory of PyCAM. This allows
12 | you to provide customized logos, menu files or non-default sample files.
13 | .IP PYCAM_FONT_DIR
14 | Override the default location of engrave fonts.
15 | .IP PYTHONPATH
16 | You may want to define this variable in case that you installed the
17 | \fBPyCAM\fR python package in a non-default location.
18 |
19 | [REPORTING BUGS]
20 | See http://sourceforge.net/tracker/?group_id=237831&atid=1104176
21 |
22 | [SEE ALSO]
23 | Take a look at the output of \fBpycam \-\-help\fR to get a slightly better
24 | formatted list of options. The manual that you are reading right now is
25 | derived from this output.
26 |
27 | Take a look at the wiki for more information about PyCAM:
28 | http://sourceforge.net/apps/mediawiki/pycam/
29 |
30 | The website of the PyCAM project: http://pycam.sourceforge.net
31 |
32 |
--------------------------------------------------------------------------------
/pycam/Cutters/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = [ "SphericalCutter", "CylindricalCutter", "ToroidalCutter",
24 | "BaseCutter" ]
25 |
26 | from pycam.Cutters.BaseCutter import BaseCutter
27 | from pycam.Cutters.SphericalCutter import SphericalCutter
28 | from pycam.Cutters.CylindricalCutter import CylindricalCutter
29 | from pycam.Cutters.ToroidalCutter import ToroidalCutter
30 |
31 |
32 | def get_tool_from_settings(tool_settings, height=None):
33 | """ get the tool specified by the relevant settings
34 |
35 | The settings must include:
36 | - "shape": one of "SphericalCutter", "CylindricalCutter" and
37 | "ToroidalCutter"
38 | - "radius": the tool radius
39 | The following settings are optional or shape specific:
40 | - "torus_radius": necessary for ToroidalCutter
41 |
42 | @type tool_settings: dict
43 | @value tool_settings: contains the attributes of the tool
44 | @type height: float
45 | @value height: the height of the tool
46 | @rtype: BaseCutter | basestring
47 | @return: a tool object or an error string
48 | """
49 | cuttername = tool_settings["shape"]
50 | radius = tool_settings["tool_radius"]
51 | if cuttername == "SphericalCutter":
52 | return SphericalCutter(radius, height=height)
53 | elif cuttername == "CylindricalCutter":
54 | return CylindricalCutter(radius, height=height)
55 | elif cuttername == "ToroidalCutter":
56 | toroid = tool_settings["torus_radius"]
57 | return ToroidalCutter(radius, toroid, height=height)
58 | else:
59 | return "Invalid cutter shape: '%s' is not known" % str(cuttername)
60 |
61 |
--------------------------------------------------------------------------------
/pycam/Exporters/EMCToolExporter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import os
24 |
25 | class EMCToolExporter(object):
26 |
27 | def __init__(self, tools):
28 | """ tools are expected to be dictionaries containing the following keys:
29 | - id
30 | - radius
31 | - name
32 | """
33 | self.tools = tools
34 |
35 | def get_tool_definition_string(self):
36 | result = []
37 | tools = list(self.tools)
38 | tools.sort(key=lambda item: item["id"])
39 | #result.append(self.HEADER_ROW)
40 | for tool in tools:
41 | # use an arbitrary length
42 | tool_length = tool["radius"] * 10
43 | line = "T%d P%d D%f Z-%f ;%s" % (tool["id"], tool["id"],
44 | 2 * tool["radius"], tool_length, tool["name"])
45 | result.append(line)
46 | # add the dummy line for the "last" tool
47 | result.append("T99999 P99999 Z+0.100000 ;dummy tool")
48 | return os.linesep.join(result)
49 |
50 |
--------------------------------------------------------------------------------
/pycam/Exporters/STLExporter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2010 Lars Kruse
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | from pycam import VERSION
25 | import datetime
26 | import os
27 |
28 | class STLExporter(object):
29 |
30 | def __init__(self, model, name="model", created_by="pycam", linesep=None,
31 | **kwargs):
32 | self.model = model
33 | self.name = name
34 | self.created_by = created_by
35 | if linesep is None:
36 | self.linesep = os.linesep
37 | else:
38 | self.linesep = linesep
39 |
40 | def __str__(self):
41 | return self.linesep.join(self.get_output_lines)
42 |
43 | def write(self, stream):
44 | for line in self.get_output_lines():
45 | stream.write(line)
46 | stream.write(self.linesep)
47 |
48 | def get_output_lines(self):
49 | date = datetime.date.today().isoformat()
50 | yield """solid "%s"; Produced by %s (v%s), %s""" \
51 | % (self.name, self.created_by, VERSION, date)
52 | for triangle in self.model.triangles():
53 | norm = triangle.normal.normalized()
54 | yield "facet normal %f %f %f" % (norm.x, norm.y, norm.z)
55 | yield " outer loop"
56 | # Triangle vertices are stored in clockwise order - thus we need
57 | # to reverse the order (STL expects counter-clockwise orientation).
58 | for point in (triangle.p1, triangle.p3, triangle.p2):
59 | yield " vertex %f %f %f" % (point.x, point.y, point.z)
60 | yield " endloop"
61 | yield "endfacet"
62 | yield "endsolid"
63 |
64 |
--------------------------------------------------------------------------------
/pycam/Exporters/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2008-2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | __all__ = [ "GCodeExporter", "SVGExporter", "STLExporter", "EMCToolExporter"]
25 |
26 |
--------------------------------------------------------------------------------
/pycam/Geometry/Path.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2008 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | """ the points of a path are only used for describing coordinates. Thus we
25 | don't really need complete "Point" instances that consume a lot of memory.
26 | Since python 2.6 the "namedtuple" factory is available.
27 | This reduces the memory consumption of a toolpath down to 1/3.
28 | """
29 |
30 | try:
31 | # this works for python 2.6 or above (saves memory)
32 | # TODO: disabled for now - check if we could enable it later ...
33 | import INVALID_IMPORT
34 | from collections import namedtuple
35 | tuple_point = namedtuple("TuplePoint", "x y z")
36 | get_point_object = lambda point: tuple_point(point.x, point.y, point.z)
37 | except ImportError:
38 | # dummy for python < v2.6 (consumes more memory)
39 | get_point_object = lambda point: point
40 |
41 | from pycam.Geometry import IDGenerator
42 |
43 |
44 | class Path(IDGenerator):
45 |
46 | def __init__(self):
47 | super(Path, self).__init__()
48 | self.top_join = None
49 | self.bot_join = None
50 | self.winding = 0
51 | self.points = []
52 |
53 | def __repr__(self):
54 | text = ""
55 | text += "path %d: " % self.id
56 | first = True
57 | for point in self.points:
58 | if first:
59 | first = False
60 | else:
61 | text += "-"
62 | text += "%d(%g,%g,%g)" % (point.id, point.x, point.y, point.z)
63 | return text
64 |
65 | def insert(self, index, point):
66 | self.points.insert(index, get_point_object(point))
67 |
68 | def append(self, point):
69 | self.points.append(get_point_object(point))
70 |
71 | def reverse(self):
72 | self.points.reverse()
73 |
--------------------------------------------------------------------------------
/pycam/Geometry/PointKdtree.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2009 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | from pycam.Geometry.utils import epsilon
24 | from pycam.Geometry.Point import Point
25 | from pycam.Geometry.kdtree import Node, kdtree
26 |
27 |
28 | class PointKdtree(kdtree):
29 |
30 | __slots__ = ["_n", "tolerance"]
31 |
32 | def __init__(self, points=None, cutoff=5, cutoff_distance=0.5,
33 | tolerance=epsilon):
34 | if points is None:
35 | points = []
36 | self._n = None
37 | self.tolerance = tolerance
38 | nodes = []
39 | for p in points:
40 | n = Node(p, (p.x, p.y, p.z))
41 | nodes.append(n)
42 | kdtree.__init__(self, nodes, cutoff, cutoff_distance)
43 |
44 | def dist(self, n1, n2):
45 | dx = n1.bound[0]-n2.bound[0]
46 | dy = n1.bound[1]-n2.bound[1]
47 | dz = n1.bound[2]-n2.bound[2]
48 | return dx*dx+dy*dy+dz*dz
49 |
50 | def Point(self, x, y, z):
51 | #return Point(x,y,z)
52 | if self._n:
53 | n = self._n
54 | n.bound = (x, y, z)
55 | else:
56 | n = Node(None, (x, y, z))
57 | (nn, dist) = self.nearest_neighbor(n, self.dist)
58 | if nn and (dist < self.tolerance):
59 | self._n = n
60 | return nn.obj
61 | else:
62 | n.obj = Point(x, y, z)
63 | self._n = None
64 | self.insert(n)
65 | return n.obj
66 |
67 |
--------------------------------------------------------------------------------
/pycam/Geometry/TriangleKdtree.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008-2009 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | from pycam.Geometry.kdtree import kdtree, Node
24 |
25 | overlaptest = True
26 |
27 | def SearchKdtree2d(tree, minx, maxx, miny, maxy):
28 | if tree.bucket:
29 | triangles = []
30 | for n in tree.nodes:
31 | if not overlaptest:
32 | triangles.append(n.obj)
33 | else:
34 | if not ((n.bound[0] > maxx) or
35 | (n.bound[1] < minx) or
36 | (n.bound[2] > maxy) or
37 | (n.bound[3] < miny)):
38 | triangles.append(n.obj)
39 | return triangles
40 | else:
41 | if tree.cutdim == 0:
42 | if maxx < tree.minval:
43 | return []
44 | elif maxx < tree.cutval:
45 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy)
46 | else:
47 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy) \
48 | + SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
49 | elif tree.cutdim == 1:
50 | if minx > tree.maxval:
51 | return []
52 | elif minx > tree.cutval:
53 | return SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
54 | else:
55 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy) \
56 | + SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
57 | elif tree.cutdim == 2:
58 | if maxy < tree.minval:
59 | return []
60 | elif maxy < tree.cutval:
61 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy)
62 | else:
63 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy) \
64 | + SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
65 | elif tree.cutdim == 3:
66 | if miny > tree.maxval:
67 | return []
68 | elif miny > tree.cutval:
69 | return SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
70 | else:
71 | return SearchKdtree2d(tree.lo, minx, maxx, miny, maxy) \
72 | + SearchKdtree2d(tree.hi, minx, maxx, miny, maxy)
73 |
74 |
75 | class TriangleKdtree(kdtree):
76 |
77 | __slots__ = []
78 |
79 | def __init__(self, triangles, cutoff=3, cutoff_distance=1.0):
80 | nodes = []
81 | for t in triangles:
82 | n = Node(t, (min(t.p1.x, t.p2.x, t.p3.x),
83 | max(t.p1.x, t.p2.x, t.p3.x),
84 | min(t.p1.y, t.p2.y, t.p3.y),
85 | max(t.p1.y, t.p2.y, t.p3.y)))
86 | nodes.append(n)
87 | super(TriangleKdtree, self).__init__(nodes, cutoff, cutoff_distance)
88 |
89 | def Search(self, minx, maxx, miny, maxy):
90 | return SearchKdtree2d(self, minx, maxx, miny, maxy)
91 |
92 |
--------------------------------------------------------------------------------
/pycam/Geometry/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import decimal
25 | import math
26 |
27 | INFINITE = 100000
28 | epsilon = 0.00001
29 |
30 | # use the "decimal" module for fixed precision numbers (only for debugging)
31 | _use_precision = False
32 |
33 |
34 | # the lambda functions below are more efficient than function definitions
35 |
36 | if _use_precision:
37 | ceil = lambda value: int((value + number(1).next_minus()) // 1)
38 | else:
39 | ceil = lambda value: int(math.ceil(value))
40 |
41 | # return "0" for "-epsilon < value < 0" (to work around floating inaccuracies)
42 | # otherwise: return the sqrt function of the current type (could even raise
43 | # exceptions)
44 | if _use_precision:
45 | sqrt = lambda value: (((value < -epsilon) or (value > 0)) and \
46 | value.sqrt()) or 0
47 | else:
48 | sqrt = lambda value: (((value < -epsilon) or (value > 0)) and \
49 | math.sqrt(value)) or 0
50 |
51 | if _use_precision:
52 | number = lambda value: decimal.Decimal(str(value))
53 | else:
54 | number = float
55 |
56 |
57 |
--------------------------------------------------------------------------------
/pycam/Gui/Console.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = ["ConsoleProgressBar"]
24 |
25 | import os
26 |
27 | class ConsoleProgressBar(object):
28 |
29 | STYLE_NONE = 0
30 | STYLE_TEXT = 1
31 | STYLE_BAR = 2
32 | STYLE_DOT = 3
33 | PROGRESS_BAR_LENGTH = 70
34 |
35 | def __init__(self, output, style=None):
36 | if style is None:
37 | style = ConsoleProgressBar.STYLE_TEXT
38 | self.output = output
39 | self.style = style
40 | self.last_length = 0
41 | self.text = ""
42 | self.percent = 0
43 |
44 | def _output_current_state(self, progress_happened=True):
45 | if self.style == ConsoleProgressBar.STYLE_TEXT:
46 | text = "%d%% %s" % (self.percent, self.text)
47 | self.last_length = len(text)
48 | elif self.style == ConsoleProgressBar.STYLE_BAR:
49 | bar_length = ConsoleProgressBar.PROGRESS_BAR_LENGTH
50 | hashes = int(bar_length * self.percent / 100.0)
51 | empty = bar_length - hashes
52 | text = "[%s%s]" % ("#" * hashes, "." * empty)
53 | # include a text like " 10% " in the middle
54 | percent_text = " %d%% " % self.percent
55 | start_text = text[:(len(text) - len(percent_text)) / 2]
56 | end_text = text[-(len(text) - len(start_text) - len(percent_text)):]
57 | text = start_text + percent_text + end_text
58 | self.last_length = len(text)
59 | elif self.style == ConsoleProgressBar.STYLE_DOT:
60 | if progress_happened:
61 | text = "."
62 | else:
63 | text = ""
64 | # don't remove any previous characters
65 | self.last_length = 0
66 | else:
67 | raise ValueError("ConsoleProgressBar: invalid style (%d)" \
68 | % self.style)
69 | self.output.write(text)
70 | self.output.flush()
71 |
72 | def update(self, text=None, percent=None, **kwargs):
73 | if self.style == ConsoleProgressBar.STYLE_NONE:
74 | return
75 | if not text is None:
76 | self.text = text
77 | if not percent is None:
78 | self.percent = int(percent)
79 | if self.last_length > 0:
80 | # delete the previous line
81 | self.output.write("\x08" * self.last_length)
82 | self._output_current_state(progress_happened = (not percent is None))
83 |
84 | def finish(self):
85 | if self.style == ConsoleProgressBar.STYLE_NONE:
86 | return
87 | # show that we are finished
88 | self.update(percent=100)
89 | # finish the line
90 | self.output.write(os.linesep)
91 |
92 |
--------------------------------------------------------------------------------
/pycam/Gui/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = ["common", "Console", "OpenGLTools", "Project", "Settings",
24 | "Vizualisation"]
25 |
26 |
--------------------------------------------------------------------------------
/pycam/Importers/PSImporter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | from pycam.Importers.SVGImporter import convert_eps2dxf
24 | import pycam.Importers.DXFImporter
25 | import pycam.Utils
26 | import tempfile
27 | import os
28 |
29 | log = pycam.Utils.log.get_logger()
30 |
31 |
32 | def import_model(filename, program_locations=None, unit="mm", callback=None,
33 | **kwargs):
34 | local_file = False
35 | if hasattr(filename, "read"):
36 | infile = filename
37 | ps_file_handle, ps_file_name = tempfile.mkstemp(suffix=".ps")
38 | try:
39 | temp_file = os.fdopen(ps_file_handle, "w")
40 | temp_file.write(infile.read())
41 | temp_file.close()
42 | except IOError, err_msg:
43 | log.error("PSImporter: Failed to create temporary local file " + \
44 | "(%s): %s" % (ps_file_name, err_msg))
45 | return
46 | filename = ps_file_name
47 | else:
48 | uri = pycam.Utils.URIHandler(filename)
49 | if not uri.exists():
50 | log.error("PSImporter: file (%s) does not exist" % filename)
51 | return None
52 | if not uri.is_local():
53 | # non-local file - write it to a temporary file first
54 | ps_file_handle, ps_file_name = tempfile.mkstemp(suffix=".ps")
55 | os.close(ps_file_handle)
56 | log.debug("Retrieving PS file for local access: %s -> %s" % \
57 | (uri, ps_file_name))
58 | if not uri.retrieve_remote_file(ps_file_name, callback=callback):
59 | log.error("PSImporter: Failed to retrieve the PS model file: " + \
60 | "%s -> %s" % (uri, ps_file_name))
61 | return
62 | filename = ps_file_name
63 | else:
64 | filename = uri.get_local_path()
65 | local_file = True
66 |
67 | if program_locations and "pstoedit" in program_locations:
68 | pstoedit_path = program_locations["pstoedit"]
69 | else:
70 | pstoedit_path = None
71 |
72 | def remove_temp_file(filename):
73 | if os.path.isfile(filename):
74 | try:
75 | os.remove(filename)
76 | except OSError, err_msg:
77 | log.warn("PSImporter: failed to remove temporary file " \
78 | + "(%s): %s" % (filename, err_msg))
79 |
80 | # convert eps to dxf via pstoedit
81 | dxf_file_handle, dxf_file_name = tempfile.mkstemp(suffix=".dxf")
82 | os.close(dxf_file_handle)
83 | success = convert_eps2dxf(filename, dxf_file_name, unit=unit,
84 | location=pstoedit_path)
85 | if not local_file:
86 | remove_temp_file(ps_file_name)
87 | if not success:
88 | result = None
89 | elif callback and callback():
90 | log.warn("PSImporter: load model operation cancelled")
91 | result = None
92 | else:
93 | log.info("Successfully converted PS file to DXF file")
94 | # pstoedit uses "inch" -> force a scale operation
95 | result = pycam.Importers.DXFImporter.import_model(dxf_file_name,
96 | unit=unit, callback=callback)
97 | # always remove the dxf file
98 | remove_temp_file(dxf_file_name)
99 | return result
100 |
101 |
--------------------------------------------------------------------------------
/pycam/Importers/TestModel.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008-2009 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | from pycam.Geometry.Triangle import Triangle
24 | from pycam.Geometry.Line import Line
25 | from pycam.Geometry.Point import Point
26 | from pycam.Geometry.Model import Model
27 |
28 |
29 | def get_test_model():
30 | points = []
31 | points.append(Point(-2, 1, 4))
32 | points.append(Point(2, 1, 4))
33 | points.append(Point(0, -2, 4))
34 | points.append(Point(-5, 2, 2))
35 | points.append(Point(-1, 3, 2))
36 | points.append(Point(5, 2, 2))
37 | points.append(Point(4, -1, 2))
38 | points.append(Point(2, -4, 2))
39 | points.append(Point(-2, -4, 2))
40 | points.append(Point(-3, -2, 2))
41 |
42 | lines = []
43 | lines.append(Line(points[0], points[1]))
44 | lines.append(Line(points[1], points[2]))
45 | lines.append(Line(points[2], points[0]))
46 | lines.append(Line(points[0], points[3]))
47 | lines.append(Line(points[3], points[4]))
48 | lines.append(Line(points[4], points[0]))
49 | lines.append(Line(points[4], points[1]))
50 | lines.append(Line(points[4], points[5]))
51 | lines.append(Line(points[5], points[1]))
52 | lines.append(Line(points[5], points[6]))
53 | lines.append(Line(points[6], points[1]))
54 | lines.append(Line(points[6], points[2]))
55 | lines.append(Line(points[6], points[7]))
56 | lines.append(Line(points[7], points[2]))
57 | lines.append(Line(points[7], points[8]))
58 | lines.append(Line(points[8], points[2]))
59 | lines.append(Line(points[8], points[9]))
60 | lines.append(Line(points[9], points[2]))
61 | lines.append(Line(points[9], points[0]))
62 | lines.append(Line(points[9], points[3]))
63 |
64 | model = Model()
65 | for p1, p2, p3, l1, l2, l3 in (
66 | (0, 1, 2, 0, 1, 2),
67 | (0, 3, 4, 3, 4, 5),
68 | (0, 4, 1, 5, 6, 0),
69 | (1, 4, 5, 6, 7, 8),
70 | (1, 5, 6, 8, 9, 10),
71 | (1, 6, 2, 10, 11, 1),
72 | (2, 6, 7, 11, 12, 13),
73 | (2, 7, 8, 13, 14, 15),
74 | (2, 8, 9, 15, 16, 17),
75 | (2, 9, 0, 17, 18, 2),
76 | (0, 9, 3, 18, 19, 3)):
77 | model.append(Triangle(points[p1], points[p2], points[p3]))
78 | return model
79 |
80 |
--------------------------------------------------------------------------------
/pycam/Importers/ToolpathSettingsParser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.Gui.Settings
24 | import pycam.Gui.Project
25 | import pycam.Utils.log
26 | import pycam.Utils
27 | import re
28 | import os
29 | import sys
30 |
31 | COMMENT_CHARACTERS = r";#"
32 | REGEX_META_KEYWORDS = r"[%s]?%s (.*): (.*)$" % (COMMENT_CHARACTERS,
33 | pycam.Gui.Project.ProjectGui.META_DATA_PREFIX)
34 | REGEX_SETTINGS_START = r"[%s]?%s$" % (COMMENT_CHARACTERS,
35 | pycam.Gui.Settings.ToolpathSettings.META_MARKER_START)
36 | REGEX_SETTINGS_END = r"[%s]?%s$" % (COMMENT_CHARACTERS,
37 | pycam.Gui.Settings.ToolpathSettings.META_MARKER_END)
38 |
39 | log = pycam.Utils.log.get_logger()
40 |
41 |
42 | def parse_toolpath_settings(filename):
43 | """ parse potential PyCAM settings from a given file
44 |
45 | This is mainly useful to retrieve task settings from a GCode file.
46 | @value filename: the name of the file to be read
47 | @type filename: str
48 | @returns: a dictionary (of all setting names and values) and the content
49 | of the 'comment' section (as a single string)
50 | @rtype: tuple(dict, str)
51 | """
52 | keywords = {}
53 | in_meta_zone = False
54 | meta_content = []
55 | if filename == "-":
56 | # read from stdin, if the input filename is "-"
57 | infile = sys.stdin
58 | close_file = False
59 | else:
60 | # open the file
61 | try:
62 | infile = pycam.Utils.URIHandler(filename).open()
63 | except IOError, err_msg:
64 | log.warn("ToolpathSettingsParser: Failed to read file (%s): %s" % \
65 | (filename, err_msg))
66 | return None
67 | close_file = True
68 | for line in infile.readlines():
69 | match = re.match(REGEX_META_KEYWORDS, line)
70 | if match:
71 | keywords[match.groups()[0]] = match.groups()[1].strip()
72 | if in_meta_zone:
73 | if re.match(REGEX_SETTINGS_END, line):
74 | in_meta_zone = False
75 | else:
76 | if line and line[0] in COMMENT_CHARACTERS:
77 | meta_content[-1].append(line[1:].strip())
78 | else:
79 | if re.match(REGEX_SETTINGS_START, line):
80 | in_meta_zone = True
81 | meta_content.append([])
82 | if close_file:
83 | # only close the file if it was opened before (e.g. not stdin)
84 | infile.close()
85 | return keywords, [os.linesep.join(one_block) for one_block in meta_content]
86 |
87 | if __name__ == "__main__":
88 | # for testing: output the parsed content of the given file (first argument)
89 | print "\n#################\n".join(parse_toolpath_settings(sys.argv[1])[1])
90 |
91 |
--------------------------------------------------------------------------------
/pycam/Importers/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 | Copyright 2010 Lars Kruse
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | __all__ = ["STLImporter", "DXFImporter", "SVGImporter", "TestModel"]
25 |
26 | import pycam.Utils.log
27 | import pycam.Utils
28 | import pycam.Importers.DXFImporter
29 | import pycam.Importers.STLImporter
30 | import pycam.Importers.SVGImporter
31 | import pycam.Importers.PSImporter
32 |
33 | import os
34 |
35 | log = pycam.Utils.log.get_logger()
36 |
37 |
38 | def detect_file_type(filename, quiet=False):
39 | # also accept URI input
40 | uri = pycam.Utils.URIHandler(filename)
41 | filename = uri.get_path()
42 | failure = (None, None)
43 | # check all listed importers
44 | # TODO: this should be done by evaluating the header of the file
45 | if filename.lower().endswith(".stl"):
46 | return ("stl", pycam.Importers.STLImporter.ImportModel)
47 | elif filename.lower().endswith(".dxf"):
48 | return ("dxf", pycam.Importers.DXFImporter.import_model)
49 | elif filename.lower().endswith(".svg"):
50 | return ("svg", pycam.Importers.SVGImporter.import_model)
51 | elif filename.lower().endswith(".eps") \
52 | or filename.lower().endswith(".ps"):
53 | return ("ps", pycam.Importers.PSImporter.import_model)
54 | else:
55 | if not quiet:
56 | log.error(("Importers: Failed to detect the model type of '%s'. " \
57 | + "Is the file extension (stl/dxf/svg/eps/ps) correct?") \
58 | % filename)
59 | return failure
60 |
61 |
--------------------------------------------------------------------------------
/pycam/PathGenerators/DropCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010-2011 Lars Kruse
6 | Copyright 2008-2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | from pycam.PathGenerators import get_max_height_dynamic
25 | from pycam.Utils import ProgressCounter
26 | from pycam.Utils.threading import run_in_parallel
27 | import pycam.Geometry.Model
28 | import pycam.Utils.log
29 |
30 | log = pycam.Utils.log.get_logger()
31 |
32 |
33 | # We need to use a global function here - otherwise it does not work with
34 | # the multiprocessing Pool.
35 | def _process_one_grid_line((positions, minz, maxz, model, cutter, physics)):
36 | """ This function assumes, that the positions are next to each other.
37 | Otherwise the dynamic over-sampling (in get_max_height_dynamic) is
38 | pointless.
39 | """
40 | return get_max_height_dynamic(model, cutter, positions, minz, maxz, physics)
41 |
42 |
43 | class DropCutter(object):
44 |
45 | def __init__(self, path_processor, physics=None):
46 | self.pa = path_processor
47 | self.physics = physics
48 |
49 | def GenerateToolPath(self, cutter, models, motion_grid, minz=None, maxz=None, draw_callback=None):
50 | quit_requested = False
51 | model = pycam.Geometry.Model.get_combined_model(models)
52 |
53 | # Transfer the grid (a generator) into a list of lists and count the
54 | # items.
55 | lines = []
56 | # usually there is only one layer - but an xy-grid consists of two
57 | for layer in motion_grid:
58 | for line in layer:
59 | lines.append(line)
60 |
61 | num_of_lines = len(lines)
62 | progress_counter = ProgressCounter(len(lines), draw_callback)
63 | current_line = 0
64 |
65 | self.pa.new_direction(0)
66 |
67 | args = []
68 | for one_grid_line in lines:
69 | # simplify the data (useful for remote processing)
70 | xy_coords = [(pos.x, pos.y) for pos in one_grid_line]
71 | args.append((xy_coords, minz, maxz, model, cutter,
72 | self.physics))
73 | for points in run_in_parallel(_process_one_grid_line, args,
74 | callback=progress_counter.update):
75 | self.pa.new_scanline()
76 | if draw_callback and draw_callback(text="DropCutter: processing " \
77 | + "line %d/%d" % (current_line + 1, num_of_lines)):
78 | # cancel requested
79 | quit_requested = True
80 | break
81 | for point in points:
82 | if point is None:
83 | # exceeded maxz - the cutter has to skip this point
84 | self.pa.end_scanline()
85 | self.pa.new_scanline()
86 | continue
87 | self.pa.append(point)
88 | # "draw_callback" returns true, if the user requested to quit
89 | # via the GUI.
90 | # The progress counter may return True, if cancel was requested.
91 | if draw_callback and draw_callback(tool_position=point,
92 | toolpath=self.pa.paths):
93 | quit_requested = True
94 | break
95 | progress_counter.increment()
96 | self.pa.end_scanline()
97 | # update progress
98 | current_line += 1
99 | if quit_requested:
100 | break
101 | self.pa.end_direction()
102 | self.pa.finish()
103 | return self.pa.paths
104 |
105 |
--------------------------------------------------------------------------------
/pycam/PathGenerators/EngraveCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2008-2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import pycam.PathProcessors.PathAccumulator
25 | from pycam.Geometry.Point import Point, Vector
26 | from pycam.Geometry.Line import Line
27 | from pycam.Geometry.Plane import Plane
28 | from pycam.Geometry.utils import ceil
29 | from pycam.PathGenerators import get_max_height_dynamic, get_free_paths_ode, \
30 | get_free_paths_triangles
31 | from pycam.Utils import ProgressCounter
32 | import pycam.Utils.log
33 |
34 | log = pycam.Utils.log.get_logger()
35 |
36 |
37 | class EngraveCutter(object):
38 |
39 | def __init__(self, path_processor, physics=None):
40 | self.pa_push = path_processor
41 | # We use a separated path processor for the last "drop" layer.
42 | # This path processor does not need to be configurable.
43 | self.pa_drop = pycam.PathProcessors.PathAccumulator.PathAccumulator(
44 | reverse=self.pa_push.reverse)
45 | self.physics = physics
46 |
47 | def GenerateToolPath(self, cutter, models, motion_grid, minz=None,
48 | maxz=None, draw_callback=None):
49 | quit_requested = False
50 |
51 | model = pycam.Geometry.Model.get_combined_model(models)
52 |
53 | if draw_callback:
54 | draw_callback(text="Engrave: optimizing polygon order")
55 |
56 | # resolve the generator
57 | motion_grid = list(motion_grid)
58 | num_of_layers = len(motion_grid)
59 |
60 | push_layers = motion_grid[:-1]
61 | push_generator = pycam.PathGenerators.PushCutter.PushCutter(
62 | self.pa_push, physics=self.physics)
63 | current_layer = 0
64 | for push_layer in push_layers:
65 | # update the progress bar and check, if we should cancel the process
66 | if draw_callback and draw_callback(text="Engrave: processing " \
67 | + "layer %d/%d" % (current_layer + 1, num_of_layers)):
68 | # cancel immediately
69 | quit_requested = True
70 | break
71 | # no callback: otherwise the status text gets lost
72 | push_generator.GenerateToolPath(cutter, [model], [push_layer])
73 | if draw_callback and draw_callback():
74 | # cancel requested
75 | quit_requested = True
76 | break
77 | current_layer += 1
78 |
79 | if quit_requested:
80 | return self.pa_push.paths
81 |
82 | drop_generator = pycam.PathGenerators.DropCutter.DropCutter(self.pa_drop,
83 | physics=self.physics)
84 | drop_layers = motion_grid[-1:]
85 | if draw_callback:
86 | draw_callback(text="Engrave: processing layer " + \
87 | "%d/%d" % (current_layer + 1, num_of_layers))
88 | drop_generator.GenerateToolPath(cutter, [model], drop_layers,
89 | minz=minz, maxz=maxz, draw_callback=draw_callback)
90 | return self.pa_push.paths + self.pa_drop.paths
91 |
92 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/ContourCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008-2010 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.PathProcessors
24 | from pycam.Geometry.PolygonExtractor import PolygonExtractor
25 | from pycam.Geometry.Point import Point
26 | from pycam.Toolpath import simplify_toolpath
27 |
28 | class ContourCutter(pycam.PathProcessors.BasePathProcessor):
29 | def __init__(self, reverse=False):
30 | super(ContourCutter, self).__init__()
31 | self.curr_path = None
32 | self.scanline = None
33 | self.polygon_extractor = None
34 | self.points = []
35 | self.reverse = reverse
36 | self.__forward = Point(1, 1, 0)
37 |
38 | def append(self, point):
39 | # Sort the points in positive x/y direction - otherwise the
40 | # PolygonExtractor breaks.
41 | if self.points and (point.sub(self.points[0]).dot(self.__forward) < 0):
42 | self.points.insert(0, point)
43 | else:
44 | self.points.append(point)
45 |
46 | def new_direction(self, direction):
47 | if self.polygon_extractor == None:
48 | self.polygon_extractor = PolygonExtractor(PolygonExtractor.CONTOUR)
49 |
50 | self.polygon_extractor.new_direction(direction)
51 |
52 | def end_direction(self):
53 | self.polygon_extractor.end_direction()
54 |
55 | def new_scanline(self):
56 | self.polygon_extractor.new_scanline()
57 | self.points = []
58 |
59 | def end_scanline(self):
60 | for i in range(1, len(self.points) - 1):
61 | self.polygon_extractor.append(self.points[i])
62 | self.polygon_extractor.end_scanline()
63 |
64 | def finish(self):
65 | self.polygon_extractor.finish()
66 | if self.polygon_extractor.merge_path_list:
67 | paths = self.polygon_extractor.merge_path_list
68 | elif self.polygon_extractor.hor_path_list:
69 | paths = self.polygon_extractor.hor_path_list
70 | else:
71 | paths = self.polygon_extractor.ver_path_list
72 | if paths:
73 | for path in paths:
74 | path.append(path.points[0])
75 | simplify_toolpath(path)
76 | if paths:
77 | if self.reverse:
78 | paths.reverse()
79 | self.paths.extend(paths)
80 | self.sort_layered()
81 | self.polygon_extractor = None
82 |
83 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/PathAccumulator.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2008 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import pycam.PathProcessors
25 | from pycam.Toolpath import simplify_toolpath
26 | from pycam.Geometry.Path import Path
27 |
28 |
29 | class PathAccumulator(pycam.PathProcessors.BasePathProcessor):
30 | def __init__(self, zigzag=False, reverse=False):
31 | super(PathAccumulator, self).__init__()
32 | self.curr_path = None
33 | self.zigzag = zigzag
34 | self.scanline = None
35 | self.reverse = reverse
36 |
37 | def append(self, point):
38 | if self.curr_path == None:
39 | self.curr_path = Path()
40 | if self.reverse:
41 | self.curr_path.insert(0, point)
42 | else:
43 | self.curr_path.append(point)
44 |
45 | def new_direction(self, direction):
46 | self.scanline = 0
47 |
48 | def new_scanline(self):
49 | self.scanline += 1
50 | if self.curr_path:
51 | print "ERROR: curr_path expected to be empty"
52 | self.curr_path = None
53 |
54 | def end_scanline(self):
55 | if self.curr_path:
56 | if self.zigzag and (self.scanline % 2 == 0):
57 | self.curr_path.reverse()
58 | simplify_toolpath(self.curr_path)
59 | if self.reverse:
60 | self.paths.insert(0, self.curr_path)
61 | else:
62 | self.paths.append(self.curr_path)
63 | self.curr_path = None
64 |
65 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/PolygonCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2008 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import pycam.PathProcessors
25 | from pycam.Geometry.Path import Path
26 | from pycam.Geometry.PolygonExtractor import PolygonExtractor
27 | from pycam.Toolpath import simplify_toolpath
28 |
29 |
30 | class PolygonCutter(pycam.PathProcessors.BasePathProcessor):
31 | def __init__(self, reverse=False):
32 | super(PolygonCutter, self).__init__()
33 | self.curr_path = None
34 | self.scanline = None
35 | self.polygon_extractor = PolygonExtractor(PolygonExtractor.MONOTONE)
36 | self.reverse = reverse
37 |
38 | def append(self, point):
39 | self.polygon_extractor.append(point)
40 |
41 | def new_direction(self, direction):
42 | self.polygon_extractor.new_direction(direction)
43 |
44 | def end_direction(self):
45 | self.polygon_extractor.end_direction()
46 |
47 | def new_scanline(self):
48 | self.polygon_extractor.new_scanline()
49 |
50 | def end_scanline(self):
51 | self.polygon_extractor.end_scanline()
52 |
53 | def finish(self):
54 | self.polygon_extractor.finish()
55 | paths = []
56 | source_paths = []
57 | if self.polygon_extractor.hor_path_list:
58 | source_paths.extend(self.polygon_extractor.hor_path_list)
59 | if self.polygon_extractor.ver_path_list:
60 | source_paths.extend(self.polygon_extractor.ver_path_list)
61 | for path in source_paths:
62 | points = path.points
63 | for i in range(0, (len(points)+1)/2):
64 | new_path = Path()
65 | if i % 2 == 0:
66 | new_path.append(points[i])
67 | new_path.append(points[-i-1])
68 | else:
69 | new_path.append(points[-i-1])
70 | new_path.append(points[i])
71 | paths.append(new_path)
72 | if paths:
73 | for path in paths:
74 | simplify_toolpath(path)
75 | if self.reverse:
76 | path.reverse()
77 | self.paths.extend(paths)
78 | self.sort_layered()
79 |
80 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/SimpleCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.PathProcessors
24 | from pycam.Geometry.Path import Path
25 | from pycam.Toolpath import simplify_toolpath
26 |
27 | class SimpleCutter(pycam.PathProcessors.BasePathProcessor):
28 | def __init__(self, reverse=False):
29 | super(SimpleCutter, self).__init__()
30 | self.curr_path = None
31 | self.reverse = reverse
32 |
33 | def append(self, point):
34 | curr_path = None
35 | if self.curr_path == None:
36 | curr_path = Path()
37 | self.curr_path = curr_path
38 | else:
39 | curr_path = self.curr_path
40 | self.curr_path = None
41 | curr_path.append(point)
42 | if self.curr_path == None:
43 | simplify_toolpath(curr_path)
44 | if self.reverse:
45 | curr_path.reverse()
46 | self.paths.insert(0, curr_path)
47 | else:
48 | self.paths.append(curr_path)
49 |
50 | def new_scanline(self):
51 | if self.curr_path:
52 | print "ERROR: curr_path expected to be empty"
53 | self.curr_path = None
54 |
55 | def end_scanline(self):
56 | if self.curr_path:
57 | print "ERROR: curr_path expected to be empty"
58 | self.curr_path = None
59 |
60 | def finish(self):
61 | self.sort_layered()
62 |
63 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/ZigZagCutter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.PathProcessors
24 | from pycam.Geometry.Path import Path
25 | from pycam.Toolpath import simplify_toolpath
26 |
27 | class ZigZagCutter(pycam.PathProcessors.BasePathProcessor):
28 | def __init__(self, reverse=False):
29 | super(ZigZagCutter, self).__init__()
30 | self.curr_path = None
31 | self.scanline = None
32 | self.curr_scanline = None
33 | self.reverse = reverse
34 |
35 | def append(self, point):
36 | curr_path = None
37 | if self.curr_path == None:
38 | curr_path = Path()
39 | self.curr_path = curr_path
40 | else:
41 | curr_path = self.curr_path
42 | self.curr_path = None
43 |
44 | curr_path.append(point)
45 |
46 | if self.curr_path == None:
47 | if (self.scanline % 2) == 0:
48 | self.curr_scanline.append(curr_path)
49 | else:
50 | curr_path.reverse()
51 | self.curr_scanline.insert(0, curr_path)
52 |
53 | def new_direction(self, direction):
54 | self.scanline = 0
55 |
56 | def new_scanline(self):
57 | self.scanline += 1
58 | self.curr_scanline = []
59 |
60 | def end_scanline(self):
61 | for path in self.curr_scanline:
62 | simplify_toolpath(path)
63 | if self.reverse:
64 | path.reverse()
65 | self.paths.append(path)
66 | self.curr_scanline = None
67 |
68 |
--------------------------------------------------------------------------------
/pycam/PathProcessors/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = ["PathAccumulator", "SimpleCutter", "ZigZagCutter", "PolygonCutter",
24 | "ContourCutter", "BasePathProcessor"]
25 |
26 |
27 | class BasePathProcessor(object):
28 |
29 | def __init__(self):
30 | self.paths = []
31 |
32 | def new_direction(self, direction):
33 | pass
34 |
35 | def end_direction(self):
36 | pass
37 |
38 | def finish(self):
39 | pass
40 |
41 | def sort_layered(self, upper_first=True):
42 | if upper_first:
43 | compare_height = lambda path1, path2: \
44 | path1.points[0].z < path2.points[0].z
45 | else:
46 | compare_height = lambda path1, path2: \
47 | path1.points[0].z > path2.points[0].z
48 | finished = False
49 | while not finished:
50 | index = 0
51 | finished = True
52 | while index < len(self.paths) - 1:
53 | current_path = self.paths[index]
54 | next_path = self.paths[index + 1]
55 | if compare_height(current_path, next_path):
56 | del self.paths[index]
57 | self.paths.insert(index + 1, current_path)
58 | finished = False
59 | index += 1
60 |
61 |
--------------------------------------------------------------------------------
/pycam/Physics/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = ["ode"]
24 |
25 |
--------------------------------------------------------------------------------
/pycam/Plugins/EMCToolExport.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | import pycam.Exporters.EMCToolExporter
26 |
27 | FILTER_EMC_TOOL = (("EMC tool files", "*.tbl"),)
28 |
29 | class EMCToolExport(pycam.Plugins.PluginBase):
30 |
31 | UI_FILE = "emc_tool_export.ui"
32 | DEPENDS = ["Tools", "FilenameDialog"]
33 | CATEGORIES = ["Export"]
34 |
35 | def setup(self):
36 | self._last_emc_tool_file = None
37 | if self.gui:
38 | self.export_action = self.gui.get_object("ExportEMCToolDefinition")
39 | self.register_gtk_accelerator("export", self.export_action, None,
40 | "ExportEMCToolDefinition")
41 | self._gtk_handlers = ((self.export_action, "activate",
42 | self.export_emc_tools), )
43 | self.core.register_ui("export_menu", "ExportEMCToolDefinition",
44 | self.export_action, 80)
45 | self._event_handlers = (("tool-selection-changed",
46 | self._update_emc_tool_button), )
47 | self.register_gtk_handlers(self._gtk_handlers)
48 | self.register_event_handlers(self._event_handlers)
49 | self._update_emc_tool_button()
50 | return True
51 |
52 | def teardown(self):
53 | if self.gui:
54 | self.core.unregister_ui("export_menu", self.export_action)
55 | self.unregister_gtk_accelerator("export", self.export_action)
56 | self.unregister_gtk_handlers(self._gtk_handlers)
57 | self.unregister_event_handlers(self._event_handlers)
58 |
59 | def _update_emc_tool_button(self, widget=None):
60 | exportable = len(self.core.get("tools")) > 0
61 | self.export_action.set_sensitive(exportable)
62 |
63 | def export_emc_tools(self, widget=None, filename=None):
64 | if callable(filename):
65 | filename = filename()
66 | if not filename:
67 | # TODO: separate this away from Gui/Project.py
68 | # TODO: implement "last_model_filename" in core
69 | filename = self.core.get("get_filename_func")("Save toolpath to ...",
70 | mode_load=False, type_filter=FILTER_EMC_TOOL,
71 | filename_templates=(self._last_emc_tool_file,
72 | self.core.get("last_model_filename")))
73 | if filename:
74 | self._last_emc_tool_file = filename
75 | tools_dict = []
76 | tools = self.core.get("tools")
77 | for tool in tools:
78 | tools_dict.append({"name": tool["name"], "id": tool["id"],
79 | "radius": tool["parameters"].get("radius", 1)})
80 | export = pycam.Exporters.EMCToolExporter.EMCToolExporter(tools_dict)
81 | text = export.get_tool_definition_string()
82 | try:
83 | out = file(filename, "w")
84 | out.write(text)
85 | out.close()
86 | self.log.info("EMC tool file written: %s" % filename)
87 | except IOError, err_msg:
88 | self.log.error("Failed to save EMC tool file: %s" % err_msg)
89 | else:
90 | self.core.emit_event("notify-file-saved", filename)
91 |
92 |
--------------------------------------------------------------------------------
/pycam/Plugins/GCodeTouchOff.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class GCodeTouchOff(pycam.Plugins.PluginBase):
28 |
29 | DEPENDS = ["GCodePreferences"]
30 | CATEGORIES = ["GCode"]
31 | UI_FILE = "gcode_touch_off.ui"
32 |
33 | def setup(self):
34 | if self.gui:
35 | box = self.gui.get_object("TouchOffBox")
36 | box.unparent()
37 | self.core.register_ui("gcode_preferences", "Touch Off",
38 | box, weight=70)
39 | self._gtk_handlers = []
40 | for objname, setting in (
41 | ("GCodeTouchOffOnStartup", "touch_off_on_startup"),
42 | ("GCodeTouchOffOnToolChange", "touch_off_on_tool_change")):
43 | obj = self.gui.get_object(objname)
44 | self._gtk_handlers.append((obj, "toggled", self.update_widgets))
45 | self.core.add_item(setting, obj.get_active, obj.set_active)
46 | selector = self.gui.get_object("TouchOffLocationSelector")
47 | self._gtk_handlers.append((selector, "changed", self.update_widgets))
48 | selector.set_active(0)
49 | self.register_gtk_handlers(self._gtk_handlers)
50 | self.update_widgets()
51 | return True
52 |
53 | def teardown(self):
54 | if self.gui:
55 | self.core.unregister_ui("gcode_preferences",
56 | self.gui.get_object("TouchOffBox"))
57 | self.unregister_gtk_handlers(self._gtk_handlers)
58 | for setting in ("touch_off_on_startup", "touch_off_on_tool_change"):
59 | del self.core[setting]
60 |
61 | def update_widgets(self, widget=None):
62 | # tool change controls
63 | pos_control = self.gui.get_object("TouchOffLocationSelector")
64 | tool_change_pos_model = pos_control.get_model()
65 | active_pos_index = pos_control.get_active()
66 | if active_pos_index < 0:
67 | pos_key = None
68 | else:
69 | pos_key = tool_change_pos_model[active_pos_index][0]
70 | # disable/enable the touch off position controls
71 | position_controls_table = self.gui.get_object("TouchOffLocationTable")
72 | touch_off_enabled = any([self.gui.get_object(objname).get_active()
73 | for objname in ("GCodeTouchOffOnStartup",
74 | "GCodeTouchOffOnToolChange")])
75 | position_controls_table.set_sensitive(touch_off_enabled)
76 | # show or hide the vbox containing the absolute tool change location
77 | absolute_pos_box = self.gui.get_object("AbsoluteToolChangePositionBox")
78 | if (pos_key == "absolute") and touch_off_enabled:
79 | absolute_pos_box.show()
80 | else:
81 | absolute_pos_box.hide()
82 | # disable/enable touch probe height
83 | if self.gui.get_object("GCodeTouchOffOnStartup").get_active():
84 | update_func = "show"
85 | else:
86 | update_func = "hide"
87 | for objname in ("TouchOffHeight", "TouchOffHeightLabel",
88 | "LengthUnitTouchOffHeight"):
89 | getattr(self.gui.get_object(objname), update_func)()
90 |
91 |
--------------------------------------------------------------------------------
/pycam/Plugins/ModelPlaneMirror.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class ModelPlaneMirror(pycam.Plugins.PluginBase):
28 |
29 | UI_FILE = "model_plane_mirror.ui"
30 | DEPENDS = ["Models"]
31 | CATEGORIES = ["Model"]
32 |
33 | def setup(self):
34 | if self.gui:
35 | mirror_box = self.gui.get_object("ModelMirrorBox")
36 | mirror_box.unparent()
37 | self.core.register_ui("model_handling", "Mirror", mirror_box, 0)
38 | self._gtk_handlers = ((self.gui.get_object("PlaneMirrorButton"),
39 | "clicked", self._plane_mirror), )
40 | self._event_handlers = (("model-selection-changed",
41 | self._update_plane_widgets), )
42 | self.register_gtk_handlers(self._gtk_handlers)
43 | self.register_event_handlers(self._event_handlers)
44 | self._update_plane_widgets()
45 | return True
46 |
47 | def teardown(self):
48 | if self.gui:
49 | self.unregister_gtk_handlers(self._gtk_handlers)
50 | self.unregister_event_handlers(self._event_handlers)
51 |
52 | def _update_plane_widgets(self):
53 | plane_widget = self.gui.get_object("ModelMirrorBox")
54 | if self.core.get("models").get_selected():
55 | plane_widget.show()
56 | else:
57 | plane_widget.hide()
58 |
59 | def _plane_mirror(self, widget=None):
60 | models = self.core.get("models").get_selected()
61 | if not models:
62 | return
63 | self.core.emit_event("model-change-before")
64 | progress = self.core.get("progress")
65 | progress.update(text="Mirroring model")
66 | progress.disable_cancel()
67 | progress.set_multiple(len(models), "Model")
68 | for plane in ("XY", "XZ", "YZ"):
69 | if self.gui.get_object("MirrorPlane%s" % plane).get_active():
70 | break
71 | for model in models:
72 | model.model.transform_by_template("%s_mirror" % plane.lower(),
73 | callback=progress.update)
74 | progress.update_multiple()
75 | progress.finish()
76 | self.core.emit_event("model-change-after")
77 |
78 |
--------------------------------------------------------------------------------
/pycam/Plugins/ModelPolygons.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class ModelPolygons(pycam.Plugins.PluginBase):
28 |
29 | UI_FILE = "model_polygons.ui"
30 | DEPENDS = ["Models"]
31 | CATEGORIES = ["Model"]
32 |
33 | def setup(self):
34 | if self.gui:
35 | polygon_frame = self.gui.get_object("ModelPolygonFrame")
36 | polygon_frame.unparent()
37 | self.core.register_ui("model_handling", "Polygons", polygon_frame, 0)
38 | self._gtk_handlers = (
39 | (self.gui.get_object("ToggleModelDirectionButton"), "clicked",
40 | self._toggle_direction),
41 | (self.gui.get_object("DirectionsGuessButton"), "clicked",
42 | self._revise_directions))
43 | self._event_handlers = (
44 | ("model-change-after", self._update_polygon_controls),
45 | ("model-selection-changed", self._update_polygon_controls))
46 | self.register_gtk_handlers(self._gtk_handlers)
47 | self.register_event_handlers(self._event_handlers)
48 | self._update_polygon_controls()
49 | return True
50 |
51 | def teardown(self):
52 | if self.gui:
53 | self.core.unregister_ui("model_handling",
54 | self.gui.get_object("ModelPolygonFrame"))
55 | self.unregister_gtk_handlers(self._gtk_handlers)
56 | self.unregister_event_handlers(self._event_handlers)
57 |
58 | def _get_polygon_models(self):
59 | models = []
60 | for model in self.core.get("models").get_selected():
61 | if model and hasattr(model.model, "reverse_directions"):
62 | models.append(model)
63 | return models
64 |
65 | def _update_polygon_controls(self):
66 | models = self._get_polygon_models()
67 | frame = self.gui.get_object("ModelPolygonFrame")
68 | if models:
69 | frame.show()
70 | else:
71 | frame.hide()
72 |
73 | def _toggle_direction(self, widget=None):
74 | models = self._get_polygon_models()
75 | if not models:
76 | return
77 | self.core.emit_event("model-change-before")
78 | progress = self.core.get("progress")
79 | progress.update(text="Reversing directions of contour model")
80 | progress.set_multiple(len(models), "Model")
81 | for model in models:
82 | model.model.reverse_directions(callback=progress.update)
83 | progress.update_multiple()
84 | progress.finish()
85 | self.core.emit_event("model-change-after")
86 |
87 | def _revise_directions(self, widget=None):
88 | models = self._get_polygon_models()
89 | if not models:
90 | return
91 | self.core.emit_event("model-change-before")
92 | progress = self.core.get("progress")
93 | progress.update(text="Analyzing directions of contour model")
94 | progress.set_multiple(len(models), "Model")
95 | for model in models:
96 | model.model.revise_directions(callback=progress.update)
97 | progress.update_multiple()
98 | progress.finish()
99 | self.core.emit_event("model-change-after")
100 |
101 |
--------------------------------------------------------------------------------
/pycam/Plugins/ModelProjection.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | from pycam.Geometry.Plane import Plane
26 | from pycam.Geometry.Point import Point, Vector
27 |
28 |
29 | class ModelProjection(pycam.Plugins.PluginBase):
30 |
31 | UI_FILE = "model_projection.ui"
32 | DEPENDS = ["Models"]
33 | CATEGORIES = ["Model"]
34 |
35 | def setup(self):
36 | if self.gui:
37 | projection_frame = self.gui.get_object("ModelProjectionFrame")
38 | projection_frame.unparent()
39 | self.core.register_ui("model_handling", "Projection",
40 | projection_frame, 10)
41 | self._gtk_handlers = ((self.gui.get_object("ProjectionButton"),
42 | "clicked", self._projection), )
43 | self._event_handlers = (
44 | ("model-change-after", self._update_controls),
45 | ("model-selection-changed", self._update_controls))
46 | self.register_gtk_handlers(self._gtk_handlers)
47 | self.register_event_handlers(self._event_handlers)
48 | self._update_controls()
49 | return True
50 |
51 | def teardown(self):
52 | if self.gui:
53 | self.core.unregister_ui("model_handling",
54 | self.gui.get_object("ModelProjectionFrame"))
55 | self.unregister_gtk_handlers(self._gtk_handlers)
56 | self.unregister_event_handlers(self._event_handlers)
57 |
58 | def _get_projectable_models(self):
59 | models = self.core.get("models").get_selected()
60 | projectables = []
61 | for model in models:
62 | if (not model is None) and \
63 | hasattr(model.model, "get_waterline_contour"):
64 | projectables.append(model)
65 | return projectables
66 |
67 | def _update_controls(self):
68 | models = self._get_projectable_models()
69 | control = self.gui.get_object("ModelProjectionFrame")
70 | if models:
71 | control.show()
72 | else:
73 | control.hide()
74 |
75 | def _projection(self, widget=None):
76 | models = self._get_projectable_models()
77 | if not models:
78 | return
79 | progress = self.core.get("progress")
80 | progress.update(text="Calculating 2D projection")
81 | progress.set_multiple(len(models), "Model")
82 | for model_dict in models:
83 | model = model_dict.model
84 | for objname, z_level in (("ProjectionModelTop", model.maxz),
85 | ("ProjectionModelMiddle", (model.minz + model.maxz) / 2.0),
86 | ("ProjectionModelBottom", model.minz),
87 | ("ProjectionModelCustom",
88 | self.gui.get_object("ProjectionZLevel").get_value())):
89 | if self.gui.get_object(objname).get_active():
90 | plane = Plane(Point(0, 0, z_level), Vector(0, 0, 1))
91 | self.log.info("Projecting 3D model at level z=%g" % plane.p.z)
92 | new_model = model.get_waterline_contour(plane,
93 | callback=progress.update)
94 | if new_model:
95 | self.core.get("models").add_model(new_model,
96 | name_template="Projected model #%d")
97 | else:
98 | self.log.warn("The 2D projection at z=%g is empty. Aborted." % \
99 | plane.p.z)
100 | break
101 | progress.update_multiple()
102 | progress.finish()
103 |
104 |
--------------------------------------------------------------------------------
/pycam/Plugins/ModelRotation.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | import pycam.Geometry.Matrix
26 |
27 |
28 | class ModelRotation(pycam.Plugins.PluginBase):
29 |
30 | UI_FILE = "model_rotation.ui"
31 | DEPENDS = ["Models"]
32 | CATEGORIES = ["Model"]
33 |
34 | def setup(self):
35 | if self.gui:
36 | rotation_box = self.gui.get_object("ModelRotationBox")
37 | rotation_box.unparent()
38 | self.core.register_ui("model_handling", "Rotation", rotation_box,
39 | -10)
40 | self._gtk_handlers = ((self.gui.get_object("RotateModelButton"),
41 | "clicked", self._rotate_model), )
42 | self._event_handlers = (("model-selection-changed",
43 | self._update_controls), )
44 | self.register_gtk_handlers(self._gtk_handlers)
45 | self.register_event_handlers(self._event_handlers)
46 | self._update_controls()
47 | return True
48 |
49 | def teardown(self):
50 | if self.gui:
51 | self.core.unregister_ui("model_handling",
52 | self.gui.get_object("ModelRotationBox"))
53 | self.unregister_gtk_handlers(self._gtk_handlers)
54 | self.unregister_event_handlers(self._event_handlers)
55 |
56 | def _update_controls(self):
57 | widget = self.gui.get_object("ModelRotationBox")
58 | if self.core.get("models").get_selected():
59 | widget.show()
60 | else:
61 | widget.hide()
62 |
63 | def _rotate_model(self, widget=None):
64 | models = self.core.get("models").get_selected()
65 | if not models:
66 | return
67 | self.core.emit_event("model-change-before")
68 | for axis in "XYZ":
69 | if self.gui.get_object("RotationAxis%s" % axis).get_active():
70 | break
71 | axis_vector = {"X": (1, 0, 0), "Y": (0, 1, 0), "Z": (0, 0, 1)}[axis]
72 | for control, angle in (("RotationAngle90CCKW", -90),
73 | ("RotationAngle90CKW", 90),
74 | ("RotationAngle180", 180),
75 | ("RotationAngleCustomCKW",
76 | self.gui.get_object("RotationAngle").get_value())):
77 | if self.gui.get_object(control).get_active():
78 | break
79 | matrix = pycam.Geometry.Matrix.get_rotation_matrix_axis_angle(
80 | axis_vector, angle, use_radians=False)
81 | progress = self.core.get("progress")
82 | progress.update(text="Rotating model")
83 | progress.disable_cancel()
84 | progress.set_multiple(len(models), "Model")
85 | for model in models:
86 | model.model.transform_by_matrix(matrix, callback=progress.update)
87 | progress.update_multiple()
88 | self.core.emit_event("model-change-after")
89 | progress.finish()
90 |
91 |
--------------------------------------------------------------------------------
/pycam/Plugins/ModelSwapAxes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class ModelSwapAxes(pycam.Plugins.PluginBase):
28 |
29 | UI_FILE = "model_swap_axes.ui"
30 | DEPENDS = ["Models"]
31 | CATEGORIES = ["Model"]
32 |
33 | def setup(self):
34 | if self.gui:
35 | swap_box = self.gui.get_object("ModelSwapBox")
36 | swap_box.unparent()
37 | self.core.register_ui("model_handling", "Swap axes", swap_box, 0)
38 | self._gtk_handlers = ((self.gui.get_object("SwapAxesButton"),
39 | "clicked", self._swap_axes), )
40 | self._event_handlers = (("model-selection-changed",
41 | self._update_controls), )
42 | self.register_gtk_handlers(self._gtk_handlers)
43 | self.register_event_handlers(self._event_handlers)
44 | self._update_controls()
45 | return True
46 |
47 | def teardown(self):
48 | if self.gui:
49 | self.core.unregister_ui("model_handling",
50 | self.gui.get_object("ModelSwapBox"))
51 | self.unregister_gtk_handlers(self._gtk_handlers)
52 | self.unregister_event_handlers(self._event_handlers)
53 |
54 | def _update_controls(self):
55 | box = self.gui.get_object("ModelSwapBox")
56 | if self.core.get("models").get_selected():
57 | box.show()
58 | else:
59 | box.hide()
60 |
61 | def _swap_axes(self, widget=None):
62 | models = self.core.get("models").get_selected()
63 | if not models:
64 | return
65 | self.core.emit_event("model-change-before")
66 | for axes, template in (("XY", "x_swap_y"), ("XZ", "x_swap_z"),
67 | ("YZ", "y_swap_z")):
68 | if self.gui.get_object("SwapAxes%s" % axes).get_active():
69 | break
70 | progress = self.core.get("progress")
71 | progress.update(text="Swap axes of model")
72 | progress.disable_cancel()
73 | progress.set_multiple(len(models), "Model")
74 | for model in models:
75 | model.model.transform_by_template(template,
76 | callback=progress.update)
77 | progress.update_multiple()
78 | progress.finish()
79 | self.core.emit_event("model-change-after")
80 |
81 |
--------------------------------------------------------------------------------
/pycam/Plugins/OpenGLViewAxes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.Plugins
24 | from pycam.Gui.OpenGLTools import draw_direction_cone
25 |
26 |
27 | class OpenGLViewAxes(pycam.Plugins.PluginBase):
28 |
29 | DEPENDS = ["OpenGLWindow"]
30 | CATEGORIES = ["Visualization", "OpenGL"]
31 |
32 | def setup(self):
33 | import OpenGL.GL
34 | self._GL = OpenGL.GL
35 | self.core.register_event("visualize-items", self.draw_axes)
36 | self.core.get("register_display_item")("show_axes",
37 | "Show Coordinate System", 50)
38 | self.core.emit_event("visual-item-updated")
39 | return True
40 |
41 | def teardown(self):
42 | self.core.unregister_event("visualize-items", self.draw_axes)
43 | self.core.get("unregister_display_item")("show_axes")
44 | self.core.emit_event("visual-item-updated")
45 |
46 | def draw_axes(self):
47 | if not self.core.get("show_axes"):
48 | return
49 | GL = self._GL
50 | GL.glMatrixMode(GL.GL_MODELVIEW)
51 | GL.glLoadIdentity()
52 | low, high = [None, None, None], [None, None, None]
53 | self.core.call_chain("get_draw_dimension", low, high)
54 | if None in low or None in high:
55 | low, high = (0, 0, 0), (10, 10, 10)
56 | length = 1.2 * max(max(high), abs(min(low)))
57 | origin = (0, 0, 0)
58 | cone_length = 0.05
59 | old_line_width = GL.glGetFloatv(GL.GL_LINE_WIDTH)
60 | if self.core.get("view_light"):
61 | GL.glDisable(GL.GL_LIGHTING)
62 | GL.glLineWidth(1.5)
63 | # draw a colored line ending in a cone for each axis
64 | for index in range(3):
65 | end = [0, 0, 0]
66 | end[index] = length
67 | color = [0.0, 0.0, 0.0]
68 | # reduced brightness (not 1.0)
69 | color[index] = 0.8
70 | GL.glColor3f(*color)
71 | # we need to wait until the color change is active
72 | GL.glFinish()
73 | GL.glBegin(GL.GL_LINES)
74 | GL.glVertex3f(*origin)
75 | GL.glVertex3f(*end)
76 | GL.glEnd()
77 | # Position the cone slightly behind the end of the line - otherwise
78 | # the end of the line (width=2) is visible at the top of the cone.
79 | draw_direction_cone(origin, end, position=1.0 + cone_length,
80 | precision=32, size=cone_length)
81 | GL.glLineWidth(old_line_width)
82 | if self.core.get("view_light"):
83 | GL.glEnable(GL.GL_LIGHTING)
84 |
85 |
--------------------------------------------------------------------------------
/pycam/Plugins/OpenGLViewBounds.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.Plugins
24 |
25 |
26 | class OpenGLViewBounds(pycam.Plugins.PluginBase):
27 |
28 | DEPENDS = ["OpenGLWindow", "Bounds"]
29 | CATEGORIES = ["Bounds", "Visualization", "OpenGL"]
30 |
31 | def setup(self):
32 | import OpenGL.GL
33 | self._GL = OpenGL.GL
34 | self.core.get("register_color")("color_bounding_box", "Bounding box",
35 | 40)
36 | self.core.get("register_display_item")("show_bounding_box",
37 | "Show Bounding Box", 40)
38 | self.core.register_chain("get_draw_dimension", self.get_draw_dimension)
39 | self.core.register_event("visualize-items", self.draw_bounds)
40 | self.core.emit_event("visual-item-updated")
41 | return True
42 |
43 | def teardown(self):
44 | self.core.unregister_event("visualize-items", self.draw_bounds)
45 | self.core.unregister_chain("get_draw_dimension",
46 | self.get_draw_dimension)
47 | self.core.get("unregister_color")("color_bounding_box")
48 | self.core.get("unregister_display_item")("show_bounding_box")
49 | self.core.emit_event("visual-item-updated")
50 |
51 | def get_draw_dimension(self, low, high):
52 | if not self.core.get("show_bounding_box"):
53 | return
54 | mlow, mhigh = self._get_bounds()
55 | if None in mlow or None in mhigh:
56 | return
57 | for index in range(3):
58 | if (low[index] is None) or (mlow[index] < low[index]):
59 | low[index] = mlow[index]
60 | if (high[index] is None) or (mhigh[index] > high[index]):
61 | high[index] = mhigh[index]
62 |
63 | def _get_bounds(self):
64 | bounds = self.core.get("bounds").get_selected()
65 | if not bounds:
66 | return ([None, None, None], [None, None, None])
67 | low, high = bounds.get_absolute_limits()
68 | return low, high
69 |
70 | def draw_bounds(self):
71 | GL = self._GL
72 | if not self.core.get("show_bounding_box"):
73 | return
74 | low, high = self._get_bounds()
75 | if None in low or None in high:
76 | return
77 | minx, miny, minz = low[0], low[1], low[2]
78 | maxx, maxy, maxz = high[0], high[1], high[2]
79 | p1 = [minx, miny, minz]
80 | p2 = [minx, maxy, minz]
81 | p3 = [maxx, maxy, minz]
82 | p4 = [maxx, miny, minz]
83 | p5 = [minx, miny, maxz]
84 | p6 = [minx, maxy, maxz]
85 | p7 = [maxx, maxy, maxz]
86 | p8 = [maxx, miny, maxz]
87 | if self.core.get("view_light"):
88 | GL.glDisable(GL.GL_LIGHTING)
89 | # lower rectangle
90 | color = self.core.get("color_bounding_box")
91 | GL.glColor4f(color["red"], color["green"], color["blue"], color["alpha"])
92 | GL.glFinish()
93 | GL.glBegin(GL.GL_LINES)
94 | # all combinations of neighbouring corners
95 | for corner_pair in [(p1, p2), (p1, p5), (p1, p4), (p2, p3),
96 | (p2, p6), (p3, p4), (p3, p7), (p4, p8), (p5, p6),
97 | (p6, p7), (p7, p8), (p8, p5)]:
98 | GL.glVertex3f(*(corner_pair[0]))
99 | GL.glVertex3f(*(corner_pair[1]))
100 | GL.glEnd()
101 | if self.core.get("view_light"):
102 | GL.glEnable(GL.GL_LIGHTING)
103 |
104 |
--------------------------------------------------------------------------------
/pycam/Plugins/OpenGLViewDimension.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class OpenGLViewDimension(pycam.Plugins.PluginBase):
28 |
29 | UI_FILE = "opengl_view_dimension.ui"
30 | DEPENDS = ["Models", "OpenGLWindow"]
31 | CATEGORIES = ["Model", "Visualization", "OpenGL"]
32 |
33 | def setup(self):
34 | if self.gui:
35 | import pango
36 | self.core.register_ui("opengl_window", "Dimension",
37 | self.gui.get_object("DimensionTable"), weight=20)
38 | self.core.get("register_display_item")("show_dimensions",
39 | "Show Dimensions", 60),
40 | # Color the dimension value according to the axes.
41 | # For "y" axis: 100% green is too bright on light background - we
42 | # reduce it a bit.
43 | for color, names in (
44 | (pango.AttrForeground(65535, 0, 0, 0, 100),
45 | ("model_dim_x_label", "model_dim_x", "ModelCornerXMax",
46 | "ModelCornerXMin", "ModelCornerXSpaces")),
47 | (pango.AttrForeground(0, 50000, 0, 0, 100),
48 | ("model_dim_y_label", "model_dim_y", "ModelCornerYMax",
49 | "ModelCornerYMin", "ModelCornerYSpaces")),
50 | (pango.AttrForeground(0, 0, 65535, 0, 100),
51 | ("model_dim_z_label", "model_dim_z", "ModelCornerZMax",
52 | "ModelCornerZMin", "ModelCornerZSpaces"))):
53 | attributes = pango.AttrList()
54 | attributes.insert(color)
55 | for name in names:
56 | self.gui.get_object(name).set_attributes(attributes)
57 | self._event_handlers = (
58 | ("model-change-after", self.update_model_dimensions),
59 | ("visual-item-updated", self.update_model_dimensions),
60 | ("model-list-chaned", self.update_model_dimensions))
61 | self.register_event_handlers(self._event_handlers)
62 | return True
63 |
64 | def teardown(self):
65 | if self.gui:
66 | self.unregister_event_handlers(self._event_handlers)
67 | self.core.unregister_ui("opengl_window",
68 | self.gui.get_object("DimensionTable"))
69 | self.core.get("unregister_display_item")("show_dimensions")
70 |
71 | def update_model_dimensions(self, widget=None):
72 | dimension_bar = self.gui.get_object("DimensionTable")
73 | models = [m.model for m in self.core.get("models").get_visible()]
74 | low, high = pycam.Geometry.Model.get_combined_bounds(models)
75 | if None in low or None in high:
76 | low, high = (0, 0, 0), (0, 0, 0)
77 | if self.core.get("show_dimensions"):
78 | for value, label_suffix in ((low[0], "XMin"), (low[1], "YMin"),
79 | (low[2], "ZMin"), (high[0], "XMax"), (high[1], "YMax"),
80 | (high[2], "ZMax")):
81 | label_name = "ModelCorner%s" % label_suffix
82 | value = "%.3f" % value
83 | self.gui.get_object(label_name).set_label(value)
84 | for name, size in (
85 | ("model_dim_x", high[0] - low[0]),
86 | ("model_dim_y", high[1] - low[1]),
87 | ("model_dim_z", high[2] - low[2])):
88 | self.gui.get_object(name).set_text("%.3f %s" \
89 | % (size, self.core.get("unit_string")))
90 |
91 | dimension_bar.show()
92 | else:
93 | dimension_bar.hide()
94 |
--------------------------------------------------------------------------------
/pycam/Plugins/OpenGLViewSupportModelPreview.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import pycam.Plugins
24 |
25 |
26 | class OpenGLViewSupportModelPreview(pycam.Plugins.PluginBase):
27 |
28 | DEPENDS = ["OpenGLWindow", "OpenGLViewModel"]
29 | CATEGORIES = ["Visualization", "OpenGL", "Support bridges"]
30 |
31 | def setup(self):
32 | import OpenGL.GL
33 | import OpenGL.GLUT
34 | self._GL = OpenGL.GL
35 | self.core.register_event("visualize-items", self.draw_support_preview)
36 | self.core.get("register_display_item")("show_support_preview", "Show Support Model Preview", 30)
37 | self.core.get("register_color")("color_support_preview", "Support model", 30)
38 | self.core.emit_event("visual-item-updated")
39 | return True
40 |
41 | def teardown(self):
42 | self.core.unregister_event("visualize-items", self.draw_support_preview)
43 | self.core.get("unregister_display_item")("show_support_preview")
44 | self.core.get("unregister_color")("color_support_preview")
45 | self.core.emit_event("visual-item-updated")
46 |
47 | def draw_support_preview(self):
48 | if not self.core.get("show_support_preview"):
49 | return
50 | models = self.core.get("current_support_models")[:]
51 | if not models:
52 | return
53 | GL = self._GL
54 | # disable lighting
55 | if self.core.get("view_light"):
56 | GL.glDisable(GL.GL_LIGHTING)
57 | # show a wireframe
58 | if self.core.get("view_polygon"):
59 | GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
60 | # change the color
61 | col = self.core.get("color_support_preview")
62 | color = (col["red"], col["green"], col["blue"], col["alpha"])
63 | GL.glColor4f(*color)
64 | # we need to wait until the color change is active
65 | GL.glFinish()
66 | # draw the models
67 | self.core.call_chain("draw_models", models)
68 | # enable lighting again
69 | if self.core.get("view_light"):
70 | GL.glEnable(GL.GL_LIGHTING)
71 | # enable polygon fill mode again
72 | if self.core.get("view_polygon"):
73 | GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
74 |
75 |
--------------------------------------------------------------------------------
/pycam/Plugins/PathPatterns.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | import pycam.Toolpath.MotionGrid
26 |
27 |
28 | class PathPatternSpiral(pycam.Plugins.PluginBase):
29 |
30 | DEPENDS = ["ParameterGroupManager", "PathParamPattern",
31 | "PathParamMillingStyle", "PathParamSpiralDirection",
32 | "PathParamRoundedSpiralCorners"]
33 | CATEGORIES = ["Process", "Path pattern"]
34 |
35 | def setup(self):
36 | parameters = {
37 | "milling_style": pycam.Toolpath.MotionGrid.MILLING_STYLE_IGNORE,
38 | "spiral_direction": None,
39 | "rounded_corners": False,
40 | }
41 | self.core.get("register_parameter_set")("path_pattern", "spiral",
42 | "Spiral", self.get_grid_generator, parameters=parameters,
43 | weight=30)
44 | return True
45 |
46 | def teardown(self):
47 | self.core.get("unregister_parameter_set")("path_pattern", "spiral")
48 |
49 | def get_grid_generator(self, pattern):
50 | kwargs = pattern["parameters"]
51 | func = pycam.Toolpath.MotionGrid.get_spiral
52 | return func, kwargs
53 |
54 |
55 | class PathPatternGrid(pycam.Plugins.PluginBase):
56 |
57 | DEPENDS = ["ParameterGroupManager", "PathParamPattern",
58 | "PathParamMillingStyle", "PathParamGridDirection"]
59 | CATEGORIES = ["Process", "Path pattern"]
60 |
61 | def setup(self):
62 | parameters = {
63 | "milling_style": pycam.Toolpath.MotionGrid.MILLING_STYLE_IGNORE,
64 | "grid_direction": pycam.Toolpath.MotionGrid.GRID_DIRECTION_X,
65 | }
66 | self.core.get("register_parameter_set")("path_pattern", "grid",
67 | "Grid", self.get_grid_generator, parameters=parameters,
68 | weight=10)
69 | return True
70 |
71 | def teardown(self):
72 | self.core.get("unregister_parameter_set")("path_pattern", "grid")
73 |
74 | def get_grid_generator(self, pattern):
75 | kwargs = pattern["parameters"]
76 | func = pycam.Toolpath.MotionGrid.get_fixed_grid
77 | return func, kwargs
78 |
79 |
--------------------------------------------------------------------------------
/pycam/Plugins/PostprocessorEMC2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | from pycam.Exporters.GCodeExporter import GCodeGenerator
26 |
27 | class PostprocessorEMC2(pycam.Plugins.PluginBase):
28 |
29 | DEPENDS = ["ToolpathExport"]
30 | CATEGORIES = ["Postprocessor"]
31 |
32 | def setup(self):
33 | self.core.get("register_postprocessor")("emc2", "EMC2", GCodeGenerator)
34 | return True
35 |
36 | def teardown(self):
37 | self.core.get("unregister_postprocessor")("emc2")
38 |
39 |
--------------------------------------------------------------------------------
/pycam/Plugins/TaskTypes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 |
26 |
27 | class TaskTypeMilling(pycam.Plugins.PluginBase):
28 |
29 | DEPENDS = ["Tasks", "TaskParamCollisionModels", "TaskParamTool",
30 | "TaskParamProcess", "TaskParamBounds"]
31 | CATEGORIES = ["Task"]
32 |
33 | def setup(self):
34 | parameters = {"collision_models": [],
35 | "tool": None,
36 | "process": None,
37 | "bounds": None,
38 | }
39 | self.core.get("register_parameter_set")("task", "milling",
40 | "Milling", self.run_task, parameters=parameters,
41 | weight=10)
42 | return True
43 |
44 | def teardown(self):
45 | self.core.get("unregister_parameter_set")("task", "milling")
46 |
47 | def run_task(self, task, callback=None):
48 | environment = {}
49 | for key in task["parameters"]:
50 | environment[key] = task["parameters"][key]
51 | if environment["tool"] is None:
52 | self.log.error("You need to assign a tool to this task.")
53 | return
54 | if environment["process"] is None:
55 | self.log.error("You need to assign a process to this task.")
56 | return
57 | if environment["bounds"] is None:
58 | self.log.error("You need to assign bounds to this task.")
59 | return
60 | funcs = {}
61 | for key, set_name in (("tool", "shape"), ("process", "strategy")):
62 | funcs[key] = self.core.get("get_parameter_sets")(
63 | key)[environment[key][set_name]]["func"]
64 | tool = funcs["tool"](tool=environment["tool"], environment=environment)
65 | path_generator, motion_grid, (low, high) = funcs["process"](
66 | environment["process"], environment=environment)
67 | if path_generator is None:
68 | # we assume that an error message was given already
69 | return
70 | models = [m.model for m in task["parameters"]["collision_models"]]
71 | if not models:
72 | # issue a warning - and go ahead ...
73 | self.log.warn("No collision model was selected. This can be " + \
74 | "intentional, but maybe you simply forgot it.")
75 | moves = path_generator.GenerateToolPath(tool, models, motion_grid,
76 | minz=low[2], maxz=high[2], draw_callback=callback)
77 | if not moves:
78 | self.log.info("No valid moves found")
79 | return None
80 | data = {}
81 | for item_name in ("tool", "process", "bounds"):
82 | self.core.call_chain("get_toolpath_information",
83 | environment[item_name], data)
84 | tp = pycam.Toolpath.Toolpath(moves, parameters=data)
85 | return tp
86 |
87 |
--------------------------------------------------------------------------------
/pycam/Plugins/ToolTypes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2011 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 |
24 | import pycam.Plugins
25 | import pycam.Cutters.SphericalCutter
26 | import pycam.Cutters.ToroidalCutter
27 | import pycam.Cutters.CylindricalCutter
28 |
29 |
30 | class ToolTypeBallNose(pycam.Plugins.PluginBase):
31 |
32 | DEPENDS = ["Tools", "ToolParamRadius", "ToolParamFeedrate"]
33 | CATEGORIES = ["Tool", "Parameter"]
34 |
35 | def setup(self):
36 | parameters = {"radius": 1.0,
37 | "feedrate": 300,
38 | "spindle_speed": 1000,
39 | }
40 | self.core.get("register_parameter_set")("tool", "ballnose",
41 | "Ball nose", self.get_tool, parameters=parameters,
42 | weight=20)
43 | return True
44 |
45 | def teardown(self):
46 | self.core.get("unregister_parameter_set")("tool", "ballnose")
47 |
48 | def get_tool(self, tool, environment=None):
49 | return pycam.Cutters.SphericalCutter(tool["parameters"]["radius"])
50 |
51 |
52 | class ToolTypeBullNose(pycam.Plugins.PluginBase):
53 |
54 | DEPENDS = ["Tools", "ToolParamRadius", "ToolParamTorusRadius",
55 | "ToolParamFeedrate"]
56 | CATEGORIES = ["Tool", "Parameter"]
57 |
58 | def setup(self):
59 | parameters = {"radius": 1.0,
60 | "torus_radius": 0.25,
61 | "feedrate": 300,
62 | "spindle_speed": 1000,
63 | }
64 | self.core.get("register_parameter_set")("tool", "bullnose",
65 | "Bull nose", self.get_tool, parameters=parameters,
66 | weight=30)
67 | return True
68 |
69 | def teardown(self):
70 | self.core.get("unregister_parameter_set")("tool", "bullnose")
71 |
72 | def get_tool(self, tool, environment=None):
73 | return pycam.Cutters.ToroidalCutter(
74 | tool["parameters"]["radius"],
75 | tool["parameters"]["torus_radius"])
76 |
77 |
78 | class ToolTypeFlat(pycam.Plugins.PluginBase):
79 |
80 | DEPENDS = ["Tools", "ToolParamRadius", "ToolParamFeedrate"]
81 | CATEGORIES = ["Tool", "Parameter"]
82 |
83 | def setup(self):
84 | parameters = {"radius": 1.0,
85 | "feedrate": 300,
86 | "spindle_speed": 1000,
87 | }
88 | self.core.get("register_parameter_set")("tool", "flat", "Flat bottom",
89 | self.get_tool, parameters=parameters, weight=10)
90 | return True
91 |
92 | def teardown(self):
93 | self.core.get("unregister_parameter_set")("tool", "flat")
94 |
95 | def get_tool(self, tool, environment=None):
96 | return pycam.Cutters.CylindricalCutter(tool["parameters"]["radius"])
97 |
98 |
--------------------------------------------------------------------------------
/pycam/Simulation/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 | Copyright 2009 Lode Leroy
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | __all__ = ['ZBuffer', 'ODEBlocks']
25 |
26 |
--------------------------------------------------------------------------------
/pycam/Utils/iterators.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | class Iterator(object):
24 | def __init__(self, seq, start=0):
25 | self.seq = seq
26 | self.ind = start
27 |
28 | def next(self):
29 | if self.ind >= len(self.seq):
30 | return None
31 | else:
32 | item = self.seq[self.ind]
33 | self.ind += 1
34 | return item
35 |
36 | def insertBefore(self, item):
37 | self.seq.insert(self.ind - 1, item)
38 | self.ind += 1
39 |
40 | def insert(self, item):
41 | self.seq.insert(self.ind, item)
42 | self.ind += 1
43 |
44 | def replace(self, item_old, item_new):
45 | for i in range(len(self.seq)):
46 | if self.seq[i] == item_old:
47 | self.seq[i] = item_new
48 |
49 | def remove(self, item):
50 | for i in range(len(self.seq)):
51 | if self.seq[i] == item:
52 | del self.seq[i]
53 | if i < self.ind:
54 | self.ind -= 1
55 | return
56 |
57 | def takeNext(self):
58 | if self.ind >= len(self.seq):
59 | return None
60 | else:
61 | return self.seq.pop(self.ind)
62 |
63 | def copy(self):
64 | return Iterator(self.seq, self.ind)
65 |
66 | def peek(self, i=0):
67 | if self.ind + i >= len(self.seq):
68 | return None
69 | else:
70 | return self.seq[self.ind + i]
71 |
72 | def remains(self):
73 | return len(self.seq) - self.ind
74 |
75 |
76 | class CyclicIterator(object):
77 | def __init__(self, seq, start=0):
78 | self.seq = seq
79 | self.ind = start
80 | self.count = len(seq)
81 |
82 | def next(self):
83 | item = self.seq[self.ind]
84 | self.ind += 1
85 | if self.ind == len(self.seq):
86 | self.ind = 0
87 | return item
88 |
89 | def copy(self):
90 | return CyclicIterator(self.seq, self.ind)
91 |
92 | def peek(self, i=0):
93 | idx = self.ind + i
94 | while idx >= len(self.seq):
95 | idx -= len(self.seq)
96 | return self.seq[idx]
97 |
98 |
99 | if __name__ == "__main__":
100 | l = [1, 2, 4, 6]
101 | print "l=", l
102 | i = Iterator(l)
103 | print i.peek()
104 | while True:
105 | val = i.next()
106 | if val == None:
107 | break
108 | if val == 4:
109 | i.insertBefore(3)
110 | i.insert(5)
111 |
112 | print "l=", l
113 | i = Iterator(l)
114 | print "peek(0)=", i.peek(0)
115 | print "peek(1)=", i.peek(1)
116 | print "i.next()=", i.next()
117 | print "peek(0)=", i.peek(0)
118 | print "peek(1)=", i.peek(1)
119 |
120 | print "remains=", i.remains()
121 |
122 | print "l=", l
123 | sum_value = 0
124 | i = CyclicIterator(l)
125 | print "cycle :",
126 | while sum_value < 30:
127 | val = i.next()
128 | print val,
129 | sum_value += val
130 | print "=", sum_value
131 |
132 | i = Iterator(l)
133 | print "l=", l
134 | i.next()
135 | i.next()
136 | print "next,next : ", i.peek()
137 | i.remove(2)
138 | print "remove(2) : ", i.peek()
139 | i.remove(4)
140 | print "remove(4) : ", i.peek()
141 | print "l=", l
142 |
143 |
--------------------------------------------------------------------------------
/pycam/Utils/rootsolver.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | def find_root_subdivide(f, x0, x1, tolerance, scale):
24 | ymin = 0
25 | xmin = 0
26 | while x1 - x0 > tolerance:
27 | for i in range(scale):
28 | x = x1 + (i / scale) * (x1 - x0)
29 | y = f(x)
30 | abs_y = abs(y)
31 | if i == 0:
32 | ymin = abs_y
33 | xmin = x
34 | else:
35 | if abs_y < ymin:
36 | ymin = abs_y
37 | xmin = x
38 | x0 = xmin - 1 / scale
39 | x1 = xmin + 1 / scale
40 | scale /= 10
41 | return xmin
42 |
43 | def find_root_newton_raphson(f, df, x0, tolerance, maxiter):
44 | x = x0
45 | iter_count = 0
46 | while iter_count < maxiter:
47 | y = f(x)
48 | if y == 0:
49 | return x
50 | dy = df(x)
51 | if dy == 0:
52 | return None
53 | dx = y / dy
54 | x = x - dx
55 | if dx < tolerance:
56 | break
57 | iter_count += 1
58 | return x
59 |
60 | def find_root(f, df=None, x0=0, x1=1, tolerance=0.001):
61 | return find_root_subdivide(f=f, x0=x0, x1=x1, tolerance=tolerance,
62 | scale=10.0)
63 |
64 |
--------------------------------------------------------------------------------
/pycam/Utils/xml_handling.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | $Id$
5 |
6 | Copyright 2010 Lars Kruse
7 |
8 | This file is part of PyCAM.
9 |
10 | PyCAM is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 |
15 | PyCAM is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 |
20 | You should have received a copy of the GNU General Public License
21 | along with PyCAM. If not, see .
22 | """
23 |
24 | import xml.etree.ElementTree as ET
25 |
26 |
27 | def get_xml(item, name=None):
28 | if name is None:
29 | if hasattr(item, "node_key"):
30 | name = item.node_key
31 | else:
32 | name = "value"
33 | if isinstance(item, (list, tuple, set)):
34 | leaf = ET.Element(name)
35 | for single in item:
36 | leaf.append(get_xml(single))
37 | return leaf
38 | elif isinstance(item, dict):
39 | leaf = ET.Element(name)
40 | for key, value in item.iteritems():
41 | leaf.append(get_xml(value, name=key))
42 | return leaf
43 | else:
44 | leaf = ET.Element(name)
45 | leaf.text = str(item)
46 | return leaf
47 |
48 | def parse_xml_dict(item):
49 | pass
50 |
51 | def get_xml_lines(item):
52 | lines = []
53 | content = ET.tostring(item)
54 | content = content.replace("><", ">\n<")
55 | indent = 0
56 | for line in content.split("\n"):
57 | indented = False
58 | if line.startswith(""):
59 | indent -= 2
60 | indented = True
61 | lines.append(" " * indent + line)
62 | if indented:
63 | pass
64 | elif line.endswith("/>"):
65 | pass
66 | elif line.startswith(""):
67 | indent -= 2
68 | elif "" in line:
69 | pass
70 | else:
71 | indent += 2
72 | return lines
73 |
74 |
--------------------------------------------------------------------------------
/pycam/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2008 Lode Leroy
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | __all__ = ["Cutters", "Exporters", "Geometry", "Gui", "Importers",
24 | "PathGenerators", "PathProcessors", "Utils"]
25 |
26 | VERSION = "0.6-svn"
27 |
28 |
--------------------------------------------------------------------------------
/pyinstaller/hooks/hook-pycam.py:
--------------------------------------------------------------------------------
1 | # keysyms does not seem to be recognized by pyinstaller
2 | # There will be exceptions after any keypress without this line.
3 | # rsvg is required for SVG icons (dynamically loaded on demand)
4 | hiddenimports = ["gtk.keysyms", "rsvg"]
5 |
6 |
--------------------------------------------------------------------------------
/pyinstaller/pyinstaller_fix_module_exception.patch:
--------------------------------------------------------------------------------
1 | diff -ruN pyinstaller-1.4/iu.py pyinstaller-1.4.fixed//iu.py
2 | --- pyinstaller-1.4/iu.py 2010-02-11 01:23:39.000000000 +0100
3 | +++ pyinstaller-1.4.fixed//iu.py 2010-08-23 19:07:50.000000000 +0200
4 | # see http://www.pyinstaller.org/ticket/205
5 | # Exceptions are thrown for imported modules are not available in Windows.
6 | @@ -451,7 +451,8 @@
7 | if ctx and hasattr(sys.modules[ctx], nmparts[i]):
8 | debug("importHook done with %s %s %s (case 1)" % (name, __globals_name, fromlist))
9 | return sys.modules[nmparts[0]]
10 | - del sys.modules[fqname]
11 | + if fqname in sys.modules:
12 | + del sys.modules[fqname]
13 | raise ImportError, "No module named %s" % fqname
14 | if fromlist is None:
15 | debug("importHook done with %s %s %s (case 2)" % (name, __globals_name, fromlist))
16 |
--------------------------------------------------------------------------------
/pyinstaller/pyinstaller_info.txt:
--------------------------------------------------------------------------------
1 | PyInstaller (http://pyinstaller.org) can be used to create standalone binaries for Windows.
2 |
3 | How to build a standalone exe file (on Windows only):
4 | 1) install the PyCAM dependency installer:
5 | https://pycam.svn.sourceforge.net/svnroot/pycam/dependency_installer
6 | * add "C:\GtkGLext\1.0\bin" to your PATH environment variable
7 | 2) install UPX (compression)
8 | * Debian/Ubuntu: apt-get install upx-ucl
9 | * Windows: http://upx.sourceforge.net
10 | * extract the archive to your program directory
11 | * add this directory to your PATH environment variable
12 | 3) download pyinstaller (svn co http://svn.pyinstaller.org/tags/1.5 pyinstaller)
13 | 4) run "cmd.exe" (or open a terminal)
14 | 5) "cd PATH_TO_PYCAM"
15 | 6) "python PYINSTALLER_PATH/Configure.py"
16 | 7) "python PYINSTALLER_PATH/Build.py pyinstaller/pycam.spec"
17 | 8) test and upload the binary file "pycam-VERSION_standalone.?"
18 |
19 | Known issues:
20 | * multiprocessing on Windows: no server/client capabilities
21 | * python-setproctitle v1.0.1 causes a segfault - remove it from the build system
22 | * Linux: pre-built executable don't seem to work across different versions of libc
23 | * Linux/MacOS?: you need to remove the reference to "windll" at the top of the "pycam" script (line 32-36)
24 |
25 | Debugging:
26 | * enable the "debug" parameter in the "EXE" call at the end of the spec file
27 |
28 |
--------------------------------------------------------------------------------
/pylint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BASE_PATH="$(dirname "$0")"
4 |
5 | set -eu
6 |
7 | IGNORE_LIST="C0111,C0103,W0613,R0914,R0911,R0912,W0511,W0603,,R0902,W0612,R0903,R0201,R0915,R0913,W0602"
8 |
9 | PYTHONPATH="$BASE_PATH/src" pylint -i y -d "$IGNORE_LIST" "$1"
10 |
11 |
--------------------------------------------------------------------------------
/release_info.txt:
--------------------------------------------------------------------------------
1 | 1) update the version and the changelog
2 | - in "Changelog" (version, release date, changes)
3 | - in "src/pycam/__init__.py" (version)
4 | - commit the changes
5 |
6 | 2) create the archives
7 | - "make dist"
8 | - carefully check the resulting content of the archives
9 |
10 | 3a) create an svn tag for the release (includes uploading the archive files)
11 | - "make upload"
12 |
13 | 3b) create the Windows standalone binary
14 | - see pyinstaller/pyinstall_info.txt for details
15 |
16 | 4) upload files to sourceforge
17 | - https://sourceforge.net/project/admin/explorer.php?group_id=237831
18 | - create a directory for the new release
19 | - click at the icon to the left of the new directory and upload the new archives
20 | - create a file called "release-notes-0.x" and upload it to the same directory
21 | - first line: "PyCAM v0.x release notes:"
22 | - second line: empty
23 | - further lines: summary of changes (complete sentences)
24 | - mark the release notes files as "Release notes" (see "Properties")
25 | - set the release notes and the target operating systems for the archives
26 | - zip: others
27 | - exe: Windows
28 | - tar.gz: Linux, Mac, BSD, Solaris
29 | - standalone binary (Windows): no specific architecture
30 |
31 | 5) announcements
32 | - run "python setup.py register" (for the PyPI package index)
33 | - create a project news items at sourceforge
34 | - create a new release at http://freshmeat.net
35 | - post the new release at http://www.cnczone.com/forums/showthread.php?t=63716
36 | - create a blog post at: http://fab.senselab.org
37 |
38 | 6) other stuff
39 | - create a new release tag for the bug tracker:
40 | https://sourceforge.net/tracker/admin/index.php?group_id=237831&atid=1104176&add_group=1
41 |
42 |
--------------------------------------------------------------------------------
/samples/Box0.stl:
--------------------------------------------------------------------------------
1 | solid box0
2 | facet normal -0.000000 0.000000 1.000000
3 | outer loop
4 | vertex -4.961088 -4.824490 4.188777
5 | vertex -0.772311 -0.635713 4.188777
6 | vertex -4.961088 -0.635713 4.188777
7 | endloop
8 | endfacet
9 | facet normal -0.000000 0.000000 1.000000
10 | outer loop
11 | vertex -4.961088 -4.824490 4.188777
12 | vertex -0.772311 -4.824490 4.188777
13 | vertex -0.772311 -0.635713 4.188777
14 | endloop
15 | endfacet
16 | facet normal 0.000000 0.000000 -1.000000
17 | outer loop
18 | vertex -0.772311 -4.824490 0.000000
19 | vertex -4.961088 -0.635713 0.000000
20 | vertex -0.772311 -0.635713 0.000000
21 | endloop
22 | endfacet
23 | facet normal 0.000000 0.000000 -1.000000
24 | outer loop
25 | vertex -0.772311 -4.824490 0.000000
26 | vertex -4.961088 -4.824490 0.000000
27 | vertex -4.961088 -0.635713 0.000000
28 | endloop
29 | endfacet
30 | facet normal 1.000000 -0.000000 0.000000
31 | outer loop
32 | vertex -0.772311 -4.824490 4.188777
33 | vertex -0.772311 -0.635713 0.000000
34 | vertex -0.772311 -0.635713 4.188777
35 | endloop
36 | endfacet
37 | facet normal 1.000000 -0.000000 0.000000
38 | outer loop
39 | vertex -0.772311 -4.824490 4.188777
40 | vertex -0.772311 -4.824490 0.000000
41 | vertex -0.772311 -0.635713 0.000000
42 | endloop
43 | endfacet
44 | facet normal -1.000000 0.000000 0.000000
45 | outer loop
46 | vertex -4.961088 -4.824490 0.000000
47 | vertex -4.961088 -0.635713 4.188777
48 | vertex -4.961088 -0.635713 0.000000
49 | endloop
50 | endfacet
51 | facet normal -1.000000 0.000000 0.000000
52 | outer loop
53 | vertex -4.961088 -4.824490 0.000000
54 | vertex -4.961088 -4.824490 4.188777
55 | vertex -4.961088 -0.635713 4.188777
56 | endloop
57 | endfacet
58 | facet normal 0.000000 1.000000 0.000000
59 | outer loop
60 | vertex -4.961088 -0.635713 4.188777
61 | vertex -0.772311 -0.635713 0.000000
62 | vertex -4.961088 -0.635713 0.000000
63 | endloop
64 | endfacet
65 | facet normal 0.000000 1.000000 0.000000
66 | outer loop
67 | vertex -4.961088 -0.635713 4.188777
68 | vertex -0.772311 -0.635713 4.188777
69 | vertex -0.772311 -0.635713 0.000000
70 | endloop
71 | endfacet
72 | facet normal 0.000000 -1.000000 0.000000
73 | outer loop
74 | vertex -4.961088 -4.824490 0.000000
75 | vertex -0.772311 -4.824490 4.188777
76 | vertex -4.961088 -4.824490 4.188777
77 | endloop
78 | endfacet
79 | facet normal 0.000000 -1.000000 0.000000
80 | outer loop
81 | vertex -4.961088 -4.824490 0.000000
82 | vertex -0.772311 -4.824490 0.000000
83 | vertex -0.772311 -4.824490 4.188777
84 | endloop
85 | endfacet
86 | endsolid box0
87 |
--------------------------------------------------------------------------------
/samples/Box1.stl:
--------------------------------------------------------------------------------
1 | solid box1
2 | facet normal 0.707143 -0.408041 0.577452
3 | outer loop
4 | vertex 2.563112 -5.599983 2.267282
5 | vertex 5.339321 -3.997340 0.000022
6 | vertex 2.562797 -2.393577 4.533387
7 | endloop
8 | endfacet
9 | facet normal 0.707143 -0.408041 0.577452
10 | outer loop
11 | vertex 5.339321 -3.997340 0.000022
12 | vertex 5.339006 -0.790934 2.266127
13 | vertex 2.562797 -2.393577 4.533387
14 | endloop
15 | endfacet
16 | facet normal -0.707143 0.408041 -0.577452
17 | outer loop
18 | vertex -0.213384 -3.997869 0.000000
19 | vertex -0.213699 -0.791464 2.266105
20 | vertex 2.562824 -2.395226 -2.267260
21 | endloop
22 | endfacet
23 | facet normal -0.707143 0.408041 -0.577452
24 | outer loop
25 | vertex -0.213699 -0.791464 2.266105
26 | vertex 2.562510 0.811179 -0.001155
27 | vertex 2.562824 -2.395226 -2.267260
28 | endloop
29 | endfacet
30 | facet normal 0.707070 0.408176 -0.577446
31 | outer loop
32 | vertex 5.339006 -0.790934 2.266127
33 | vertex 2.562824 -2.395226 -2.267260
34 | vertex 2.562510 0.811179 -0.001155
35 | endloop
36 | endfacet
37 | facet normal 0.707070 0.408176 -0.577446
38 | outer loop
39 | vertex 5.339006 -0.790934 2.266127
40 | vertex 5.339321 -3.997340 0.000022
41 | vertex 2.562824 -2.395226 -2.267260
42 | endloop
43 | endfacet
44 | facet normal -0.707070 -0.408176 0.577446
45 | outer loop
46 | vertex -0.213384 -3.997869 0.000000
47 | vertex 2.562797 -2.393577 4.533387
48 | vertex -0.213699 -0.791464 2.266105
49 | endloop
50 | endfacet
51 | facet normal -0.707070 -0.408176 0.577446
52 | outer loop
53 | vertex -0.213384 -3.997869 0.000000
54 | vertex 2.563112 -5.599983 2.267282
55 | vertex 2.562797 -2.393577 4.533387
56 | endloop
57 | endfacet
58 | facet normal -0.000080 0.816637 0.577152
59 | outer loop
60 | vertex -0.213699 -0.791464 2.266105
61 | vertex 5.339006 -0.790934 2.266127
62 | vertex 2.562510 0.811179 -0.001155
63 | endloop
64 | endfacet
65 | facet normal -0.000080 0.816637 0.577152
66 | outer loop
67 | vertex -0.213699 -0.791464 2.266105
68 | vertex 2.562797 -2.393577 4.533387
69 | vertex 5.339006 -0.790934 2.266127
70 | endloop
71 | endfacet
72 | facet normal 0.000080 -0.816637 -0.577152
73 | outer loop
74 | vertex -0.213384 -3.997869 0.000000
75 | vertex 5.339321 -3.997340 0.000022
76 | vertex 2.563112 -5.599983 2.267282
77 | endloop
78 | endfacet
79 | facet normal 0.000080 -0.816637 -0.577152
80 | outer loop
81 | vertex -0.213384 -3.997869 0.000000
82 | vertex 2.562824 -2.395226 -2.267260
83 | vertex 5.339321 -3.997340 0.000022
84 | endloop
85 | endfacet
86 | endsolid box1
87 |
--------------------------------------------------------------------------------
/samples/Box2.stl:
--------------------------------------------------------------------------------
1 | solid "model"; Produced by pycam (v0.3), 2010-10-03
2 | facet normal 0.707143 -0.408041 0.577452
3 | outer loop
4 | vertex 5.339321 -3.997340 0.000022
5 | vertex 2.562797 -2.393577 4.533387
6 | vertex 2.563112 -5.599983 2.267282
7 | endloop
8 | endfacet
9 | facet normal 0.707143 -0.408041 0.577452
10 | outer loop
11 | vertex 5.339006 -0.790934 2.266127
12 | vertex 2.562797 -2.393577 4.533387
13 | vertex 5.339321 -3.997340 0.000022
14 | endloop
15 | endfacet
16 | facet normal -0.707143 0.408041 -0.577452
17 | outer loop
18 | vertex -0.213699 -0.791464 2.266105
19 | vertex 2.562824 -2.395226 -2.267260
20 | vertex -0.213384 -3.997869 0.000000
21 | endloop
22 | endfacet
23 | facet normal -0.707143 0.408041 -0.577452
24 | outer loop
25 | vertex 2.562510 0.811179 -0.001155
26 | vertex 2.562824 -2.395226 -2.267260
27 | vertex -0.213699 -0.791464 2.266105
28 | endloop
29 | endfacet
30 | facet normal 0.707070 0.408176 -0.577446
31 | outer loop
32 | vertex 2.562824 -2.395226 -2.267260
33 | vertex 2.562510 0.811179 -0.001155
34 | vertex 5.339006 -0.790934 2.266127
35 | endloop
36 | endfacet
37 | facet normal 0.707070 0.408176 -0.577446
38 | outer loop
39 | vertex 5.339321 -3.997340 0.000022
40 | vertex 2.562824 -2.395226 -2.267260
41 | vertex 5.339006 -0.790934 2.266127
42 | endloop
43 | endfacet
44 | facet normal -0.707070 -0.408176 0.577446
45 | outer loop
46 | vertex 2.562797 -2.393577 4.533387
47 | vertex -0.213699 -0.791464 2.266105
48 | vertex -0.213384 -3.997869 0.000000
49 | endloop
50 | endfacet
51 | facet normal -0.707070 -0.408176 0.577446
52 | outer loop
53 | vertex 2.563112 -5.599983 2.267282
54 | vertex 2.562797 -2.393577 4.533387
55 | vertex -0.213384 -3.997869 0.000000
56 | endloop
57 | endfacet
58 | facet normal -0.000080 0.816637 0.577152
59 | outer loop
60 | vertex 5.339006 -0.790934 2.266127
61 | vertex 2.562510 0.811179 -0.001155
62 | vertex -0.213699 -0.791464 2.266105
63 | endloop
64 | endfacet
65 | facet normal -0.000080 0.816637 0.577152
66 | outer loop
67 | vertex 2.562797 -2.393577 4.533387
68 | vertex 5.339006 -0.790934 2.266127
69 | vertex -0.213699 -0.791464 2.266105
70 | endloop
71 | endfacet
72 | facet normal 0.000080 -0.816637 -0.577152
73 | outer loop
74 | vertex 5.339321 -3.997340 0.000022
75 | vertex 2.563112 -5.599983 2.267282
76 | vertex -0.213384 -3.997869 0.000000
77 | endloop
78 | endfacet
79 | facet normal 0.000080 -0.816637 -0.577152
80 | outer loop
81 | vertex 2.562824 -2.395226 -2.267260
82 | vertex 5.339321 -3.997340 0.000022
83 | vertex -0.213384 -3.997869 0.000000
84 | endloop
85 | endfacet
86 | endsolid
87 |
--------------------------------------------------------------------------------
/samples/SampleScene2.scad:
--------------------------------------------------------------------------------
1 | // example scene for pycam
2 |
3 | module scene() {
4 | sphere(r=15, center=true);
5 | translate(v=[0,80,5]) cylinder(h = 10, r1 = 20, r2 = 10, center = true);
6 | translate(v=[0,40,15]) {
7 | intersection() {
8 | translate([0,0,-15]) rotate([60,0,90]) cube([30, 20, 20], center=true);
9 | cylinder(h = 30, r = 10, center = true);
10 | }
11 | }
12 | translate([0,130,-5]) {
13 | intersection() {
14 | sphere(20, center=true);
15 | rotate([30,60,0]) cube(30, center=true);
16 | }
17 | }
18 | }
19 |
20 | // remove the parts of the objects below the zero plane
21 | difference() {
22 | scale([0.5, 0.5, 0.3]) scene();
23 | translate([0,0,-20]) cube([200,200,40], center=true);
24 | }
--------------------------------------------------------------------------------
/samples/SampleScene3.scad:
--------------------------------------------------------------------------------
1 | difference() {
2 | sphere(r=30);
3 | translate([-50, -50, -100]) cube([100, 100, 100]);
4 | }
5 |
6 | translate([-30, 50, 0]) union() {
7 | cube([140, 30, 10]);
8 | translate([40, 15, 10]) rotate([0, 90, 0]) cylinder(r=10, h=60);
9 | }
10 |
11 | translate([80, 0, 0]) scale(1.2) union() {
12 | difference() {
13 | cylinder(r1=20, r2=5, h=20);
14 | translate([-25, 30, -10]) rotate([60, 0, 0]) cube([50, 50, 50]);
15 | }
16 | difference() {
17 | translate([0, 35, -21]) rotate([70, 0, 0]) cylinder(r=16, h=50);
18 | translate([-50, -50, -100]) cube([100, 100, 100]);
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/Sphere_cut.scad:
--------------------------------------------------------------------------------
1 | intersection() {
2 | sphere(r=5, center=true, $fn=6);
3 | cube([20,20,5], center=true);
4 | }
--------------------------------------------------------------------------------
/samples/multilayer_engrave.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
88 |
--------------------------------------------------------------------------------
/samples/polygon2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
69 |
--------------------------------------------------------------------------------
/samples/polygon3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
69 |
--------------------------------------------------------------------------------
/samples/polygon4.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
73 |
--------------------------------------------------------------------------------
/samples/problem.conf:
--------------------------------------------------------------------------------
1 | [ProcessDefault]
2 | engrave_offset: 0.0
3 | pocketing_type: none
4 |
5 | [Process0]
6 | name: Remove material y
7 | path_strategy: PushRemoveStrategy
8 | path_direction: y
9 | milling_style: ignore
10 | material_allowance: 0.01
11 | overlap_percent: 25
12 | step_down: 0.12
13 |
14 | [Process1]
15 | name: Carve contour
16 | path_strategy: ContourPolygonStrategy
17 | path_direction: x
18 | milling_style: ignore
19 | material_allowance: 0.01
20 | overlap_percent: 40
21 | step_down: 1.0
22 |
23 | [Process2]
24 | name: Cleanup x
25 | path_strategy: SurfaceStrategy
26 | path_direction: x
27 | milling_style: ignore
28 | material_allowance: 0.0
29 | overlap_percent: 75
30 | step_down: 0.11811023622
31 |
32 | [Process3]
33 | name: cleanup y
34 | path_strategy: SurfaceStrategy
35 | path_direction: y
36 | milling_style: ignore
37 | material_allowance: 0.0
38 | overlap_percent: 75
39 | step_down: 0.11811023622
40 |
41 | [Process4]
42 | name: Gravure
43 | path_strategy: EngraveStrategy
44 | path_direction: x
45 | milling_style: conventional
46 | material_allowance: 0.0
47 | overlap_percent: 50
48 | step_down: 0.0393700787402
49 |
50 | [Tool0]
51 | name: Cylindrical
52 | shape: CylindricalCutter
53 | tool_radius: 0.0625
54 | torus_radius: 0.25
55 | feedrate: 60.0
56 | speed: 10000.0
57 |
58 | [Tool1]
59 | name: Toroidal
60 | shape: ToroidalCutter
61 | tool_radius: 0.062
62 | torus_radius: 0.02
63 | feedrate: 30.0
64 | speed: 1000.0
65 |
66 | [Tool2]
67 | name: Spherical
68 | shape: SphericalCutter
69 | tool_radius: 0.0625
70 | torus_radius: 0.00984251968504
71 | feedrate: 30.0
72 | speed: 1000.0
73 |
74 | [TaskDefault]
75 | tool: 0
76 |
77 | [Task0]
78 | name: Rough top
79 | process: 0
80 | bounds: 0
81 | enabled: 0
82 |
83 | [Task1]
84 | name: rough2 bottom
85 | process: 0
86 | bounds: 0
87 | enabled: 0
88 |
89 | [Task2]
90 | name: finish x top
91 | process: 2
92 | bounds: 0
93 | enabled: 0
94 |
95 | [Task3]
96 | name: finish x middle
97 | process: 2
98 | bounds: 0
99 | enabled: 0
100 |
101 | [Task4]
102 | name: finish x bottom
103 | process: 2
104 | bounds: 2
105 | enabled: 0
106 |
107 | [Task5]
108 | name: finish y top
109 | process: 3
110 | bounds: 0
111 | enabled: 0
112 |
113 | [Task6]
114 | name: finish y middle
115 | process: 3
116 | bounds: 0
117 | enabled: 0
118 |
119 | [Task7]
120 | name: finish y bottom
121 | process: 3
122 | bounds: 2
123 | enabled: 1
124 |
125 | [BoundsDefault]
126 | type: relative_margin
127 | z_high: 0.0
128 |
129 | [Bounds0]
130 | name: Minimum
131 | x_low: 0.0
132 | x_high: 0.0
133 | y_low: 0.0
134 | y_high: 0.0
135 | z_low: 0.0
136 |
137 | [Bounds1]
138 | name: 10% margin
139 | x_low: 0.1
140 | x_high: 0.1
141 | y_low: 0.1
142 | y_high: 0.1
143 | z_low: 0.05
144 |
145 | [Bounds2]
146 | name: problem
147 | x_low: -0.13
148 | x_high: -0.83
149 | y_low: -0.28
150 | y_high: -0.68
151 | z_low: 0.05
152 |
--------------------------------------------------------------------------------
/samples/problem_1_triangle.stl:
--------------------------------------------------------------------------------
1 | solid vcg
2 | facet normal -2.847750e-02 1.220272e-01 -9.921181e-01
3 | outer loop
4 | vertex 8.845570e-01 1.323334e+00 8.942510e-01
5 | vertex 8.742290e-01 1.593305e+00 9.277530e-01
6 | vertex 1.152810e+00 1.435092e+00 9.002970e-01
7 | endloop
8 | endfacet
9 | endsolid vcg
10 |
--------------------------------------------------------------------------------
/samples/pycam-textbox.scad:
--------------------------------------------------------------------------------
1 | // Combine the PyCAM logo with a slightly depressed rounded-cornered box.
2 |
3 | module block(a, b, height, radius) {
4 | translate([radius, radius, 0]) union() {
5 | translate([-radius, 0, 0]) cube([a, b - 2 * radius, height]);
6 | translate([0, -radius, 0]) cube([a - 2 * radius, b, height]);
7 | cylinder(r=radius, h=height);
8 | translate([a - 2 * radius, 0, 0]) cylinder(r=radius, h=height);
9 | translate([a - 2 * radius, b - 2 * radius, 0]) cylinder(r=radius, h=height);
10 | translate([0, b - 2 * radius, 0]) cylinder(r=radius, h=height);
11 | }
12 | }
13 |
14 | translate([0, 0, -10]) difference() {
15 | block(130, 50, 10, 10);
16 | translate([5, 5, 5]) block(120, 40, 6, 10);
17 | }
18 | translate([15, 17, -5.05]) linear_extrude(file="pycam-text.dxf", height=3);
19 |
20 |
--------------------------------------------------------------------------------
/samples/rectangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/scripts/profile_pycam.py:
--------------------------------------------------------------------------------
1 | import cProfile
2 | from os.path import join
3 | import pstats
4 | import sys
5 | from time import time
6 |
7 | from pycam.Cutters.CylindricalCutter import CylindricalCutter
8 | from pycam.Gui.Console import ConsoleProgressBar
9 | from pycam.Importers.STLImporter import ImportModel
10 | from pycam.PathGenerators.DropCutter import DropCutter
11 | from pycam.PathProcessors.PathAccumulator import PathAccumulator
12 | from pycam.Toolpath import Bounds
13 | from pycam.Toolpath.MotionGrid import get_fixed_grid
14 | from pycam.Utils.locations import get_data_file_location
15 |
16 | # Disable multi processing
17 | from pycam.Utils import threading
18 | threading.__multiprocessing = False
19 |
20 | """ Profile PyCAM doing several operations, print out the top 10
21 | (sorted by actual local runtime) methods.
22 | """
23 |
24 | model = ImportModel(get_data_file_location(join('samples', 'pycam-textbox.stl')))
25 | print model.minx, model.miny, model.maxx, model.maxy
26 |
27 |
28 | def run_dropcutter():
29 | """ Run DropCutter on standard PyCAM sample plaque """
30 | progress_bar = ConsoleProgressBar(sys.stdout)
31 |
32 | overlap = .6
33 | layer_distance = 1
34 | tool = CylindricalCutter(10)
35 | path_generator = DropCutter(PathAccumulator())
36 | bounds = Bounds(Bounds.TYPE_CUSTOM,
37 | (model.minx-5, model.miny-5, model.minz),
38 | (model.maxx+5, model.maxy+5, model.maxz))
39 |
40 |
41 | low, high = bounds.get_absolute_limits()
42 | line_distance = 2 * tool.radius * (1.0 - overlap)
43 |
44 | motion_grid = get_fixed_grid((low, high), layer_distance,
45 | line_distance, tool.radius / 4.0)
46 | moves = path_generator.GenerateToolPath(tool, [model], motion_grid,
47 | minz=low[2], maxz=high[2],
48 | draw_callback=progress_bar.update)
49 |
50 |
51 |
52 | if __name__ == '__main__':
53 | start_time = time()
54 | cProfile.run('run_dropcutter()', 'dropcutter.pyprof')
55 | run_time = time() - start_time
56 | print '\nDropcutter took %f seconds' % run_time
57 | p = pstats.Stats('dropcutter.pyprof')
58 | print 'Top ten time-consuming functions:'
59 | p.sort_stats('time').print_stats(10)
60 |
--------------------------------------------------------------------------------
/scripts/pycam_win32_postinstall.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | $Id$
4 |
5 | Copyright 2010 Lars Kruse
6 |
7 | This file is part of PyCAM.
8 |
9 | PyCAM is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | PyCAM is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with PyCAM. If not, see .
21 | """
22 |
23 | import distutils.sysconfig
24 | import os
25 | import sys
26 |
27 | try:
28 | logfile = os.path.join(distutils.sysconfig.PREFIX, "pycam-wininst-postinstall.log", "a")
29 | except OSError:
30 | logfile = None
31 | if logfile:
32 | sys.stdout = logfile
33 | sys.stderr = logfile
34 |
35 | LINK_EXTENSION = ".lnk"
36 |
37 | try:
38 | START_MENU_BASEDIR = get_special_folder_path("CSIDL_COMMON_PROGRAMS")
39 | except OSError:
40 | START_MENU_BASEDIR = get_special_folder_path("CSIDL_PROGRAMS")
41 | except NameError:
42 | START_MENU_BASEDIR = "C:\\"
43 | START_MENU_SUBDIR = os.path.join(START_MENU_BASEDIR, "PyCAM")
44 |
45 | # create a start menu item for pycam
46 | PYTHON_EXE = os.path.join(distutils.sysconfig.EXEC_PREFIX, "pythonw.exe")
47 | # surround the start script with quotes to avoid space-issues
48 | START_SCRIPT = '"%s"' % os.path.join(distutils.sysconfig.EXEC_PREFIX, "Scripts", "pycam-loader.py")
49 |
50 | SHARE_DIR = os.path.join(distutils.sysconfig.PREFIX, "share", "pycam")
51 |
52 | PYTHON_DOC_DIR = os.path.join(SHARE_DIR, "doc")
53 |
54 | ICON_FILE = os.path.join(SHARE_DIR, "pycam.ico")
55 |
56 | # add some more doc files
57 | DOC_FILES = [("LICENSE.TXT", "License"),]
58 | WEB_LINKS = [
59 | (r"http://pycam.sourceforge.net/", "Project's Website"),
60 | (r"http://sourceforge.net/tracker/?group_id=237831&atid=1104176", "Report a Bug"),
61 | (r"http://sourceforge.net/projects/pycam/forums", "Forum Discussions"),
62 | (r"http://sourceforge.net/apps/mediawiki/pycam/index.php?title=User_Manual", "User Manual")]
63 |
64 | MENU_ITEMS = map(lambda v: (os.path.join(PYTHON_DOC_DIR, v[0]), v[1]), DOC_FILES)
65 | MENU_ITEMS.extend(WEB_LINKS)
66 |
67 | action = sys.argv[1]
68 |
69 | if action == "-install":
70 | if not os.path.exists(START_MENU_SUBDIR):
71 | os.mkdir(START_MENU_SUBDIR)
72 | directory_created(START_MENU_SUBDIR)
73 | for menu_item in MENU_ITEMS:
74 | target, description = menu_item
75 | filename = os.path.join(START_MENU_SUBDIR, description) + LINK_EXTENSION
76 | create_shortcut(target, description, filename)
77 | file_created(filename)
78 | filename = os.path.join(START_MENU_SUBDIR, "Run PyCAM") + LINK_EXTENSION
79 | create_shortcut(PYTHON_EXE, "Run PyCAM", filename, START_SCRIPT, "", ICON_FILE)
80 | file_created(filename)
81 | elif action == "-remove":
82 | pass
83 | else:
84 | pass
85 |
86 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wininst]
2 | install_script = pycam_win32_postinstall.py
3 | bitmap = share/ui/logo_gui_vertical.bmp
4 |
5 | [bdist_msi]
6 | install_script = pycam_win32_postinstall.py
7 |
8 | [bdist_rpm]
9 | packager = Lars Kruse
10 | doc_files = Changelog
11 | README.TXT
12 | INSTALL.TXT
13 | LICENSE.TXT
14 | COPYING.TXT
15 | doc/
16 | examples/
17 |
18 |
--------------------------------------------------------------------------------
/share/desktop/pycam.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Version=1.0
3 | Name=PyCAM
4 | GenericName=Toolpath Generator
5 | Comment=generate GCode for 3-Axis CNC machining
6 | Exec=pycam %u
7 | TryExec=pycam
8 | Terminal=false
9 | Type=Application
10 | Categories=Development;Engineering;Robotics;Education;Science;2DGraphics;VectorGraphics;3DGraphics;
11 | MimeType=application/sla;image/svg+xml;application/postscript;image/vnd.dxf;
12 | Icon=pycam
13 |
14 |
--------------------------------------------------------------------------------
/share/fonts/README:
--------------------------------------------------------------------------------
1 | The fonts delivered with PyCAM are taken from QCAD:
2 | http://www.qcad.org/
3 |
4 | The fonts are licensed under the GPL v2.0 or later.
5 |
6 | Thanks to the QCAD developers for this great piece of work!
7 |
8 |
--------------------------------------------------------------------------------
/share/fonts/courier.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/courier.cxf
--------------------------------------------------------------------------------
/share/fonts/cursive.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/cursive.cxf
--------------------------------------------------------------------------------
/share/fonts/iso8859-11.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/iso8859-11.cxf
--------------------------------------------------------------------------------
/share/fonts/italicc.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/italicc.cxf
--------------------------------------------------------------------------------
/share/fonts/italiccs.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/italiccs.cxf
--------------------------------------------------------------------------------
/share/fonts/italict.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/italict.cxf
--------------------------------------------------------------------------------
/share/fonts/normallatin1.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/normallatin1.cxf
--------------------------------------------------------------------------------
/share/fonts/normallatin1.readme:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/normallatin1.readme
--------------------------------------------------------------------------------
/share/fonts/normallatin2.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/normallatin2.cxf
--------------------------------------------------------------------------------
/share/fonts/romanc.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romanc.cxf
--------------------------------------------------------------------------------
/share/fonts/romancs.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romancs.cxf
--------------------------------------------------------------------------------
/share/fonts/romand.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romand.cxf
--------------------------------------------------------------------------------
/share/fonts/romanp.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romanp.cxf
--------------------------------------------------------------------------------
/share/fonts/romans.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romans.cxf
--------------------------------------------------------------------------------
/share/fonts/romans2.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romans2.cxf
--------------------------------------------------------------------------------
/share/fonts/romant.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/romant.cxf
--------------------------------------------------------------------------------
/share/fonts/scriptc.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/scriptc.cxf
--------------------------------------------------------------------------------
/share/fonts/scripts.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/scripts.cxf
--------------------------------------------------------------------------------
/share/fonts/standard.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/standard.cxf
--------------------------------------------------------------------------------
/share/fonts/symbol.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/symbol.cxf
--------------------------------------------------------------------------------
/share/fonts/unicode.cxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/fonts/unicode.cxf
--------------------------------------------------------------------------------
/share/mime/icons/128x128/application-sla.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/mime/icons/128x128/application-sla.png
--------------------------------------------------------------------------------
/share/mime/icons/32x32/application-sla.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/mime/icons/32x32/application-sla.png
--------------------------------------------------------------------------------
/share/mime/icons/64x64/application-sla.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/mime/icons/64x64/application-sla.png
--------------------------------------------------------------------------------
/share/mime/pycam.mime:
--------------------------------------------------------------------------------
1 | application/sla; pycam '%s'; description="Stereo Lithographic File"; test=test -n "$DISPLAY"; nametemplate=%s.stl
2 | image/vnd.dxf; pycam '%s'; description="Drawing Interchange File"; test=test -n "$DISPLAY"; nametemplate=%s.dxf
3 | image/svg+xml; pycam '%s'; description="Vector Graphics"; test=test -n "$DISPLAY" && which inkscape && which pstoedit; nametemplate=%s.svg
4 | application/postscript; pycam '%s'; description="Postscript"; test=test -n "$DISPLAY" && which pstoedit
5 |
--------------------------------------------------------------------------------
/share/mime/pycam.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Stereo Lithographic Files (solid 3D models)
5 |
6 |
7 |
8 |
9 |
10 |
11 | Drawing Interchange Files (2D or 3D models)
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/share/pycam.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/pycam.ico
--------------------------------------------------------------------------------
/share/ui/clipboard.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/share/ui/emc_tool_export.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/share/ui/extrusion_chamfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/extrusion_chamfer.png
--------------------------------------------------------------------------------
/share/ui/extrusion_radius_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/extrusion_radius_down.png
--------------------------------------------------------------------------------
/share/ui/extrusion_radius_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/extrusion_radius_up.png
--------------------------------------------------------------------------------
/share/ui/extrusion_sigmoidal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/extrusion_sigmoidal.png
--------------------------------------------------------------------------------
/share/ui/extrusion_sine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/extrusion_sine.png
--------------------------------------------------------------------------------
/share/ui/gcode_preferences.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
55 |
56 | True
57 |
58 |
59 | gtk-preferences
60 | True
61 | True
62 | True
63 | True
64 |
65 |
66 | False
67 | False
68 | 0
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/share/ui/gtkrc_windows:
--------------------------------------------------------------------------------
1 | gtk-theme-name = "MS-Windows"
2 |
3 | gtk-icon-sizes = "gtk-menu=13,13:gtk-small-toolbar=16,16:gtk-large-toolbar=24,24:gtk-dnd=32,32"
4 | gtk-toolbar-icon-size = small-toolbar
5 |
6 | # disable images in buttons. i've only seen ugly delphi apps use this feature.
7 | gtk-button-images = 0
8 |
9 | # enable/disable images in menus. most "stock" microsoft apps don't use these, except sparingly.
10 | # the office apps use them heavily, though.
11 | gtk-menu-images = 1
12 |
13 | # use the win32 button ordering instead of the GNOME HIG one, where applicable
14 | gtk-alternative-button-order = 1
15 |
16 | # use the win32 sort indicators direction, as in Explorer
17 | gtk-alternative-sort-arrows = 1
18 |
19 | # Windows users don't expect the PC Speaker beeping at them when they backspace in an empty textview and stuff like that
20 | gtk-error-bell = 0
21 |
22 | style "msw-default"
23 | {
24 | GtkWidget::interior-focus = 1
25 | GtkOptionMenu::indicator-size = { 9, 5 }
26 | GtkOptionMenu::indicator-spacing = { 7, 5, 2, 2 }
27 | GtkSpinButton::shadow-type = in
28 |
29 | # Owen and I disagree that these should be themable
30 | #GtkUIManager::add-tearoffs = 0
31 | #GtkComboBox::add-tearoffs = 0
32 |
33 | GtkComboBox::appears-as-list = 1
34 | GtkComboBox::focus-on-click = 0
35 |
36 | GOComboBox::add_tearoffs = 0
37 |
38 | GtkTreeView::allow-rules = 0
39 | GtkTreeView::expander-size = 12
40 |
41 | GtkExpander::expander-size = 12
42 |
43 | GtkScrolledWindow::scrollbar_spacing = 1
44 |
45 | GtkSeparatorMenuItem::horizontal-padding = 2
46 |
47 | engine "wimp"
48 | {
49 | }
50 | }
51 | class "*" style "msw-default"
52 |
53 | binding "ms-windows-tree-view"
54 | {
55 | bind "Right" { "expand-collapse-cursor-row" (1,1,0) }
56 | bind "Left" { "expand-collapse-cursor-row" (1,0,0) }
57 | }
58 |
59 | class "GtkTreeView" binding "ms-windows-tree-view"
60 |
61 | style "msw-combobox-thickness" = "msw-default"
62 | {
63 | xthickness = 0
64 | ythickness = 0
65 | }
66 |
67 | widget_class "*TreeView*ComboBox*" style "msw-combobox-thickness"
68 | widget_class "*ComboBox*GtkFrame*" style "msw-combobox-thickness"
69 |
--------------------------------------------------------------------------------
/share/ui/logo_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_128px.png
--------------------------------------------------------------------------------
/share/ui/logo_16px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_16px.png
--------------------------------------------------------------------------------
/share/ui/logo_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_32px.png
--------------------------------------------------------------------------------
/share/ui/logo_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_48px.png
--------------------------------------------------------------------------------
/share/ui/logo_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_64px.png
--------------------------------------------------------------------------------
/share/ui/logo_gui.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_gui.bmp
--------------------------------------------------------------------------------
/share/ui/logo_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_gui.png
--------------------------------------------------------------------------------
/share/ui/logo_gui_vertical.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pib/pycam/df4e35e19cfafdef35853d42815cb6c17ed28964/share/ui/logo_gui_vertical.bmp
--------------------------------------------------------------------------------
/share/ui/menubar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/share/ui/model_export.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Save Model _as ...
7 | Save Model as ...
8 | Save the current model to a new STL file.
9 | gtk-save-as
10 |
11 |
12 | _Save Model
13 | _Save Model
14 | Save the current model to the STL file.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/share/ui/model_polygons.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | True
9 | 0
10 | none
11 |
12 |
13 | True
14 | 12
15 |
16 |
17 | True
18 | vertical
19 |
20 |
21 | True
22 | 3
23 | start
24 |
25 |
26 | Toggle direction
27 | True
28 | True
29 | True
30 |
31 |
32 | False
33 | False
34 | 2
35 | 0
36 |
37 |
38 |
39 |
40 | Revise directions
41 | True
42 | True
43 | True
44 |
45 |
46 | False
47 | False
48 | 1
49 |
50 |
51 |
52 |
53 | False
54 | 0
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | True
64 | <b>Polygon winding</b>
65 | True
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/share/ui/opengl_view_grid.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 | 3
8 |
9 |
10 | True
11 | Major grid:
12 |
13 |
14 | False
15 | 0
16 |
17 |
18 |
19 |
20 | True
21 | 100mm
22 |
23 |
24 | False
25 | 1
26 |
27 |
28 |
29 |
30 | True
31 | vertical
32 |
33 |
34 | False
35 | 3
36 | 2
37 |
38 |
39 |
40 |
41 | True
42 | Minor grid:
43 |
44 |
45 | False
46 | 3
47 |
48 |
49 |
50 |
51 | True
52 | 20mm
53 |
54 |
55 | False
56 | 4
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/share/ui/progress_bar.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | True
9 | vertical
10 |
11 |
12 | end
13 |
14 |
15 | False
16 | 0
17 |
18 |
19 |
20 |
21 | 3
22 |
23 |
24 | True
25 | end
26 |
27 |
28 | 0
29 |
30 |
31 |
32 |
33 | True
34 | vertical
35 |
36 |
37 | Show Progress
38 | True
39 | True
40 | True
41 |
42 |
43 | False
44 | 0
45 |
46 |
47 |
48 |
49 | Cancel
50 | True
51 | True
52 | True
53 |
54 |
55 | False
56 | 1
57 |
58 |
59 |
60 |
61 | False
62 | 1
63 |
64 |
65 |
66 |
67 | False
68 | 1
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/technical_details.txt:
--------------------------------------------------------------------------------
1 |
2 | The DropCutter goes as folows:
3 | 1) make lines along the X-axis (Lines)
4 | 2) for each line, take a number of samples (Samples)
5 | 3) at each sample, drop the cutter until it hits the model, this is a cutter location point
6 | 4) connect the cutter location points along the line
7 |
8 |
9 | The PushCutter goes as follows:
10 | 1) make slices along the Z-axis (Levels)
11 | 2) make lines along the X-axis (Lines)
12 | 3) push the cutter along the line until it hits the model, this is a cutter location
13 | if we do not hit anything, we're finished
14 | 4) skip the parts where the model is above the current z-level
15 | 5) go back to 3
16 |
17 |
18 | The PathAccumulator connects the cutter locations by cutting in between,
19 | it's usefull mainly for debugging
20 |
21 | The SimpleCutter connects the cutter locations in an on-off (cut/don't cut) pattern,
22 | it's usefull only for debugging
23 |
24 | The ZigZagCutter connects the cutter locations by cutting in between points,
25 | then come back along the next line in reverse direction.
26 | it's usefull mainly in combination with the DropCutter
27 |
28 | The PolygonCutter extracts polygonial sections from the cutter scanlines,
29 | then cuts those in a zig-zag pattern
30 | it's usefull mainly in combination with the PushCutter
31 |
32 | The ContourCutter extracts contours from the cutter scanlines,
33 | it's usefull only in combination with the PushCutter
34 |
35 |
--------------------------------------------------------------------------------