Comparar commits

...

159 Commits

Autor SHA1 Mensagem Data
daid 5a6ebd4156 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2015-01-16 12:17:31 +01:00
daid 0fdec5246b CNC GCode needs P instead of S parameters. 2015-01-16 12:17:22 +01:00
daid bbed68e588 Merge pull request #153 from presslab-us/fixskintravel
Use retract on skins
2015-01-14 14:32:03 +01:00
Ryan Press 49d5a81d02 Add option to disable combing on skin surfaces
The existing enableCombing options 0, 1 work as before.  A new
option 2 disables combing on the skin surfaces.  This forces a
retract to reduce the unsightly scarring on flat surfaces.
2015-01-09 14:20:52 -08:00
Ryan Press 3e980a84fd slab-us presslab-us authored 5 days ago evert "Use retract on skins"
This reverts commit af14f82a7f.
Because this option should be configurable, change the implementation.
2015-01-09 10:06:39 -08:00
Ryan Press af14f82a7f Use retract on skins
Using combing on skins causes unsightly scarring on flat surfaces.
By forcing a retract between travel moves we can lift the head
above the surface to avoid this.
2015-01-04 14:19:50 -08:00
daid ff4fa3581e Fix the raft printing speed. 2014-12-18 11:02:18 +01:00
daid 2bef59e79b Seperate the skin and infill to give them different speeds. Also change the printing order of this to handle pressure differences better. Which should increase print quality on advanced profiles. 2014-12-16 15:54:55 +01:00
daid 2e5b72cfef Merge pull request #150 from presslab-us/fixpathstart
Reset start position for new layers
2014-12-15 09:59:11 +01:00
Ryan Press 3aa638ea42 Reset start position for new layers
Reset the start position of the path optimizer for each new layer
so that it doesn't double up on the layers, causing problems for
cooling.
2014-12-14 09:08:17 -08:00
daid edf2f897d1 Change how open polygons are saved. As std::vector never clears memory and the old implementation could mean a lot of memory was never freed. 2014-12-12 11:00:33 +01:00
daid cacffadc61 It looks like removing duplicate faces actually has a negative effect on "bad" model. 2014-12-10 08:54:38 +01:00
daid d7a26ab715 Improve skirt handling with support material 2014-12-03 15:39:30 +01:00
daid 65dc9fe64b Fix #144 for dos format files. 2014-11-26 15:08:09 +01:00
daid db6c9a05e0 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-11-26 12:14:08 +01:00
daid 93e2cda988 Fix BFB support. 2014-11-26 12:12:57 +01:00
daid d49257d30c Merge pull request #141 from qbit/master
Add flags for OpenBSD
2014-11-24 07:25:31 +01:00
Aaron Bieber e53408dc52 Flags for OpenBSD 2014-11-20 18:06:06 -07:00
daid ed8c6cd5ce Remove build clipper on clean. 2014-10-30 10:08:14 +01:00
daid 9a3d0c7099 Fix in the dot_score calculation, was causing wrong start points to be selected. 2014-10-28 11:00:40 +01:00
TimKuipers 40ff880ef0 Update README.md
removed typos, improved syntax and changed some expressions
2014-10-20 16:15:34 +02:00
daid 4cf43fca20 Merge pull request #134 from zwbrush/master
Update optimizedModel.h
2014-10-16 16:00:47 +02:00
zwbrush 525c8a6966 Update optimizedModel.h
fix position calculate error when autoCenter!=1
2014-10-15 21:18:11 +08:00
daid 68473ad811 Fix problems with path order generating more travel on infill then it should. See: http://umforum.ultimaker.com/index.php?/topic/7727-1409-creates-insane-amounts-of-travel/ 2014-10-13 09:50:57 +02:00
daid 90ca7b8658 Make the hashPoint function use a const to fix compile issues. 2014-10-01 15:22:19 +02:00
daid b3b79f42d5 Speed up the path-order-optimizer by using a hashmap to look for nearby points. This greatly speeds up the order optimizer when used on infill lines. 2014-09-26 14:56:57 +02:00
daid 7b042b2b7d Merge pull request #128 from foosel/fix/fileExtension
"*.STL" files might be STL files too
2014-09-25 10:57:55 +02:00
Gina Häußge b5e4928780 "*.STL" files might be STL files too
Makes STL file detection based on extension work case insensitive. Without
that patch, files named like "FILE.STL" or "file.StL" or ... couldn't be
sliced. CuraEngine quit attempts to do so with "Could not load file"
instead.
2014-09-25 10:39:14 +02:00
daid e089c82781 Merge pull request #127 from repetier/master
Fix problems in settings parser
2014-09-24 15:05:00 +02:00
repetier cf5008bec3 Fixed problems with settings 2014-09-23 17:50:01 +02:00
repetier ceae269ff1 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2014-09-23 16:19:26 +02:00
repetier fac9b89f81 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2014-09-23 16:17:44 +02:00
daid 613c2af385 Fix build on linux. 2014-09-19 13:35:13 +02:00
daid 0c60a087f3 Cross hatch the first layer of line grids so you get better bed adheasion. Also expand these lines a bit more so they attach to a brim better, if there is a brim. 2014-09-19 11:41:37 +02:00
daid 0f7f58aab4 Some better parameters for the corner finding routine. 2014-09-19 11:39:57 +02:00
daid dd1612c763 Try to put the Z seam in internal corners instead of anywhere on the external face. 2014-09-19 11:39:44 +02:00
daid 9fd36d8820 Merge pull request #124 from foosel/fix/tooMuchRounding
Fix rounding issue on generated GCODE
2014-08-28 12:01:05 +02:00
Gina Häußge e72aee5558 Fix rounding issue on generated GCODE
We store the X, Y, Z coordinates in microns, let's try to
keep as much precision and do not round to 2 digits after
the decimal point but 3 in generated GCODEs when converting
into mm.
2014-08-28 10:40:24 +02:00
daid 4c1043f69a Merge pull request #118 from electrocbd/use_gdb
No -flto compiler option in debug mode under Linux
2014-07-19 23:11:23 +02:00
electrocbd 7df7da788b No -flto compiler option in debug mode under Linux 2014-07-19 15:10:14 +02:00
daid beb781ac68 Merge pull request #115 from electrocbd/raftskirt
Print skirt before raft
2014-07-16 17:17:57 +02:00
electrocbd b5e94f93b7 Print skirt before raft 2014-07-15 10:34:53 +02:00
repetier 2451df3f1e Do not add CodeBlocks config 2014-07-10 15:46:37 +02:00
daid e4e2c0428d Merge pull request #111 from cyberjoac/master
Handling correctly both the Ascii models and Binary models.
2014-06-28 21:33:14 +02:00
Joachim Hagege 074d4cea00 Bug correction in the logic for loading an Ascii model 2014-06-28 18:13:11 +03:00
Joachim Hagege b17a3f2a92 Corrected a bug with loading an ASCII model 2014-06-28 18:03:08 +03:00
daid 481df6ddcd On -- process the current file, which allows for one-at-a-time printing from the commandline. 2014-06-25 09:28:19 +02:00
daid 0f85c400d3 Fix loading of solidworks files, which start with "solid" but are binary files... 2014-06-24 10:21:11 +02:00
daid e5f42cb1f7 Fixed #107 - Good catch! 2014-06-17 09:09:21 +02:00
daid 1a21409220 Fix problems with dual-extrusion retraction. Make sure retraction on head-lift happens. 2014-06-05 09:37:07 +02:00
daid 08b16b3c4e Merge pull request #104 from cederom/master
Fixed abs() -> fabs() typo that blocked build on FreeBSD.
2014-06-04 11:56:51 +02:00
Tomek CEDRO 8b94f8d886 Fixed abs() -> fabs() typo that blocked build on FreeBSD.
BUILD SYSTEM:
% uname -a
FreeBSD hexagon 10.0-RELEASE-p2 FreeBSD 10.0-RELEASE-p2 #0: Tue Apr 29 17:06:01 UTC 2014     root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC  amd64

BUILD LOG BEFORE FIX:
% gmake CXX="c++"
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/bridge.cpp -o build/bridge.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/comb.cpp -o build/comb.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/gcodeExport.cpp -o build/gcodeExport.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/infill.cpp -o build/infill.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/inset.cpp -o build/inset.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/layerPart.cpp -o build/layerPart.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/main.cpp -o build/main.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/optimizedModel.cpp -o build/optimizedModel.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/pathOrderOptimizer.cpp -o build/pathOrderOptimizer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/polygonOptimizer.cpp -o build/polygonOptimizer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/raft.cpp -o build/raft.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/settings.cpp -o build/settings.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/skin.cpp -o build/skin.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/skirt.cpp -o build/skirt.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/slicer.cpp -o build/slicer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/support.cpp -o build/support.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/timeEstimate.cpp -o build/timeEstimate.o
src/timeEstimate.cpp:119:35: error: use of undeclared identifier 'abs'; did you mean 'fabs'?
        current_abs_feedrate[n] = abs(current_feedrate[n]);
                                  ^~~
                                  fabs
/usr/include/math.h:259:8: note: 'fabs' declared here
double  fabs(double) __pure2;
        ^
1 error generated.
gmake: *** [build/timeEstimate.o] Error 1

BUILD LOG AFTER FIX:
% gmake CXX="c++"
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/bridge.cpp -o build/bridge.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/comb.cpp -o build/comb.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/gcodeExport.cpp -o build/gcodeExport.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/infill.cpp -o build/infill.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/inset.cpp -o build/inset.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/layerPart.cpp -o build/layerPart.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/main.cpp -o build/main.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/optimizedModel.cpp -o build/optimizedModel.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/pathOrderOptimizer.cpp -o build/pathOrderOptimizer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/polygonOptimizer.cpp -o build/polygonOptimizer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/raft.cpp -o build/raft.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/settings.cpp -o build/settings.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/skin.cpp -o build/skin.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/skirt.cpp -o build/skirt.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/slicer.cpp -o build/slicer.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/support.cpp -o build/support.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/timeEstimate.cpp -o build/timeEstimate.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/modelFile/modelFile.cpp -o build/modelFile/modelFile.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/utils/gettime.cpp -o build/utils/gettime.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/utils/logoutput.cpp -o build/utils/logoutput.o
c++ -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"DEV\" -isystem libs -O3 -fomit-frame-pointer src/utils/socket.cpp -o build/utils/socket.o
c++ build/bridge.o build/comb.o build/gcodeExport.o build/infill.o build/inset.o build/layerPart.o build/main.o build/optimizedModel.o build/pathOrderOptimizer.o build/polygonOptimizer.o build/raft.o build/settings.o build/skin.o build/skirt.o build/slicer.o build/support.o build/timeEstimate.o build/modelFile/modelFile.o build/utils/gettime.o build/utils/logoutput.o build/utils/socket.o -o build/CuraEngine -Lbuild/ -lclipper
2014-06-02 21:59:48 +02:00
daid b27a51e031 Add missing code for the pre/post extruder swap codes. 2014-05-27 09:53:06 +02:00
daid ae7f359f23 Add pre/post extruder switch gcode templates. Add ZHop to extruder switch retraction. Fixed #99 2014-05-26 09:46:55 +02:00
daid 4b4c71c537 Fix multi-object slicing in cura. 2014-05-23 10:31:53 +02:00
daid 3c3a24ff92 Optimize line segment order a bit so it favors going more in a straight line. 2014-05-21 09:05:02 +02:00
daid b993e93a68 Some fixes for the SimpleMode. 2014-05-21 08:55:30 +02:00
daid cc4a65de19 Fix the bug introduced into the concentric infill in #90 2014-05-20 16:13:51 +02:00
daid e71ea1f3a9 Merge pull request #96 from oliv3r/master
Carriage return cleanups from CuraEngine
2014-05-20 09:59:56 +02:00
Olliver Schinagl a4957c52fd Remove \r from CuraEngine
CuraEngine uses newline's, not carriage return's. This patch strips \r's
from the source files. No code changes where performed.

Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2014-05-19 13:50:53 +02:00
Olliver Schinagl 848e395b72 Add missing stdio.h for usage of filestream functions
Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2014-05-19 13:11:32 +02:00
daid b3d79e675b Add simple mode which does hardly anything, but has nice use for some artists. 2014-05-17 07:18:38 +02:00
daid 8c6f42cbb8 Some minor cleanup after #95 2014-05-17 06:40:53 +02:00
daid 40fb8896d8 Some minor cleanup after #95 2014-05-17 06:40:11 +02:00
daid fe6bc0499e Merge pull request #95 from repetier/master
Multi-Stl-load, codeblocks support, autocenter, bug fixes
2014-05-17 06:31:38 +02:00
repetier b8a91bb06f Merge branch 'master' of https://github.com/Ultimaker/CuraEngine
Fixed raft air gap bug
2014-05-15 18:48:26 +02:00
daid aa46967d79 Fix problem with G11 added at the first skirt line. 2014-05-15 12:58:07 +02:00
daid d0ce9523d3 Merge pull request #90 from znib/patch-1
removed an unnecessary if()
2014-05-05 10:58:23 +02:00
znib 05657bbe6c removed an unnecessary if()
Nothing too important, but I think it makes the code cleaner :-)
2014-05-03 12:56:10 +02:00
repetier 7b0e835168 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2014-04-29 17:25:19 +02:00
daid ad28c7707c The lto flag does not work on MacOS with multi-arch builds. 2014-04-23 15:19:54 +02:00
daid 4fc877afd0 Cross-hatch the surface layers of the raft. 2014-04-23 14:26:02 +02:00
daid ea10ec7c44 Do not retract during raft, that is unneeded. 2014-04-23 12:42:34 +02:00
daid c4a127fcd6 Fix makefile, crashed on msys windows. 2014-04-23 10:54:56 +02:00
daid 129f502f28 Fix makefile, crashed on msys windows. 2014-04-23 10:53:59 +02:00
daid cd0ef93813 Add volumatric RepRap GCode for https://github.com/daid/Cura/issues/825 2014-04-23 09:55:49 +02:00
daid 1ceac5ab0e Merge pull request #84 from Skeen/modern_casts
Modern casting
2014-04-23 09:52:28 +02:00
daid b5769093c4 Merge pull request #86 from Skeen/enums
Change defines into enums
2014-04-23 09:51:44 +02:00
daid 73157f49d1 Merge pull request #87 from Skeen/origin
Link time optimization
2014-04-23 09:51:26 +02:00
repetier 16a51edc2d Load multiple stl files at once to allow multi-extruder slicing from command line 2014-04-21 19:59:59 +02:00
Skeen cd8cb7733a Merge remote-tracking branch 'upstream/master' into HEAD
Conflicts:
	Makefile
2014-04-18 23:56:00 +02:00
Skeen 884b9100d1 Merge remote-tracking branch 'upstream/master' into enums
Conflicts:
	src/settings.cpp
2014-04-18 23:49:20 +02:00
Skeen 4999c1d6ea Merge remote-tracking branch 'upstream/master' into modern_casts
Conflicts:
	Makefile
2014-04-18 23:47:38 +02:00
daid 981d956766 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-04-18 06:44:55 +02:00
daid 98c7fc1ee3 Fixing https://github.com/daid/Cura/issues/820 2014-04-18 06:44:46 +02:00
daid 9434abfcf6 Merge pull request #85 from Skeen/multi_infill
Prepare for supporting multiple different infills.
2014-04-18 06:41:29 +02:00
daid f1acd11ffd Merge pull request #83 from Skeen/nullptr
Change NULL into nullptr
2014-04-18 06:38:11 +02:00
daid 3c252ff91e Merge pull request #82 from Skeen/build_profiles
Add support for multiple build profiles
2014-04-18 06:37:24 +02:00
Skeen 55cd07cf58 We should do link type optimization, in order to get a faster executable. 2014-04-16 17:18:11 +02:00
Skeen 7051907cd5 Integer constructors are really casts too. 2014-04-16 17:15:48 +02:00
Skeen be02553c46 Changed a few defines into weak type enums.
These should eventually be changed into strong type enums,
in order to provide type safety!
2014-04-16 17:11:36 +02:00
Skeen 3a1f0ca2e1 Applied the quickfix from the working branch. 2014-04-16 17:05:59 +02:00
Skeen c5f3fe2b9c Removing a wierd unwanted space. 2014-04-16 17:02:26 +02:00
Skeen 094ed5b4c7 Prepare for supporting multiple different infills. 2014-04-16 17:01:17 +02:00
Skeen c7eb07468d Added a flag to warn on C style casts.
Removed all C style flags, and replaced with C++ casts.
2014-04-16 16:50:16 +02:00
Skeen 7623c51e41 Changed NULL to nullptr 2014-04-16 16:43:03 +02:00
Skeen 2c8ddc3295 Added support for multiple build profiles. Defaulting to release. 2014-04-16 16:38:59 +02:00
Skeen 7c2606287e Removed invalid hint about how to easily build project 2014-04-16 16:31:45 +02:00
repetier 82028756da Add auto center option, Codeblocks compilation support 2014-04-13 10:45:59 +02:00
daid 8bd2bcacbc _Polygon define is no longer needed due to namespaces. 2014-04-11 16:09:25 +02:00
daid dc74a09092 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-04-11 10:15:22 +02:00
daid 8f049641d7 Fix crash bug. 2014-04-11 09:04:54 +02:00
daid 2ee44238ec Fix crash bug. 2014-04-11 09:04:15 +02:00
daid 74b1b67b0d Merge pull request #80 from martinxyz/master
fix compilation on Linux
2014-04-10 11:45:49 +02:00
Martin Renold 0e6f4abe27 fix compilation on Linux
src/gcodeExport.cpp:63:23: error: cannot convert ‘fpos_t {aka _G_fpos_t}’ to ‘long int’ for argument ‘2’ to ‘int fseek(FILE*, long int, int)’
     fseek(f, oldPos, SEEK_SET);
2014-04-09 15:30:43 +02:00
daid 10b6a4788f Make bridges per fill area instead of per layer. 2014-04-08 13:58:00 +02:00
daid 6430e67ddd Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-04-08 12:09:53 +02:00
daid 6d38063e34 Some cleanup, moved the inside function into the polygon class instead of the comb class. Made all the lineends unix style. Added namespaces. 2014-04-08 12:09:42 +02:00
Daid ab08b89496 Fix MacOS build. 2014-04-07 14:45:28 +02:00
daid 4f8caa85ec Add missing util/string.h 2014-04-07 14:41:32 +02:00
daid 8b4d4743e9 Strange shit is happening... how the hell did this happen?. 2014-04-07 13:30:24 +02:00
daid c2348ef17c Fix c++11 compiling. 2014-04-07 13:26:41 +02:00
daid 78853237e0 Do not force lineends. 2014-04-07 12:15:57 +02:00
daid 87eacf161b Fix building libclipper.a all the time after #76 2014-04-07 11:32:33 +02:00
daid 24db283088 Merge pull request #76 from Skeen/master
out-of-source building + more
2014-04-07 10:52:56 +02:00
Skeen d7d9b5b9a4 Moved clipper to a libraries folder.
Added a DO_NOT_CHANGE_THESE files, nothing developers that the sources in this
folder, is not to be changed directly in this repository, but should rather be
modified in their respective repositories.
Updated the Makefile to build this library.
Updated the Makefile to include the libs folder, as a isystem include
    I.e. do not throw errors, warnings, ect. from these headers.
Updated intpoint, and polygon to include clipper via. compiler lookup.
2014-04-06 22:24:19 +02:00
Skeen d00682c56f We want crlf endings 2014-04-06 21:45:38 +02:00
Skeen 0888e50219 We want uniform line endings!
The gitattributes file, should ensure that files always have the same
line endings at checkout.
2014-04-06 21:36:47 +02:00
Skeen 4abd629e2f Cross platform makefile
The mkdir step, should now work on all platforms.
2014-04-06 19:11:11 +02:00
Skeen c454b7881c Fixed gitignore, made makefile cross platform.
Fixed gitignore to actually ignore vim swap files.
2014-04-06 19:10:43 +02:00
Skeen f0d7e830f5 Removed vim swap file
This was an accidental upload.
2014-04-06 19:03:32 +02:00
Skeen b539f0d2b0 Updated gitignore
Ignore vim comment files, and the build folder (for out of source
builds)
2014-04-06 19:00:13 +02:00
daid 6aa27e0271 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-03-31 12:09:29 +02:00
daid c4f0f354ae M103 is stop all extruders. 2014-03-31 10:13:51 +02:00
daid 6cc0cca7a6 Merge pull request #73 from Ryoko/Raft_interface_generation_angle
Change the angle on which raft interface layer's infill generated.
2014-03-31 10:01:55 +02:00
Ryoko e41ebcb3ae In case of no surface layers in raft - generate interface layer infill on 90 degree angle. 2014-03-30 22:11:24 +03:00
daid 943ae5901f Add AGPL notice to engine. 2014-03-28 16:21:32 +01:00
daid de8661da31 Change extruder swaps for BFB. 2014-03-28 10:21:14 +01:00
daid 93dfc8b2d6 Do not use the G92 Exxx for BFB code. 2014-03-28 09:48:53 +01:00
daid 1cea56cdd3 Fix RPM and multiple extruders for BFB style GCode. 2014-03-28 09:31:52 +01:00
daid 483f18ed96 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2014-03-25 14:10:29 +01:00
daid c4fa1ba55a Fix the problem where very high polygon circles generate a lot of very small segments 2014-03-25 14:09:34 +01:00
daid 0ef0ae3ca0 Merge pull request #71 from Ryoko/AddToPrime
Added setting 'retractionAmountPrime'
2014-03-24 10:01:14 +01:00
daid a9376b50e6 Merge pull request #72 from Gregwar/raft_parameters
Introducing raftFanSpeed and raftSurfaceSpeed
2014-03-24 10:00:14 +01:00
Gregwar 8d57f871aa Introducing raftFanSpeed and raftSurfaceSpeed 2014-03-23 22:33:32 +01:00
Ryoko bb4ff5a2ca Added setting 'retractionAmountPrime' specifying the amount of filament added to the prime after the retraction. 2014-03-23 19:45:03 +03:00
daid 62667ff2e7 Merge pull request #68 from Gregwar/first_layer_gap
Shifting raft surface only for first layer
2014-03-19 17:22:58 +01:00
daid ea767b8615 Merge pull request #66 from Gregwar/fixing_speed_smoothing
Fixing speed smoothing
2014-03-18 13:40:08 +01:00
daid e2c33e576e Set default raftSurface layers to 0. 2014-03-18 08:33:46 +01:00
Gregwar 80cea5e104 Fixing speed smoothing 2014-03-18 00:05:46 +01:00
Gregwar 052c79b8eb Shifting raft surface only for first layer 2014-03-17 23:41:24 +01:00
daid 218067b650 Merge branch 'BFB' 2014-03-17 15:59:53 +01:00
daid a77a4549b8 Merge pull request #64 from Gregwar/master
Adding raft surface support
2014-03-17 15:24:12 +01:00
Gregwar c7f18f21a4 Defaulting raftBaseSpeed to 0 2014-03-15 15:56:36 +01:00
Gregwar 8403b3d238 Removing testing config 2014-03-15 15:54:27 +01:00
Gregwar e9ce954141 Renaming raftFull to raftSurface 2014-03-15 13:42:07 +01:00
Gregwar 3467e1f1a5 Fixes 2014-03-14 11:16:52 +01:00
Gregwar b67f18a64a Introducing raftFull layers 2014-03-14 10:53:47 +01:00
daid 9f46abc2b0 Reduce the default layer0extrusionwidth a bit. 2014-03-14 09:02:47 +01:00
Gregwar 704492360d Introducing new parameters (for solid raft) 2014-03-13 23:58:25 +01:00
daid 338b4d374d Fix retraction for RepRap. 2014-03-13 17:23:19 +01:00
daid 142739a24b Merge branch 'master' into BFB 2014-03-13 15:55:22 +01:00
daid 3943b2863d Merge branch 'master' into BFB 2014-03-12 08:33:36 +01:00
daid a69de22d31 Merge fix. 2014-03-10 14:40:19 +01:00
daid 46f376ba8b Update some BFB comments. 2014-02-26 10:39:56 +01:00
daid bccf807b1c Fix usage message. 2014-02-25 19:54:47 +01:00
daid fc000dae7a Merge branch 'master' into BFB 2014-02-25 14:52:07 +01:00
daid 445498eba1 Add MACH3 GCode flavor. 2014-02-19 09:41:09 +01:00
daid f94a77bf94 Adds initial bits from bytes support. 2014-02-17 16:54:08 +01:00
61 arquivos alterados com 11211 adições e 10508 exclusões
+5 -1
Ver Arquivo
@@ -13,4 +13,8 @@ _obj
.*.swp
*.gcode
CuraEngine
build/*
*~
NUL
CuraEngine.layout
CuraEngine.cbp
+63 -27
Ver Arquivo
@@ -2,52 +2,88 @@
# Makefile for CuraEngine
#
# simplest working invocation to compile it
#g++ main.cpp modelFile/modelFile.cpp clipper/clipper.cpp -o CuraEngine
BUILD_DIR = build
SRC_DIR = src
LIBS_DIR = libs
BUILD_TYPE = RELEASE
VERSION ?= DEV
CXX ?= g++
CFLAGS += -c -Wall -Wextra -O3 -fomit-frame-pointer -DVERSION=\"$(VERSION)\"
# also include debug symbols
#CFLAGS+=-ggdb
LDFLAGS +=
SOURCES = bridge.cpp comb.cpp gcodeExport.cpp infill.cpp inset.cpp layerPart.cpp main.cpp optimizedModel.cpp pathOrderOptimizer.cpp polygonOptimizer.cpp raft.cpp settings.cpp skin.cpp skirt.cpp slicer.cpp support.cpp timeEstimate.cpp
SOURCES += clipper/clipper.cpp modelFile/modelFile.cpp utils/gettime.cpp utils/logoutput.cpp utils/socket.cpp
OBJECTS = $(SOURCES:.cpp=.o)
EXECUTABLE = ./CuraEngine
UNAME := $(shell uname)
CFLAGS += -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"$(VERSION)\" -isystem libs
ifeq ($(UNAME), Linux)
OPEN_HTML=firefox
LDFLAGS += --static
ifeq ($(BUILD_TYPE),DEBUG)
CFLAGS+=-ggdb -Og -g
endif
ifeq ($(UNAME), Darwin)
OPEN_HTML=open
#For MacOS force to build
CFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
LDFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
ifeq ($(BUILD_TYPE),PROFILE)
CFLAGS+= -pg
endif
ifeq ($(UNAME), MINGW32_NT-6.1)
ifeq ($(BUILD_TYPE),RELEASE)
CFLAGS+= -O3 -fomit-frame-pointer
endif
LDFLAGS += -Lbuild/ -lclipper
SOURCES_RAW = bridge.cpp comb.cpp gcodeExport.cpp infill.cpp inset.cpp layerPart.cpp main.cpp optimizedModel.cpp pathOrderOptimizer.cpp polygonOptimizer.cpp raft.cpp settings.cpp skin.cpp skirt.cpp slicer.cpp support.cpp timeEstimate.cpp
SOURCES_RAW += modelFile/modelFile.cpp utils/gettime.cpp utils/logoutput.cpp utils/socket.cpp
SOURCES = $(addprefix $(SRC_DIR)/,$(SOURCES_RAW))
OBJECTS_RAW = $(SOURCES_RAW:.cpp=.o)
OBJECTS = $(addprefix $(BUILD_DIR)/,$(OBJECTS_RAW))
DIRS = $(sort $(dir $(OBJECTS)))
EXECUTABLE = $(BUILD_DIR)/CuraEngine
ifeq ($(OS),Windows_NT)
#For windows make it large address aware, which allows the process to use more then 2GB of memory.
EXECUTABLE := $(EXECUTABLE).exe
CFLAGS += -march=pentium4
LDFLAGS += -Wl,--large-address-aware -lm -lwsock32
CFLAGS += -march=pentium4 -flto
LDFLAGS += -Wl,--large-address-aware -lm -lwsock32 -flto
MKDIR_PREFIX = mkdir -p
else
MKDIR_PREFIX = mkdir -p
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
OPEN_HTML=firefox
ifeq ($(BUILD_TYPE),DEBUG)
LDFLAGS += --static
else
CFLAGS += -flto
LDFLAGS += --static -flto
endif
endif
ifeq ($(UNAME), OpenBSD)
LDFLAGS += -lm -lpthread
endif
ifeq ($(UNAME), Darwin)
OPEN_HTML=open
#For MacOS force to build
CFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
LDFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
endif
endif
all: $(SOURCES) $(EXECUTABLE)
all: $(DIRS) $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(BUILD_DIR)/libclipper.a: $(LIBS_DIR)/clipper/clipper.cpp
$(CXX) $(CFLAGS) -o $(BUILD_DIR)/libclipper.a $(LIBS_DIR)/clipper/clipper.cpp
$(EXECUTABLE): $(OBJECTS) $(BUILD_DIR)/libclipper.a
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
.cpp.o:
$(DIRS):
-@$(MKDIR_PREFIX) $(DIRS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(CFLAGS) $< -o $@
test: $(EXECUTABLE)
python _tests/runtest.py $(abspath $(EXECUTABLE))
python tests/runtest.py $(abspath $(EXECUTABLE))
## clean stuff
clean:
rm -f $(EXECUTABLE) $(OBJECTS)
rm -f $(EXECUTABLE) $(OBJECTS) $(BUILD_DIR)/libclipper.a
help:
@cat Makefile |grep \#\#| grep \: |cut -d\# -f3
+11 -11
Ver Arquivo
@@ -5,9 +5,9 @@ The CuraEngine is a C++ console application for 3D printing GCode generation. It
The CuraEngine is pure C++ and uses Clipper from http://www.angusj.com/delphi/clipper.php
There are no external dependences and Clipper is included in the source code without modifications.
This is just a console application for GCode generation. For a full graphical application look at https://github.com/daid/Cura with is the graphical frontend for CuraEngine.
This is just a console application for GCode generation. For a full graphical application look at https://github.com/daid/Cura which is the graphical frontend for CuraEngine.
The CuraEngine can be used seperately or in other applications. Feel free to add it to your application. But to take note of the License.
The CuraEngine can be used seperately or in other applications. Feel free to add it to your application. But please take note of the License.
License
=======
@@ -20,7 +20,7 @@ But in general it boils down to: You need to share the source of any CuraEngine
Internals
=========
The Cura Engine is structured as mainly .h files. This is not standard for an C++ project. However, using less cpp files makes the optimizer work harder and removes linking error issues. It's partialy a result of lazyness but also for optimalizations.
The Cura Engine is structured as mainly .h files. This is not standard for a C++ project. However, using less cpp files makes the optimizer work harder and removes linking error issues. It's partialy a result of lazyness but comes in handy for optimalizations.
The .h files contain different steps called from the main.cpp file. The main.cpp file contains the global slicing logic.
@@ -45,20 +45,20 @@ The OptimizedModel is a 3D model stored with vertex<->face relations. This gives
Slicer
======
While usually the whole GCode generation process is called Slicing. The slicer in the CuraEngine is the piece of code that generates layers. Each layer has closed 2D polygons.
While usually the whole GCode generation process is called 'slicing', the Slicer in the CuraEngine is the piece of code that generates layers. Each layer contains closed 2D polygons.
These polygons are generated in a 2 step process. First all triangles are cut into lines per layer, for each layer a "line segment" is added to that layer.
Next all these line-segments are connected to eachother to make Polygons. The vertex<->face relations of the OptimizedModel help to make this process fast, as there is a huge chance that 2 connecting faces also make 2 connecting line-segments.
This code also fixes up small holes in the 3D model, so your model doesn't need to be perfect Manifold. It also accounts for incorrect normals, so it can flip around line-segments to fit end-to-end.
This code also patches up small holes in the 3D model, so your model doesn't need to be a perfect Manifold. It also deals with incorrect normals, so it can flip around line-segments to fit end-to-end.
After the Slicer we have closed Polygons which can be used in Clipper, as Clipper can only opperate on closed 2D polygons.
LayerParts
==========
An important concept to grasp is the LayerParts. LayerParts are seperate parts inside a single layer. For example, if you have a cube. Then each layer has a single LayerPart. However, if you have a table, then the layers which build the legs have a LayerPart per leg, and thus there will be 4 LayerParts.
A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to process. During GCode generation handling each LayerPart as an own step makes sure you never travel between LayerParts and thus reducing the amount of external travel.
An important concept to grasp is the idea of LayerParts. LayerParts are seperate parts inside a single layer. For example, in a solid cube each layer has a single LayerPart. However, in a table the layers which cover the legs have a LayerPart per leg, and thus there will be 4 LayerParts.
A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to be processed. During GCode generation handling each LayerPart as a separate step makes sure you never travel between LayerParts which reduces the amount of external travel.
LayerParts are generated after the Slicer step.
To generate the LayerParts Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.
In order to generate the LayerParts, Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.
Insets
@@ -70,12 +70,12 @@ Up/Down skin
The skin code generates the fully filled areas, it does this with some heavy boolean Clipper action. The skin step uses data from different layers to get the job done. Check the code for details.
The sparse infill area code is almost the same as the skin code. With the difference that it keeps the other areas and uses different offsets.
Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are list of Polygons which are the areas that need to be filled.
Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are lists of Polygons which are the areas that need to be filled.
GCode generation
================
The GCode generation is quite a large bit of code. As a lot is going on here. Important bits here are:
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
* Comb: The combing code is the code that tries to avoid holes when moving around the head without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode, and to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
* Comb: The combing code is the code that tries to avoid holes when moving the head around without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax;meshmdhfdhfdhf to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
+3
Ver Arquivo
@@ -0,0 +1,3 @@
These libraries are extern, and while they're utilized by this project,
they are in fact not actually developed on this repository.
If changes are desired for these projects, please refer to their official repositories.
+26 -26
Ver Arquivo
@@ -1,26 +1,26 @@
The Clipper Library (including Delphi, C++ & C# source code, other accompanying
code, examples and documentation), hereafter called "the Software", has been
released under the following license, terms and conditions:
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the Software covered by this license to use, reproduce,
display, distribute, execute, and transmit the Software, and to prepare
derivative works of the Software, and to permit third-parties to whom the
Software is furnished to do so, all subject to the following:
The copyright notices in the Software and this entire statement, including the
above license grant, this restriction and the following disclaimer, must be
included in all copies of the Software, in whole or in part, and all derivative
works of the Software, unless such copies or derivative works are solely in the
form of machine-executable object code generated by a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The Clipper Library (including Delphi, C++ & C# source code, other accompanying
code, examples and documentation), hereafter called "the Software", has been
released under the following license, terms and conditions:
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the Software covered by this license to use, reproduce,
display, distribute, execute, and transmit the Software, and to prepare
derivative works of the Software, and to permit third-parties to whom the
Software is furnished to do so, all subject to the following:
The copyright notices in the Software and this entire statement, including the
above license grant, this restriction and the following disclaimer, must be
included in all copies of the Software, in whole or in part, and all derivative
works of the Software, unless such copies or derivative works are solely in the
form of machine-executable object code generated by a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+375 -375
Ver Arquivo
@@ -1,376 +1,376 @@
=====================================================================
Clipper Change Log
=====================================================================
v6.1.3 (19 January 2014)
* Fixed potential endless loop condition when adding open
paths to Clipper.
* Fixed missing implementation of SimplifyPolygon function
in C++ code.
* Fixed incorrect upper range constant for polygon coordinates
in Delphi code.
* Added PointInPolygon function.
* Overloaded MinkowskiSum function to accommodate multi-contour
paths.
v6.1.2 (15 December 2013)
* Fixed broken C++ header file.
* Minor improvement to joining polygons.
v6.1.1 (13 December 2013)
* Fixed a couple of bugs affecting open paths that could
raise unhandled exceptions.
v6.1.0 (12 December 2013)
* Deleted: Previously deprecated code has been removed.
* Modified: The OffsetPaths function is now deprecated as it has
been replaced by the ClipperOffset class which is much more
flexible.
* Bugfixes: Several minor bugs have been fixed including
occasionally an incorrect nesting within the PolyTree structure.
v6.0.0 (30 October 2013)
* Added: Open path (polyline) clipping. A new 'Curves' demo
application showcases this (see the 'Curves' directory).
* Update: Major improvement in the merging of
shared/collinear edges in clip solutions (see Execute).
* Added: The IntPoint structure now has an optional 'Z' member.
(See the precompiler directive use_xyz.)
* Added: Users can now force Clipper to use 32bit integers
(via the precompiler directive use_int32) instead of using
64bit integers.
* Modified: To accommodate open paths, the Polygon and Polygons
structures have been renamed Path and Paths respectively. The
AddPolygon and AddPolygons methods of the ClipperBase class
have been renamed AddPath and AddPaths respectively. Several
other functions have been similarly renamed.
* Modified: The PolyNode Class has a new IsOpen property.
* Modified: The Clipper class has a new ZFillFunction property.
* Added: MinkowskiSum and MinkowskiDiff functions added.
* Added: Several other new functions have been added including
PolyTreeToPaths, OpenPathsFromPolyTree and ClosedPathsFromPolyTree.
* Added: The Clipper constructor now accepts an optional InitOptions
parameter to simplify setting properties.
* Bugfixes: Numerous minor bugs have been fixed.
* Deprecated: Version 6 is a major upgrade from previous versions
and quite a number of changes have been made to exposed structures
and functions. To minimize inconvenience to existing library users,
some code has been retained and some added to maintain backward
compatibility. However, because this code will be removed in a
future update, it has been marked as deprecated and a precompiler
directive use_deprecated has been defined.
v5.1.6 (23 May 2013)
* BugFix: CleanPolygon function was buggy.
* Changed: The behaviour of the 'miter' JoinType has been
changed so that when squaring occurs, it's no longer
extended up to the miter limit but is squared off at
exactly 'delta' units. (This improves the look of mitering
with larger limits at acute angles.)
* Added: New OffsetPolyLines function
* Update: Minor code refactoring and optimisations
v5.1.5 (5 May 2013)
* Added: ForceSimple property to Clipper class
* Update: Improved documentation
v5.1.4 (24 March 2013)
* Update: CleanPolygon function enhanced.
* Update: Documentation improved.
v5.1.3 (14 March 2013)
* Bugfix: Minor bugfixes.
* Update: Documentation significantly improved.
v5.1.2 (26 February 2013)
* Bugfix: PolyNode class was missing a constructor.
* Update: The MiterLimit parameter in the OffsetPolygons
function has been renamed Limit and can now also be used to
limit the number of vertices used to construct arcs when
JoinType is set to jtRound.
v5.1.0 (17 February 2013)
* Update: ExPolygons has been replaced with the PolyTree &
PolyNode classes to more fully represent the parent-child
relationships of the polygons returned by Clipper.
* Added: New CleanPolygon and CleanPolygons functions.
* Bugfix: Another orientation bug fixed.
v5.0.2 - 30 December 2012
* Bugfix: Significant fixes in and tidy of the internal
Int128 class (which is used only when polygon coordinate
values are greater than ±0x3FFFFFFF (~1.07e9)).
* Update: The Area algorithm has been updated and is faster.
* Update: Documentation updates. The newish but undocumented
'CheckInputs' parameter of the OffsetPolygons function has been
renamed 'AutoFix' and documented too. The comments on rounding
have also been improved (ie clearer and expanded).
v4.10.0 - 25 December 2012
* Bugfix: Orientation bugs should now be resolved (finally!).
* Bugfix: Bug in Int128 class
v4.9.8 - 2 December 2012
* Bugfix: Further fixes to rare Orientation bug.
v4.9.7 - 29 November 2012
* Bugfix: Bug that very rarely returned the wrong polygon
orientation.
* Bugfix: Obscure bug affecting OffsetPolygons when using
jtRound for the JoinType parameter and when polygons also
contain very large coordinate values (> +/-100000000000).
v4.9.6 - 9 November 2012
* Bugfix: Another obscure bug related to joining polygons.
v4.9.4 - 2 November 2012
* Bugfix: Bugs in Int128 class occasionally causing
wrong orientations.
* Bugfix: Further fixes related to joining polygons.
v4.9.0 - 9 October 2012
* Bugfix: Obscure bug related to joining polygons.
v4.8.9 - 25 September 2012
* Bugfix: Obscure bug related to precision of intersections.
v4.8.8 - 30 August 2012
* Bugfix: Fixed bug in OffsetPolygons function introduced in
version 4.8.5.
v4.8.7 - 24 August 2012
* Bugfix: ReversePolygon function in C++ translation was broken.
* Bugfix: Two obscure bugs affecting orientation fixed too.
v4.8.6 - 11 August 2012
* Bugfix: Potential for memory overflow errors when using
ExPolygons structure.
* Bugfix: The polygon coordinate range has been reduced to
+/- 0x3FFFFFFFFFFFFFFF (4.6e18).
* Update: ReversePolygons function was misnamed ReversePoints in C++.
* Update: SimplifyPolygon function now takes a PolyFillType parameter.
v4.8.5 - 15 July 2012
* Bugfix: Potential for memory overflow errors in OffsetPolygons().
v4.8.4 - 1 June 2012
* Bugfix: Another obscure bug affecting ExPolygons structure.
v4.8.3 - 27 May 2012
* Bugfix: Obscure bug causing incorrect removal of a vertex.
v4.8.2 - 21 May 2012
* Bugfix: Obscure bug could cause an exception when using
ExPolygon structure.
v4.8.1 - 12 May 2012
* Update: Cody tidy and minor bug fixes.
v4.8.0 - 30 April 2012
* Bugfix: Occasional errors in orientation fixed.
* Update: Added notes on rounding to the documentation.
v4.7.6 - 11 April 2012
* Fixed a bug in Orientation function (affecting C# translations only).
* Minor documentation update.
v4.7.5 - 28 March 2012
* Bugfix: Fixed a recently introduced bug that occasionally caused an
unhandled exception in C++ and C# translations.
v4.7.4 - 15 March 2012
* Bugfix: Another minor bugfix.
v4.7.2 - 4 March 2012
* Bugfix: Fixed bug introduced in ver 4.7 which sometimes caused
an exception if ExPolygon structure was passed to Clipper's
Execute method.
v4.7.1 - 3 March 2012
* Bugfix: Rare crash when JoinCommonEdges joined polygons that
'cancelled' each other.
* Bugfix: Clipper's internal Orientation method occasionally
returned wrong result.
* Update: Improved C# code (thanks to numerous excellent suggestions
from David Piepgrass)
v4.7 - 10 February 2012
* Improved the joining of output polygons sharing a common edge.
v4.6.6 - 3 February 2012
* Bugfix: Another obscure bug occasionally causing incorrect
polygon orientation.
v4.6.5 - 17 January 2012
* Bugfix: Obscure bug occasionally causing incorrect hole
assignment in ExPolygon structure.
v4.6.4 - 8 November 2011
* Added: SimplifyPolygon and SimplifyPolygons functions.
v4.6.3 - 11 November 2011
* Bugfix: Fixed another minor mitering bug in OffsetPolygons.
v4.6.2 - 10 November 2011
* Bugfix: Fixed a rare bug in the orientation of polygons
returned by Clipper's Execute() method.
* Bugfix: Previous update introduced a mitering bug in the
OffsetPolygons function.
v4.6 - 29 October 2011
* Added: Support for Positive and Negative polygon fill
types (in addition to the EvenOdd and NonZero fill types).
* Bugfix: The OffsetPolygons function was generating the
occasional artefact when 'shrinking' polygons.
v4.5.5 - 8 October 2011
* Bugfix: Fixed an obscure bug in Clipper's JoinCommonEdges
method.
* Update: Replaced IsClockwise function with Orientation
function. The orientation issues affecting OffsetPolygons
should now be finally resolved.
* Change: The Area function once again returns a signed value.
v4.5.1 - 28 September 2011
* Deleted: The UseFullCoordinateRange property has been
deleted since integer range is now managed implicitly.
* BugFix: Minor bug in OffsetPolygon mitering.
* Change: C# JoinType enum moved from Clipper class to
ClipperLib namespace.
* Change: The Area function now returns the absolute area
(irrespective of orientation).
* Change: The IsClockwise function now requires a second
parameter - YAxisPositiveUpward - to accommodate displays
with Y-axis oriented in either direction
v4.4.4 - 10 September 2011
* Change: Deleted jtButt from JoinType (used by the
OffsetPolygons function).
* BugFix: Fixed another minor bug in OffsetPolygons function.
* Update: Further improvements to the help file
v4.4.3 - 29 August 2011
* BugFix: fixed a minor rounding issue in OffsetPolygons
function (affected C++ & C# translations).
* BugFix: fixed a minor bug in OffsetPolygons' function
declaration (affected C++ translation only).
* Change: 'clipper' namespace changed to 'ClipperLib'
namespace in both C++ and C# code to remove the ambiguity
between the Clipper class and the namespace. (This also
required numerous updates to the accompanying demos.)
v4.4.2 - 26 August 2011
* BugFix: minor bugfixes in Clipper.
* Update: the OffsetPolygons function has been significantly
improved by offering 4 different join styles.
v4.4.0 - 6 August 2011
* BugFix: A number of minor bugs have been fixed that mostly
affected the new ExPolygons structure.
v4.3.0 - 17 June 2011
* New: ExPolygons structure that explicitly associates 'hole'
polygons with their 'outer' container polygons.
* New: Execute method overloaded so the solution parameter
can now be either Polygons or ExPolygons.
* BugFix: Fixed a rare bug in solution polygons orientation.
v4.2.8 - 21 May 2011
* Update: JoinCommonEdges() improved once more.
* BugFix: Several minor bugs fixed.
v4.2.6 - 1 May 2011
* Bugfix: minor bug in SlopesEqual function.
* Update: Merging of output polygons sharing common edges
has been significantly inproved
v4.2.4 - 26 April 2011
Input polygon coordinates can now contain the full range of
signed 64bit integers (ie +/-9,223,372,036,854,775,807). This
means that floating point values can be converted to and from
Clipper's 64bit integer coordinates structure (IntPoint) and
still retain a precision of up to 18 decimal places. However,
since the large-integer math that supports this expanded range
imposes a small cost on performance (~15%), a new property
UseFullCoordinateRange has been added to the Clipper class to
allow users the choice of whether or not to use this expanded
coordinate range. If this property is disabled, coordinate values
are restricted to +/-1,500,000,000.
v4.2 - 12 April 2011
JoinCommonEdges() code significantly improved plus other minor
improvements.
v4.1.2 - 9 April 2011
* Update: Minor code tidy.
* Bugfix: Possible endless loop in JoinCommonEdges() in clipper.pas.
v4.1.1 - 8 April 2011
* Update: All polygon coordinates are now stored as 64bit integers
(though they're still restricted to range -1.5e9 to +1.5e9 pending
the inclusion of code supporting 64bit math).
* Change: AddPolygon and AddPolygons methods now return boolean
values.
* Bugfix: Bug in JoinCommonEdges() caused potential endless loop.
* Bugfix: Bug in IsClockwise(). (C++ code only)
v4.0 - 5 April 2011
* Clipper 4 is a major rewrite of earlier versions. The biggest
change is that floating point values are no longer used,
except for the storing of edge slope values. The main benefit
of this is the issue of numerical robustness has been
addressed. Due to other major code improvements Clipper v4
is approximately 40% faster than Clipper v3.
* The AddPolyPolygon method has been renamed to AddPolygons.
* The IgnoreOrientation property has been removed.
* The clipper_misc library has been merged back into the
main clipper library.
v3.1.0 - 17 February 2011
* Bugfix: Obscure bug in TClipperBase.SetDx method that caused
problems with very small edges ( edges <1/1000th pixel in size).
v3.0.3 - 9 February 2011
* Bugfix: Significant bug, but only in C# code.
* Update: Minor refactoring.
v3.0 - 31 January 2011
* Update: Major rewrite of the portion of code that calculates
the output polygons' orientation.
* Update: Help file significantly improved.
* Change: Renamed ForceOrientation property to IgnoreOrientation.
If the orientation of output polygons is not important, or can
be managed separately, clipping routines can be sped up by about
60% by setting IgnoreOrientation to true. Defaults to false.
* Change: The OffsetPolygon and Area functions have been moved to
the new unit - clipper_misc.
2.99 - 15 January 2011
* Bugfix: Obscure bug in AddPolygon method could cause an endless loop.
2.8 - 20 November 2010
* Updated: Output polygons which previously shared a common
edge are now merged.
* Changed: The orientation of outer polygons is now clockwise
when the display's Y axis is positive downwards (as is
typical for most Windows applications). Inner polygons
(holes) have the opposite orientation.
* Added: Support module for Cairo Graphics Library (with demo).
* Updated: C# and C++ demos.
2.522 - 15 October 2010
* Added C# translation (thanks to Olivier Lejeune) and
a link to Ruby bindings (thanks to Mike Owens).
2.0 - 30 July 2010
* Clipper now clips using both the Even-Odd (alternate) and
Non-Zero (winding) polygon filling rules. (Previously Clipper
assumed the Even-Odd rule for polygon filling.)
1.4c - 16 June 2010
* Added C++ support for AGG graphics library
1.2s - 2 June 2010
* Added C++ translation of clipper.pas
=====================================================================
Clipper Change Log
=====================================================================
v6.1.3 (19 January 2014)
* Fixed potential endless loop condition when adding open
paths to Clipper.
* Fixed missing implementation of SimplifyPolygon function
in C++ code.
* Fixed incorrect upper range constant for polygon coordinates
in Delphi code.
* Added PointInPolygon function.
* Overloaded MinkowskiSum function to accommodate multi-contour
paths.
v6.1.2 (15 December 2013)
* Fixed broken C++ header file.
* Minor improvement to joining polygons.
v6.1.1 (13 December 2013)
* Fixed a couple of bugs affecting open paths that could
raise unhandled exceptions.
v6.1.0 (12 December 2013)
* Deleted: Previously deprecated code has been removed.
* Modified: The OffsetPaths function is now deprecated as it has
been replaced by the ClipperOffset class which is much more
flexible.
* Bugfixes: Several minor bugs have been fixed including
occasionally an incorrect nesting within the PolyTree structure.
v6.0.0 (30 October 2013)
* Added: Open path (polyline) clipping. A new 'Curves' demo
application showcases this (see the 'Curves' directory).
* Update: Major improvement in the merging of
shared/collinear edges in clip solutions (see Execute).
* Added: The IntPoint structure now has an optional 'Z' member.
(See the precompiler directive use_xyz.)
* Added: Users can now force Clipper to use 32bit integers
(via the precompiler directive use_int32) instead of using
64bit integers.
* Modified: To accommodate open paths, the Polygon and Polygons
structures have been renamed Path and Paths respectively. The
AddPolygon and AddPolygons methods of the ClipperBase class
have been renamed AddPath and AddPaths respectively. Several
other functions have been similarly renamed.
* Modified: The PolyNode Class has a new IsOpen property.
* Modified: The Clipper class has a new ZFillFunction property.
* Added: MinkowskiSum and MinkowskiDiff functions added.
* Added: Several other new functions have been added including
PolyTreeToPaths, OpenPathsFromPolyTree and ClosedPathsFromPolyTree.
* Added: The Clipper constructor now accepts an optional InitOptions
parameter to simplify setting properties.
* Bugfixes: Numerous minor bugs have been fixed.
* Deprecated: Version 6 is a major upgrade from previous versions
and quite a number of changes have been made to exposed structures
and functions. To minimize inconvenience to existing library users,
some code has been retained and some added to maintain backward
compatibility. However, because this code will be removed in a
future update, it has been marked as deprecated and a precompiler
directive use_deprecated has been defined.
v5.1.6 (23 May 2013)
* BugFix: CleanPolygon function was buggy.
* Changed: The behaviour of the 'miter' JoinType has been
changed so that when squaring occurs, it's no longer
extended up to the miter limit but is squared off at
exactly 'delta' units. (This improves the look of mitering
with larger limits at acute angles.)
* Added: New OffsetPolyLines function
* Update: Minor code refactoring and optimisations
v5.1.5 (5 May 2013)
* Added: ForceSimple property to Clipper class
* Update: Improved documentation
v5.1.4 (24 March 2013)
* Update: CleanPolygon function enhanced.
* Update: Documentation improved.
v5.1.3 (14 March 2013)
* Bugfix: Minor bugfixes.
* Update: Documentation significantly improved.
v5.1.2 (26 February 2013)
* Bugfix: PolyNode class was missing a constructor.
* Update: The MiterLimit parameter in the OffsetPolygons
function has been renamed Limit and can now also be used to
limit the number of vertices used to construct arcs when
JoinType is set to jtRound.
v5.1.0 (17 February 2013)
* Update: ExPolygons has been replaced with the PolyTree &
PolyNode classes to more fully represent the parent-child
relationships of the polygons returned by Clipper.
* Added: New CleanPolygon and CleanPolygons functions.
* Bugfix: Another orientation bug fixed.
v5.0.2 - 30 December 2012
* Bugfix: Significant fixes in and tidy of the internal
Int128 class (which is used only when polygon coordinate
values are greater than ±0x3FFFFFFF (~1.07e9)).
* Update: The Area algorithm has been updated and is faster.
* Update: Documentation updates. The newish but undocumented
'CheckInputs' parameter of the OffsetPolygons function has been
renamed 'AutoFix' and documented too. The comments on rounding
have also been improved (ie clearer and expanded).
v4.10.0 - 25 December 2012
* Bugfix: Orientation bugs should now be resolved (finally!).
* Bugfix: Bug in Int128 class
v4.9.8 - 2 December 2012
* Bugfix: Further fixes to rare Orientation bug.
v4.9.7 - 29 November 2012
* Bugfix: Bug that very rarely returned the wrong polygon
orientation.
* Bugfix: Obscure bug affecting OffsetPolygons when using
jtRound for the JoinType parameter and when polygons also
contain very large coordinate values (> +/-100000000000).
v4.9.6 - 9 November 2012
* Bugfix: Another obscure bug related to joining polygons.
v4.9.4 - 2 November 2012
* Bugfix: Bugs in Int128 class occasionally causing
wrong orientations.
* Bugfix: Further fixes related to joining polygons.
v4.9.0 - 9 October 2012
* Bugfix: Obscure bug related to joining polygons.
v4.8.9 - 25 September 2012
* Bugfix: Obscure bug related to precision of intersections.
v4.8.8 - 30 August 2012
* Bugfix: Fixed bug in OffsetPolygons function introduced in
version 4.8.5.
v4.8.7 - 24 August 2012
* Bugfix: ReversePolygon function in C++ translation was broken.
* Bugfix: Two obscure bugs affecting orientation fixed too.
v4.8.6 - 11 August 2012
* Bugfix: Potential for memory overflow errors when using
ExPolygons structure.
* Bugfix: The polygon coordinate range has been reduced to
+/- 0x3FFFFFFFFFFFFFFF (4.6e18).
* Update: ReversePolygons function was misnamed ReversePoints in C++.
* Update: SimplifyPolygon function now takes a PolyFillType parameter.
v4.8.5 - 15 July 2012
* Bugfix: Potential for memory overflow errors in OffsetPolygons().
v4.8.4 - 1 June 2012
* Bugfix: Another obscure bug affecting ExPolygons structure.
v4.8.3 - 27 May 2012
* Bugfix: Obscure bug causing incorrect removal of a vertex.
v4.8.2 - 21 May 2012
* Bugfix: Obscure bug could cause an exception when using
ExPolygon structure.
v4.8.1 - 12 May 2012
* Update: Cody tidy and minor bug fixes.
v4.8.0 - 30 April 2012
* Bugfix: Occasional errors in orientation fixed.
* Update: Added notes on rounding to the documentation.
v4.7.6 - 11 April 2012
* Fixed a bug in Orientation function (affecting C# translations only).
* Minor documentation update.
v4.7.5 - 28 March 2012
* Bugfix: Fixed a recently introduced bug that occasionally caused an
unhandled exception in C++ and C# translations.
v4.7.4 - 15 March 2012
* Bugfix: Another minor bugfix.
v4.7.2 - 4 March 2012
* Bugfix: Fixed bug introduced in ver 4.7 which sometimes caused
an exception if ExPolygon structure was passed to Clipper's
Execute method.
v4.7.1 - 3 March 2012
* Bugfix: Rare crash when JoinCommonEdges joined polygons that
'cancelled' each other.
* Bugfix: Clipper's internal Orientation method occasionally
returned wrong result.
* Update: Improved C# code (thanks to numerous excellent suggestions
from David Piepgrass)
v4.7 - 10 February 2012
* Improved the joining of output polygons sharing a common edge.
v4.6.6 - 3 February 2012
* Bugfix: Another obscure bug occasionally causing incorrect
polygon orientation.
v4.6.5 - 17 January 2012
* Bugfix: Obscure bug occasionally causing incorrect hole
assignment in ExPolygon structure.
v4.6.4 - 8 November 2011
* Added: SimplifyPolygon and SimplifyPolygons functions.
v4.6.3 - 11 November 2011
* Bugfix: Fixed another minor mitering bug in OffsetPolygons.
v4.6.2 - 10 November 2011
* Bugfix: Fixed a rare bug in the orientation of polygons
returned by Clipper's Execute() method.
* Bugfix: Previous update introduced a mitering bug in the
OffsetPolygons function.
v4.6 - 29 October 2011
* Added: Support for Positive and Negative polygon fill
types (in addition to the EvenOdd and NonZero fill types).
* Bugfix: The OffsetPolygons function was generating the
occasional artefact when 'shrinking' polygons.
v4.5.5 - 8 October 2011
* Bugfix: Fixed an obscure bug in Clipper's JoinCommonEdges
method.
* Update: Replaced IsClockwise function with Orientation
function. The orientation issues affecting OffsetPolygons
should now be finally resolved.
* Change: The Area function once again returns a signed value.
v4.5.1 - 28 September 2011
* Deleted: The UseFullCoordinateRange property has been
deleted since integer range is now managed implicitly.
* BugFix: Minor bug in OffsetPolygon mitering.
* Change: C# JoinType enum moved from Clipper class to
ClipperLib namespace.
* Change: The Area function now returns the absolute area
(irrespective of orientation).
* Change: The IsClockwise function now requires a second
parameter - YAxisPositiveUpward - to accommodate displays
with Y-axis oriented in either direction
v4.4.4 - 10 September 2011
* Change: Deleted jtButt from JoinType (used by the
OffsetPolygons function).
* BugFix: Fixed another minor bug in OffsetPolygons function.
* Update: Further improvements to the help file
v4.4.3 - 29 August 2011
* BugFix: fixed a minor rounding issue in OffsetPolygons
function (affected C++ & C# translations).
* BugFix: fixed a minor bug in OffsetPolygons' function
declaration (affected C++ translation only).
* Change: 'clipper' namespace changed to 'ClipperLib'
namespace in both C++ and C# code to remove the ambiguity
between the Clipper class and the namespace. (This also
required numerous updates to the accompanying demos.)
v4.4.2 - 26 August 2011
* BugFix: minor bugfixes in Clipper.
* Update: the OffsetPolygons function has been significantly
improved by offering 4 different join styles.
v4.4.0 - 6 August 2011
* BugFix: A number of minor bugs have been fixed that mostly
affected the new ExPolygons structure.
v4.3.0 - 17 June 2011
* New: ExPolygons structure that explicitly associates 'hole'
polygons with their 'outer' container polygons.
* New: Execute method overloaded so the solution parameter
can now be either Polygons or ExPolygons.
* BugFix: Fixed a rare bug in solution polygons orientation.
v4.2.8 - 21 May 2011
* Update: JoinCommonEdges() improved once more.
* BugFix: Several minor bugs fixed.
v4.2.6 - 1 May 2011
* Bugfix: minor bug in SlopesEqual function.
* Update: Merging of output polygons sharing common edges
has been significantly inproved
v4.2.4 - 26 April 2011
Input polygon coordinates can now contain the full range of
signed 64bit integers (ie +/-9,223,372,036,854,775,807). This
means that floating point values can be converted to and from
Clipper's 64bit integer coordinates structure (IntPoint) and
still retain a precision of up to 18 decimal places. However,
since the large-integer math that supports this expanded range
imposes a small cost on performance (~15%), a new property
UseFullCoordinateRange has been added to the Clipper class to
allow users the choice of whether or not to use this expanded
coordinate range. If this property is disabled, coordinate values
are restricted to +/-1,500,000,000.
v4.2 - 12 April 2011
JoinCommonEdges() code significantly improved plus other minor
improvements.
v4.1.2 - 9 April 2011
* Update: Minor code tidy.
* Bugfix: Possible endless loop in JoinCommonEdges() in clipper.pas.
v4.1.1 - 8 April 2011
* Update: All polygon coordinates are now stored as 64bit integers
(though they're still restricted to range -1.5e9 to +1.5e9 pending
the inclusion of code supporting 64bit math).
* Change: AddPolygon and AddPolygons methods now return boolean
values.
* Bugfix: Bug in JoinCommonEdges() caused potential endless loop.
* Bugfix: Bug in IsClockwise(). (C++ code only)
v4.0 - 5 April 2011
* Clipper 4 is a major rewrite of earlier versions. The biggest
change is that floating point values are no longer used,
except for the storing of edge slope values. The main benefit
of this is the issue of numerical robustness has been
addressed. Due to other major code improvements Clipper v4
is approximately 40% faster than Clipper v3.
* The AddPolyPolygon method has been renamed to AddPolygons.
* The IgnoreOrientation property has been removed.
* The clipper_misc library has been merged back into the
main clipper library.
v3.1.0 - 17 February 2011
* Bugfix: Obscure bug in TClipperBase.SetDx method that caused
problems with very small edges ( edges <1/1000th pixel in size).
v3.0.3 - 9 February 2011
* Bugfix: Significant bug, but only in C# code.
* Update: Minor refactoring.
v3.0 - 31 January 2011
* Update: Major rewrite of the portion of code that calculates
the output polygons' orientation.
* Update: Help file significantly improved.
* Change: Renamed ForceOrientation property to IgnoreOrientation.
If the orientation of output polygons is not important, or can
be managed separately, clipping routines can be sped up by about
60% by setting IgnoreOrientation to true. Defaults to false.
* Change: The OffsetPolygon and Area functions have been moved to
the new unit - clipper_misc.
2.99 - 15 January 2011
* Bugfix: Obscure bug in AddPolygon method could cause an endless loop.
2.8 - 20 November 2010
* Updated: Output polygons which previously shared a common
edge are now merged.
* Changed: The orientation of outer polygons is now clockwise
when the display's Y axis is positive downwards (as is
typical for most Windows applications). Inner polygons
(holes) have the opposite orientation.
* Added: Support module for Cairo Graphics Library (with demo).
* Updated: C# and C++ demos.
2.522 - 15 October 2010
* Added C# translation (thanks to Olivier Lejeune) and
a link to Ruby bindings (thanks to Mike Owens).
2.0 - 30 July 2010
* Clipper now clips using both the Even-Odd (alternate) and
Non-Zero (winding) polygon filling rules. (Previously Clipper
assumed the Even-Odd rule for polygon filling.)
1.4c - 16 June 2010
* Added C++ support for AGG graphics library
1.2s - 2 June 2010
* Added C++ translation of clipper.pas
1.0 - 9 May 2010
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+398 -398
Ver Arquivo
@@ -1,398 +1,398 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.1.3"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
#include <vector>
#include <set>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
#include <functional>
namespace ClipperLib {
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip };
//By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
//see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
typedef unsigned int cUInt;
#else
typedef signed long long cInt;
typedef unsigned long long cUInt;
#endif
struct IntPoint {
cInt X;
cInt Y;
#ifdef use_xyz
cInt Z;
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
#else
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
#endif
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
{
return a.X == b.X && a.Y == b.Y;
}
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
{
return a.X != b.X || a.Y != b.Y;
}
};
//------------------------------------------------------------------------------
typedef std::vector< IntPoint > Path;
typedef std::vector< Path > Paths;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
std::ostream& operator <<(std::ostream &s, const Path &p);
std::ostream& operator <<(std::ostream &s, const Paths &p);
struct DoublePoint
{
double X;
double Y;
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
};
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
class PolyNode
{
public:
PolyNode();
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
PolyNode* GetNext() const;
bool IsHole() const;
bool IsOpen() const;
int ChildCount() const;
private:
unsigned Index; //node index in Parent.Childs
bool m_IsOpen;
JoinType m_jointype;
EndType m_endtype;
PolyNode* GetNextSiblingUp() const;
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
friend class ClipperOffset;
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};
bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415);
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
void ReversePath(Path& p);
void ReversePaths(Paths& p);
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
//enums that are used internally ...
enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinima;
struct Scanbeam;
struct OutPt;
struct OutRec;
struct Join;
typedef std::vector < OutRec* > PolyOutList;
typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima list.
class ClipperBase
{
public:
ClipperBase();
virtual ~ClipperBase();
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
virtual void Clear();
IntRect GetBounds();
bool PreserveCollinear() {return m_PreserveCollinear;};
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
protected:
void DisposeLocalMinimaList();
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
bool m_HasOpenPaths;
};
//------------------------------------------------------------------------------
class Clipper : public virtual ClipperBase
{
public:
Clipper(int initOptions = 0);
~Clipper();
bool Execute(ClipType clipType,
Paths &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool ReverseSolution() {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool StrictlySimple() {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(TZFillCallback zFillFunc);
#endif
protected:
void Reset();
virtual bool ExecuteInternal();
private:
PolyOutList m_PolyOuts;
JoinList m_Joins;
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
TZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
bool IsEvenOddAltFillType(const TEdge& edge) const;
void InsertScanbeam(const cInt Y);
cInt PopScanbeam();
void InsertLocalMinimaIntoAEL(const cInt botY);
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
void AddEdgeToSEL(TEdge *edge);
void CopyAELToSEL();
void DeleteFromSEL(TEdge *e);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
void DisposeIntersectNodes();
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec);
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
void ClearJoins();
void ClearGhostJoins();
void AddGhostJoin(OutPt *op, const IntPoint offPt);
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e);
#endif
};
//------------------------------------------------------------------------------
class ClipperOffset
{
public:
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
~ClipperOffset();
void AddPath(const Path& path, JoinType joinType, EndType endType);
void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
void Execute(Paths& solution, double delta);
void Execute(PolyTree& solution, double delta);
void Clear();
double MiterLimit;
double ArcTolerance;
private:
Paths m_destPolys;
Path m_srcPoly;
Path m_destPoly;
std::vector<DoublePoint> m_normals;
double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad;
IntPoint m_lowest;
PolyNode m_polyNodes;
void FixOrientations();
void DoOffset(double delta);
void OffsetPoint(int j, int& k, JoinType jointype);
void DoSquare(int j, int k);
void DoMiter(int j, int k, double r);
void DoRound(int j, int k);
};
//------------------------------------------------------------------------------
class clipperException : public std::exception
{
public:
clipperException(const char* description): m_descr(description) {}
virtual ~clipperException() throw() {}
virtual const char* what() const throw() {return m_descr.c_str();}
private:
std::string m_descr;
};
//------------------------------------------------------------------------------
} //ClipperLib namespace
#endif //clipper_hpp
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.1.3"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
#include <vector>
#include <set>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
#include <functional>
namespace ClipperLib {
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip };
//By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
//see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
typedef unsigned int cUInt;
#else
typedef signed long long cInt;
typedef unsigned long long cUInt;
#endif
struct IntPoint {
cInt X;
cInt Y;
#ifdef use_xyz
cInt Z;
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
#else
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
#endif
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
{
return a.X == b.X && a.Y == b.Y;
}
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
{
return a.X != b.X || a.Y != b.Y;
}
};
//------------------------------------------------------------------------------
typedef std::vector< IntPoint > Path;
typedef std::vector< Path > Paths;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
std::ostream& operator <<(std::ostream &s, const Path &p);
std::ostream& operator <<(std::ostream &s, const Paths &p);
struct DoublePoint
{
double X;
double Y;
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
};
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
class PolyNode
{
public:
PolyNode();
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
PolyNode* GetNext() const;
bool IsHole() const;
bool IsOpen() const;
int ChildCount() const;
private:
unsigned Index; //node index in Parent.Childs
bool m_IsOpen;
JoinType m_jointype;
EndType m_endtype;
PolyNode* GetNextSiblingUp() const;
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
friend class ClipperOffset;
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};
bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415);
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
void ReversePath(Path& p);
void ReversePaths(Paths& p);
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
//enums that are used internally ...
enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinima;
struct Scanbeam;
struct OutPt;
struct OutRec;
struct Join;
typedef std::vector < OutRec* > PolyOutList;
typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima list.
class ClipperBase
{
public:
ClipperBase();
virtual ~ClipperBase();
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
virtual void Clear();
IntRect GetBounds();
bool PreserveCollinear() {return m_PreserveCollinear;};
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
protected:
void DisposeLocalMinimaList();
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
bool m_HasOpenPaths;
};
//------------------------------------------------------------------------------
class Clipper : public virtual ClipperBase
{
public:
Clipper(int initOptions = 0);
~Clipper();
bool Execute(ClipType clipType,
Paths &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool ReverseSolution() {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool StrictlySimple() {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(TZFillCallback zFillFunc);
#endif
protected:
void Reset();
virtual bool ExecuteInternal();
private:
PolyOutList m_PolyOuts;
JoinList m_Joins;
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
TZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
bool IsEvenOddAltFillType(const TEdge& edge) const;
void InsertScanbeam(const cInt Y);
cInt PopScanbeam();
void InsertLocalMinimaIntoAEL(const cInt botY);
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
void AddEdgeToSEL(TEdge *edge);
void CopyAELToSEL();
void DeleteFromSEL(TEdge *e);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
void DisposeIntersectNodes();
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec);
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
void ClearJoins();
void ClearGhostJoins();
void AddGhostJoin(OutPt *op, const IntPoint offPt);
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e);
#endif
};
//------------------------------------------------------------------------------
class ClipperOffset
{
public:
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
~ClipperOffset();
void AddPath(const Path& path, JoinType joinType, EndType endType);
void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
void Execute(Paths& solution, double delta);
void Execute(PolyTree& solution, double delta);
void Clear();
double MiterLimit;
double ArcTolerance;
private:
Paths m_destPolys;
Path m_srcPoly;
Path m_destPoly;
std::vector<DoublePoint> m_normals;
double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad;
IntPoint m_lowest;
PolyNode m_polyNodes;
void FixOrientations();
void DoOffset(double delta);
void OffsetPoint(int j, int& k, JoinType jointype);
void DoSquare(int j, int k);
void DoMiter(int j, int k, double r);
void DoRound(int j, int k);
};
//------------------------------------------------------------------------------
class clipperException : public std::exception
{
public:
clipperException(const char* description): m_descr(description) {}
virtual ~clipperException() throw() {}
virtual const char* what() const throw() {return m_descr.c_str();}
private:
std::string m_descr;
};
//------------------------------------------------------------------------------
} //ClipperLib namespace
#endif //clipper_hpp
-95
Ver Arquivo
@@ -1,95 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
void PathOrderOptimizer::optimize()
{
std::vector<bool> picked;
for(unsigned int i=0;i<polygons.size(); i++)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
PolygonRef poly = polygons[i];
for(unsigned int j=0; j<poly.size(); j++)
{
float dist = vSize2f(poly[j] - startPoint);
if (dist < bestDist)
{
best = j;
bestDist = dist;
}
}
polyStart.push_back(best);
picked.push_back(false);
}
Point p0 = startPoint;
for(unsigned int n=0; n<polygons.size(); n++)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
for(unsigned int i=0;i<polygons.size(); i++)
{
if (picked[i] || polygons[i].size() < 1)
continue;
if (polygons[i].size() == 2)
{
float dist = vSize2f(polygons[i][0] - p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 0;
}
dist = vSize2f(polygons[i][1] - p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 1;
}
}else{
float dist = vSize2f(polygons[i][polyStart[i]] - p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
}
}
}
if (best > -1)
{
if (polygons[best].size() == 2)
{
p0 = polygons[best][(polyStart[best] + 1) % 2];
}else{
p0 = polygons[best][polyStart[best]];
}
picked[best] = true;
polyOrder.push_back(best);
}
}
p0 = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++)
{
int nr = polyOrder[n];
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
for(unsigned int i=0;i<polygons[nr].size(); i++)
{
float dist = vSize2f(polygons[nr][i] - p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
}
}
polyStart[nr] = best;
if (polygons[nr].size() <= 2)
{
p0 = polygons[nr][(best + 1) % 2];
}else{
p0 = polygons[nr][best];
}
}
}
+59 -56
Ver Arquivo
@@ -1,56 +1,59 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "bridge.h"
#include "utils/polygondebug.h"
int bridgeAngle(SliceLayerPart* part, SliceLayer* prevLayer)
{
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
// This gives us the islands that the layer rests on.
Polygons islands;
for(unsigned int n=0; n<prevLayer->parts.size(); n++)
{
if (!part->boundaryBox.hit(prevLayer->parts[n].boundaryBox)) continue;
islands.add(part->outline.intersection(prevLayer->parts[n].outline));
}
if (islands.size() > 5)
return -1;
//Next find the 2 largest islands that we rest on.
double area1 = 0;
double area2 = 0;
int idx1 = -1;
int idx2 = -1;
for(unsigned int n=0; n<islands.size(); n++)
{
//Skip internal holes
if (!islands[n].orientation())
continue;
double area = fabs(islands[n].area());
if (area > area1)
{
if (area1 > area2)
{
area2 = area1;
idx2 = idx1;
}
area1 = area;
idx1 = n;
}else if (area > area2)
{
area2 = area;
idx2 = n;
}
}
if (idx1 < 0 || idx2 < 0)
return -1;
Point center1 = islands[idx1].centerOfMass();
Point center2 = islands[idx2].centerOfMass();
double angle = atan2(center2.X - center1.X, center2.Y - center1.Y) / M_PI * 180;
if (angle < 0) angle += 360;
return angle;
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "bridge.h"
namespace cura {
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
{
AABB boundaryBox(outline);
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
// This gives us the islands that the layer rests on.
Polygons islands;
for(auto prevLayerPart : prevLayer->parts)
{
if (!boundaryBox.hit(prevLayerPart.boundaryBox))
continue;
islands.add(outline.intersection(prevLayerPart.outline));
}
if (islands.size() > 5 || islands.size() < 1)
return -1;
//Next find the 2 largest islands that we rest on.
double area1 = 0;
double area2 = 0;
int idx1 = -1;
int idx2 = -1;
for(unsigned int n=0; n<islands.size(); n++)
{
//Skip internal holes
if (!islands[n].orientation())
continue;
double area = fabs(islands[n].area());
if (area > area1)
{
if (area1 > area2)
{
area2 = area1;
idx2 = idx1;
}
area1 = area;
idx1 = n;
}else if (area > area2)
{
area2 = area;
idx2 = n;
}
}
if (idx1 < 0 || idx2 < 0)
return -1;
Point center1 = islands[idx1].centerOfMass();
Point center2 = islands[idx2].centerOfMass();
return angle(center2 - center1);
}
}//namespace cura
+13 -9
Ver Arquivo
@@ -1,9 +1,13 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef BRIDGE_H
#define BRIDGE_H
#include "sliceDataStorage.h"
int bridgeAngle(SliceLayerPart* part, SliceLayer* prevLayer);
#endif//BRIDGE_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef BRIDGE_H
#define BRIDGE_H
#include "sliceDataStorage.h"
namespace cura {
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
}//namespace cura
#endif//BRIDGE_H
+237 -256
Ver Arquivo
@@ -1,256 +1,237 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "comb.h"
bool Comb::preTest(Point startPoint, Point endPoint)
{
return collisionTest(startPoint, endPoint);
}
bool Comb::collisionTest(Point startPoint, Point endPoint)
{
Point diff = endPoint - startPoint;
matrix = PointMatrix(diff);
sp = matrix.apply(startPoint);
ep = matrix.apply(endPoint);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x > sp.X && x < ep.X)
return true;
}
p0 = p1;
}
}
return false;
}
void Comb::calcMinMax()
{
for(unsigned int n=0; n<boundery.size(); n++)
{
minX[n] = LLONG_MAX;
maxX[n] = LLONG_MIN;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= sp.X && x <= ep.X)
{
if (x < minX[n]) { minX[n] = x; minIdx[n] = i; }
if (x > maxX[n]) { maxX[n] = x; maxIdx[n] = i; }
}
}
p0 = p1;
}
}
}
unsigned int Comb::getPolygonAbove(int64_t x)
{
int64_t min = LLONG_MAX;
unsigned int ret = UINT_MAX;
for(unsigned int n=0; n<boundery.size(); n++)
{
if (minX[n] > x && minX[n] < min)
{
min = minX[n];
ret = n;
}
}
return ret;
}
Point Comb::getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx)
{
Point p0 = boundery[polygonNr][(idx > 0) ? (idx - 1) : (boundery[polygonNr].size() - 1)];
Point p1 = boundery[polygonNr][idx];
Point p2 = boundery[polygonNr][(idx < (boundery[polygonNr].size() - 1)) ? (idx + 1) : (0)];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0)));
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0)));
Point n = normal(off0 + off1, MM2INT(0.2));
return p1 + n;
}
Comb::Comb(Polygons& _boundery)
: boundery(_boundery)
{
minX = new int64_t[boundery.size()];
maxX = new int64_t[boundery.size()];
minIdx = new unsigned int[boundery.size()];
maxIdx = new unsigned int[boundery.size()];
}
Comb::~Comb()
{
delete[] minX;
delete[] maxX;
delete[] minIdx;
delete[] maxIdx;
}
bool Comb::checkInside(Point p)
{
//Check if we are inside the comb boundary. We do this by tracing from the point towards the negative X direction,
// every boundary we cross increments the crossings counter. If we have an even number of crossings then we are not inside the boundary
int crossings = 0;
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = boundery[n][boundery[n].size()-1];
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = boundery[n][i];
if ((p0.Y >= p.Y && p1.Y < p.Y) || (p1.Y > p.Y && p0.Y <= p.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (p.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= p.X)
crossings ++;
}
p0 = p1;
}
}
if ((crossings % 2) == 0)
return false;
return true;
}
bool Comb::moveInside(Point* p, int distance)
{
Point ret = *p;
int64_t bestDist = MM2INT(2.0) * MM2INT(2.0);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = boundery[n][boundery[n].size()-1];
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = boundery[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
int64_t distOnLine = dot(pDiff, *p - p0) / lineLength;
if (distOnLine < 10)
distOnLine = 10;
if (distOnLine > lineLength - 10)
distOnLine = lineLength - 10;
Point q = p0 + pDiff * distOnLine / lineLength;
int64_t dist = vSize2(q - *p);
if (dist < bestDist)
{
bestDist = dist;
ret = q + crossZ(normal(p1 - p0, distance));
}
p0 = p1;
}
}
if (bestDist < MM2INT(2.0) * MM2INT(2.0))
{
*p = ret;
return true;
}
return false;
}
bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, MM2INT(1.5)))
return true;
bool addEndpoint = false;
//Check if we are inside the comb boundaries
if (!checkInside(startPoint))
{
if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
combPoints.push_back(startPoint);
}
if (!checkInside(endPoint))
{
if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
addEndpoint = true;
}
//Check if we are crossing any bounderies, and pre-calculate some values.
if (!preTest(startPoint, endPoint))
{
//We're not crossing any boundaries. So skip the comb generation.
if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point.
return true;
}
//Calculate the minimum and maximum positions where we cross the comb boundary
calcMinMax();
int64_t x = sp.X;
vector<Point> pointList;
//Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
// to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross.
// This gives a path from the start to finish curved around the holes that it encounters.
while(true)
{
unsigned int n = getPolygonAbove(x);
if (n == UINT_MAX) break;
pointList.push_back(matrix.unapply(Point(minX[n] - MM2INT(0.2), sp.Y)));
if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size())
{
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}else{
minIdx[n]--;
if (minIdx[n] == UINT_MAX) minIdx[n] = boundery[n].size() - 1;
maxIdx[n]--;
if (maxIdx[n] == UINT_MAX) maxIdx[n] = boundery[n].size() - 1;
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}
pointList.push_back(matrix.unapply(Point(maxX[n] + MM2INT(0.2), sp.Y)));
x = maxX[n];
}
pointList.push_back(endPoint);
//Optimize the pointList, skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip any unneeded corners.
Point p0 = startPoint;
for(unsigned int n=1; n<pointList.size(); n++)
{
if (collisionTest(p0, pointList[n]))
{
if (collisionTest(p0, pointList[n-1]))
return false;
p0 = pointList[n-1];
combPoints.push_back(p0);
}
}
if (addEndpoint)
combPoints.push_back(endPoint);
return true;
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "comb.h"
namespace cura {
bool Comb::preTest(Point startPoint, Point endPoint)
{
return collisionTest(startPoint, endPoint);
}
bool Comb::collisionTest(Point startPoint, Point endPoint)
{
Point diff = endPoint - startPoint;
matrix = PointMatrix(diff);
sp = matrix.apply(startPoint);
ep = matrix.apply(endPoint);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x > sp.X && x < ep.X)
return true;
}
p0 = p1;
}
}
return false;
}
void Comb::calcMinMax()
{
for(unsigned int n=0; n<boundery.size(); n++)
{
minX[n] = INT64_MAX;
maxX[n] = INT64_MIN;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= sp.X && x <= ep.X)
{
if (x < minX[n]) { minX[n] = x; minIdx[n] = i; }
if (x > maxX[n]) { maxX[n] = x; maxIdx[n] = i; }
}
}
p0 = p1;
}
}
}
unsigned int Comb::getPolygonAbove(int64_t x)
{
int64_t min = POINT_MAX;
unsigned int ret = NO_INDEX;
for(unsigned int n=0; n<boundery.size(); n++)
{
if (minX[n] > x && minX[n] < min)
{
min = minX[n];
ret = n;
}
}
return ret;
}
Point Comb::getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx)
{
Point p0 = boundery[polygonNr][(idx > 0) ? (idx - 1) : (boundery[polygonNr].size() - 1)];
Point p1 = boundery[polygonNr][idx];
Point p2 = boundery[polygonNr][(idx < (boundery[polygonNr].size() - 1)) ? (idx + 1) : (0)];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0)));
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0)));
Point n = normal(off0 + off1, MM2INT(0.2));
return p1 + n;
}
Comb::Comb(Polygons& _boundery)
: boundery(_boundery)
{
minX = new int64_t[boundery.size()];
maxX = new int64_t[boundery.size()];
minIdx = new unsigned int[boundery.size()];
maxIdx = new unsigned int[boundery.size()];
}
Comb::~Comb()
{
delete[] minX;
delete[] maxX;
delete[] minIdx;
delete[] maxIdx;
}
bool Comb::moveInside(Point* p, int distance)
{
Point ret = *p;
int64_t bestDist = MM2INT(2.0) * MM2INT(2.0);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = boundery[n][boundery[n].size()-1];
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = boundery[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
int64_t distOnLine = dot(pDiff, *p - p0) / lineLength;
if (distOnLine < 10)
distOnLine = 10;
if (distOnLine > lineLength - 10)
distOnLine = lineLength - 10;
Point q = p0 + pDiff * distOnLine / lineLength;
int64_t dist = vSize2(q - *p);
if (dist < bestDist)
{
bestDist = dist;
ret = q + crossZ(normal(p1 - p0, distance));
}
p0 = p1;
}
}
if (bestDist < MM2INT(2.0) * MM2INT(2.0))
{
*p = ret;
return true;
}
return false;
}
bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, MM2INT(1.5)))
return true;
bool addEndpoint = false;
//Check if we are inside the comb boundaries
if (!boundery.inside(startPoint))
{
if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
combPoints.push_back(startPoint);
}
if (!boundery.inside(endPoint))
{
if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
addEndpoint = true;
}
//Check if we are crossing any bounderies, and pre-calculate some values.
if (!preTest(startPoint, endPoint))
{
//We're not crossing any boundaries. So skip the comb generation.
if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point.
return true;
}
//Calculate the minimum and maximum positions where we cross the comb boundary
calcMinMax();
int64_t x = sp.X;
vector<Point> pointList;
//Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
// to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross.
// This gives a path from the start to finish curved around the holes that it encounters.
while(true)
{
unsigned int n = getPolygonAbove(x);
if (n == NO_INDEX) break;
pointList.push_back(matrix.unapply(Point(minX[n] - MM2INT(0.2), sp.Y)));
if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size())
{
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}else{
if (minIdx[n] == 0)
minIdx[n] = boundery[n].size() - 1;
else
minIdx[n]--;
if (maxIdx[n] == 0)
maxIdx[n] = boundery[n].size() - 1;
else
maxIdx[n]--;
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}
pointList.push_back(matrix.unapply(Point(maxX[n] + MM2INT(0.2), sp.Y)));
x = maxX[n];
}
pointList.push_back(endPoint);
//Optimize the pointList, skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip any unneeded corners.
Point p0 = startPoint;
for(unsigned int n=1; n<pointList.size(); n++)
{
if (collisionTest(p0, pointList[n]))
{
if (collisionTest(p0, pointList[n-1]))
return false;
p0 = pointList[n-1];
combPoints.push_back(p0);
}
}
if (addEndpoint)
combPoints.push_back(endPoint);
return true;
}
}//namespace cura
+44 -41
Ver Arquivo
@@ -1,41 +1,44 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef COMB_H
#define COMB_H
#include "utils/polygon.h"
class Comb
{
private:
Polygons& boundery;
int64_t* minX;
int64_t* maxX;
unsigned int* minIdx;
unsigned int* maxIdx;
PointMatrix matrix;
Point sp;
Point ep;
bool preTest(Point startPoint, Point endPoint);
bool collisionTest(Point startPoint, Point endPoint);
void calcMinMax();
unsigned int getPolygonAbove(int64_t x);
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx);
public:
Comb(Polygons& _boundery);
~Comb();
bool checkInside(Point p);
bool moveInside(Point* p, int distance = 100);
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints);
};
#endif//COMB_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef COMB_H
#define COMB_H
#include "utils/polygon.h"
namespace cura {
class Comb
{
private:
Polygons& boundery;
int64_t* minX;
int64_t* maxX;
unsigned int* minIdx;
unsigned int* maxIdx;
PointMatrix matrix;
Point sp;
Point ep;
bool preTest(Point startPoint, Point endPoint);
bool collisionTest(Point startPoint, Point endPoint);
void calcMinMax();
unsigned int getPolygonAbove(int64_t x);
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx);
public:
Comb(Polygons& _boundery);
~Comb();
bool inside(const Point p) { return boundery.inside(p); }
bool moveInside(Point* p, int distance = 100);
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints);
};
}//namespace cura
#endif//COMB_H
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+277 -178
Ver Arquivo
@@ -1,21 +1,17 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdarg.h>
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdarg.h>
#include <stdio.h>
#include "gcodeExport.h"
#include "pathOrderOptimizer.h"
#include "timeEstimate.h"
#include "settings.h"
#include "utils/logoutput.h"
#include "settings.h"
#include "utils/logoutput.h"
#if defined(__APPLE__) && defined(__MACH__)
//On MacOS the file offset functions are always 64bit.
#define off64_t off_t
#define ftello64 ftello
#define fseeko64 fseeko
#endif
namespace cura {
GCodeExport::GCodeExport()
: currentPosition(0,0,0)
: currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
{
extrusionAmount = 0;
extrusionPerMM = 0;
@@ -26,13 +22,14 @@ GCodeExport::GCodeExport()
extruderNr = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
totalPrintTime = 0.0;
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
totalFilament[e] = 0.0;
currentSpeed = 0;
retractionSpeed = 45;
isRetracted = true;
isRetracted = false;
setFlavor(GCODE_FLAVOR_REPRAP);
memset(extruderOffset, 0, sizeof(extruderOffset));
f = stdout;
}
@@ -44,26 +41,27 @@ GCodeExport::~GCodeExport()
}
void GCodeExport::replaceTagInStart(const char* tag, const char* replaceValue)
{
if (f == stdout)
{
log("Replace:%s:%s\n", tag, replaceValue);
return;
{
if (f == stdout)
{
cura::log("Replace:%s:%s\n", tag, replaceValue);
return;
}
off64_t oldPos = ftello64(f);
fpos_t oldPos;
fgetpos(f, &oldPos);
char buffer[1024];
fseeko64(f, 0, SEEK_SET);
fseek(f, 0, SEEK_SET);
fread(buffer, 1024, 1, f);
char* c = strstr(buffer, tag);
memset(c, ' ', strlen(tag));
if (c) memcpy(c, replaceValue, strlen(replaceValue));
fseeko64(f, 0, SEEK_SET);
fseek(f, 0, SEEK_SET);
fwrite(buffer, 1024, 1, f);
fseeko64(f, oldPos, SEEK_SET);
fsetpos(f, &oldPos);
}
void GCodeExport::setExtruderOffset(int id, Point p)
@@ -71,9 +69,21 @@ void GCodeExport::setExtruderOffset(int id, Point p)
extruderOffset[id] = p;
}
void GCodeExport::setSwitchExtruderCode(std::string preSwitchExtruderCode, std::string postSwitchExtruderCode)
{
this->preSwitchExtruderCode = preSwitchExtruderCode;
this->postSwitchExtruderCode = postSwitchExtruderCode;
}
void GCodeExport::setFlavor(int flavor)
{
this->flavor = flavor;
if (flavor == GCODE_FLAVOR_MACH3)
for(int n=0; n<MAX_EXTRUDERS; n++)
extruderCharacter[n] = 'A' + n;
else
for(int n=0; n<MAX_EXTRUDERS; n++)
extruderCharacter[n] = 'E';
}
int GCodeExport::getFlavor()
{
@@ -87,24 +97,25 @@ void GCodeExport::setFilename(const char* filename)
bool GCodeExport::isOpened()
{
return f != NULL;
return f != nullptr;
}
void GCodeExport::setExtrusion(int layerThickness, int filamentDiameter, int flow)
{
double filamentArea = M_PI * (INT2MM(filamentDiameter) / 2.0) * (INT2MM(filamentDiameter) / 2.0);
if (flavor == GCODE_FLAVOR_ULTIGCODE)//UltiGCode uses volume extrusion as E value, and thus does not need the filamentArea in the mix.
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)//UltiGCode uses volume extrusion as E value, and thus does not need the filamentArea in the mix.
extrusionPerMM = INT2MM(layerThickness);
else
extrusionPerMM = INT2MM(layerThickness) / filamentArea * double(flow) / 100.0;
}
void GCodeExport::setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop)
void GCodeExport::setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop, int retractionAmountPrime)
{
this->retractionAmount = INT2MM(retractionAmount);
this->retractionAmountPrime = INT2MM(retractionAmountPrime);
this->retractionSpeed = retractionSpeed;
this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction);
this->minimalExtrusionBeforeRetraction = INT2MM(minimalExtrusionBeforeRetraction);
this->minimalExtrusionBeforeRetraction = INT2MM(minimalExtrusionBeforeRetraction);
this->retractionZHop = zHop;
}
@@ -118,6 +129,17 @@ Point GCodeExport::getPositionXY()
return Point(currentPosition.x, currentPosition.y);
}
void GCodeExport::resetStartPosition()
{
startPosition.x = INT32_MIN;
startPosition.y = INT32_MIN;
}
Point GCodeExport::getStartPositionXY()
{
return Point(startPosition.x, startPosition.y);
}
int GCodeExport::getPositionZ()
{
return currentPosition.z;
@@ -129,21 +151,21 @@ int GCodeExport::getExtruderNr()
}
double GCodeExport::getTotalFilamentUsed(int e)
{
{
if (e == extruderNr)
return totalFilament[e] + extrusionAmount;
return totalFilament[e];
}
double GCodeExport::getTotalPrintTime()
{
{
return totalPrintTime;
}
void GCodeExport::updateTotalPrintTime()
{
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
}
void GCodeExport::updateTotalPrintTime()
{
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
}
void GCodeExport::writeComment(const char* comment, ...)
@@ -152,7 +174,10 @@ void GCodeExport::writeComment(const char* comment, ...)
va_start(args, comment);
fprintf(f, ";");
vfprintf(f, comment, args);
fprintf(f, "\n");
if (flavor == GCODE_FLAVOR_BFB)
fprintf(f, "\r\n");
else
fprintf(f, "\n");
va_end(args);
}
@@ -161,15 +186,18 @@ void GCodeExport::writeLine(const char* line, ...)
va_list args;
va_start(args, line);
vfprintf(f, line, args);
fprintf(f, "\n");
if (flavor == GCODE_FLAVOR_BFB)
fprintf(f, "\r\n");
else
fprintf(f, "\n");
va_end(args);
}
void GCodeExport::resetExtrusionValue()
{
if (extrusionAmount != 0.0 && flavor != GCODE_FLAVOR_MAKERBOT)
if (extrusionAmount != 0.0 && flavor != GCODE_FLAVOR_MAKERBOT && flavor != GCODE_FLAVOR_BFB)
{
fprintf(f, "G92 E0\n");
fprintf(f, "G92 %c0\n", extruderCharacter[extruderNr]);
totalFilament[extruderNr] += extrusionAmount;
extrusionAmountAtPreviousRetraction -= extrusionAmount;
extrusionAmount = 0.0;
@@ -178,67 +206,116 @@ void GCodeExport::resetExtrusionValue()
void GCodeExport::writeDelay(double timeAmount)
{
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
totalPrintTime += timeAmount;
}
void GCodeExport::writeMove(Point p, int speed, int lineWidth)
{
if (lineWidth != 0)
if (currentPosition.x == p.X && currentPosition.y == p.Y && currentPosition.z == zPos)
return;
if (flavor == GCODE_FLAVOR_BFB)
{
Point diff = p - getPositionXY();
if (isRetracted)
{
if (retractionZHop > 0)
fprintf(f, "G1 Z%0.2f\n", INT2MM(currentPosition.z));
if (flavor == GCODE_FLAVOR_ULTIGCODE)
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
float fspeed = speed * 60;
float rpm = (extrusionPerMM * double(lineWidth) / 1000.0) * speed * 60;
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
rpm /= mm_per_rpm;
if (rpm > 0)
{
if (isRetracted)
{
fprintf(f, "G11\n");
}else{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount);
currentSpeed = retractionSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(p.X), INT2MM(p.Y), INT2MM(zPos), extrusionAmount), currentSpeed);
if (currentSpeed != int(rpm * 10))
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusionPerMM, lineWidth, speed);
fprintf(f, "M108 S%0.1f\r\n", rpm);
currentSpeed = int(rpm * 10);
}
fprintf(f, "M%d01\r\n", extruderNr + 1);
isRetracted = false;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
// (Trick copied from KISSlicer, thanks Jonathan)
fspeed *= (rpm / (roundf(rpm * 100) / 100));
//Increase the extrusion amount to calculate the amount of filament used.
Point diff = p - getPositionXY();
extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff);
}else{
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!isRetracted)
{
fprintf(f, "M103\r\n");
isRetracted = true;
}
if (extrusionAmount > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
resetExtrusionValue();
isRetracted = false;
}
extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff);
fprintf(f, "G1");
fprintf(f, "G1 X%0.3f Y%0.3f Z%0.3f F%0.1f\r\n", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y), INT2MM(zPos), fspeed);
}else{
fprintf(f, "G0");
//Normal E handling.
if (lineWidth != 0)
{
Point diff = p - getPositionXY();
if (isRetracted)
{
if (retractionZHop > 0)
fprintf(f, "G1 Z%0.3f\n", float(currentPosition.z)/1000);
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
{
fprintf(f, "G11\n");
}else{
extrusionAmount += retractionAmountPrime;
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount);
currentSpeed = retractionSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), currentSpeed);
}
if (extrusionAmount > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
resetExtrusionValue();
isRetracted = false;
}
extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff);
fprintf(f, "G1");
}else{
fprintf(f, "G0");
}
if (currentSpeed != speed)
{
fprintf(f, " F%i", speed * 60);
currentSpeed = speed;
}
fprintf(f, " X%0.3f Y%0.3f", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y));
if (zPos != currentPosition.z)
fprintf(f, " Z%0.3f", INT2MM(zPos));
if (lineWidth != 0)
fprintf(f, " %c%0.5f", extruderCharacter[extruderNr], extrusionAmount);
fprintf(f, "\n");
}
if (currentSpeed != speed)
{
fprintf(f, " F%i", speed * 60);
currentSpeed = speed;
}
fprintf(f, " X%0.2f Y%0.2f", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y));
if (zPos != currentPosition.z)
fprintf(f, " Z%0.2f", INT2MM(zPos));
if (lineWidth != 0)
fprintf(f, " E%0.5lf", extrusionAmount);
fprintf(f, "\n");
currentPosition = Point3(p.X, p.Y, zPos);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), currentSpeed);
currentPosition = Point3(p.X, p.Y, zPos);
startPosition = currentPosition;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), speed);
}
void GCodeExport::writeRetraction()
void GCodeExport::writeRetraction(bool force)
{
if (retractionAmount > 0 && !isRetracted && extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount)
if (flavor == GCODE_FLAVOR_BFB)//BitsFromBytes does automatic retraction.
return;
if (retractionAmount > 0 && !isRetracted && (extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount || force))
{
if (flavor == GCODE_FLAVOR_ULTIGCODE)
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
{
fprintf(f, "G10\n");
}else{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount - retractionAmount);
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - retractionAmount);
currentSpeed = retractionSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
}
if (retractionZHop > 0)
fprintf(f, "G1 Z%0.2f\n", INT2MM(currentPosition.z + retractionZHop));
if (retractionZHop > 0)
fprintf(f, "G1 Z%0.3f\n", INT2MM(currentPosition.z + retractionZHop));
extrusionAmountAtPreviousRetraction = extrusionAmount;
isRetracted = true;
}
@@ -248,46 +325,66 @@ void GCodeExport::switchExtruder(int newExtruder)
{
if (extruderNr == newExtruder)
return;
if (flavor == GCODE_FLAVOR_BFB)
{
if (!isRetracted)
fprintf(f, "M103\r\n");
isRetracted = true;
return;
}
resetExtrusionValue();
extruderNr = newExtruder;
if (flavor == GCODE_FLAVOR_ULTIGCODE)
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
{
fprintf(f, "G10 S1\n");
}else{
fprintf(f, "G1 F%i E%0.4lf\n", retractionSpeed * 60, extrusionAmount - extruderSwitchRetraction);
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - extruderSwitchRetraction);
currentSpeed = retractionSpeed;
}
isRetracted = true;
if (flavor == GCODE_FLAVOR_MAKERBOT)
if (retractionZHop > 0)
fprintf(f, "G1 Z%0.3f\n", INT2MM(currentPosition.z + retractionZHop));
extruderNr = newExtruder;
if (flavor == GCODE_FLAVOR_MACH3)
resetExtrusionValue();
isRetracted = true;
writeCode(preSwitchExtruderCode.c_str());
if (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M135 T%i\n", extruderNr);
else
fprintf(f, "T%i\n", extruderNr);
writeCode(postSwitchExtruderCode.c_str());
}
void GCodeExport::writeCode(const char* str)
{
fprintf(f, "%s\n", str);
fprintf(f, "%s", str);
if (flavor == GCODE_FLAVOR_BFB)
fprintf(f, "\r\n");
else
fprintf(f, "\n");
}
void GCodeExport::writeFanCommand(int speed)
{
if (currentFanSpeed == speed)
return;
if (speed > 0)
{
if (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M126 T0 ; value = %d\n", speed * 255 / 100);
else
fprintf(f, "M106 S%d\n", speed * 255 / 100);
}
else
if (speed > 0)
{
if (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M127 T0\n");
if (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M126 T0 ; value = %d\n", speed * 255 / 100);
else if (flavor == GCODE_FLAVOR_MACH3)
fprintf(f, "M106 P%d\n", speed * 255 / 100);
else
fprintf(f, "M107\n");
fprintf(f, "M106 S%d\n", speed * 255 / 100);
}
else
{
if (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M127 T0\n");
else if (flavor == GCODE_FLAVOR_MACH3)
fprintf(f, "M106 P%d\n", 0);
else
fprintf(f, "M107\n");
}
currentFanSpeed = speed;
}
@@ -296,38 +393,38 @@ int GCodeExport::getFileSize(){
return ftell(f);
}
void GCodeExport::tellFileSize() {
float fsize = (float) ftell(f);
float fsize = ftell(f);
if(fsize > 1024*1024) {
fsize /= 1024.0*1024.0;
log("Wrote %5.1f MB.\n",fsize);
cura::log("Wrote %5.1f MB.\n",fsize);
}
if(fsize > 1024) {
fsize /= 1024.0;
log("Wrote %5.1f kilobytes.\n",fsize);
cura::log("Wrote %5.1f kilobytes.\n",fsize);
}
}
void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCode)
{
writeFanCommand(0);
writeRetraction();
setZ(maxObjectHeight + 5000);
writeMove(getPositionXY(), moveSpeed, 0);
writeCode(endCode);
cura::log("Print time: %d\n", int(getTotalPrintTime()));
cura::log("Filament: %d\n", int(getTotalFilamentUsed(0)));
cura::log("Filament2: %d\n", int(getTotalFilamentUsed(1)));
if (getFlavor() == GCODE_FLAVOR_ULTIGCODE)
{
char numberString[16];
sprintf(numberString, "%d", int(getTotalPrintTime()));
replaceTagInStart("<__TIME__>", numberString);
sprintf(numberString, "%d", int(getTotalFilamentUsed(0)));
replaceTagInStart("<FILAMENT>", numberString);
sprintf(numberString, "%d", int(getTotalFilamentUsed(1)));
replaceTagInStart("<FILAMEN2>", numberString);
}
}
void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCode)
{
writeFanCommand(0);
writeRetraction();
setZ(maxObjectHeight + 5000);
writeMove(getPositionXY(), moveSpeed, 0);
writeCode(endCode);
log("Print time: %d\n", int(getTotalPrintTime()));
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
log("Filament2: %d\n", int(getTotalFilamentUsed(1)));
if (getFlavor() == GCODE_FLAVOR_ULTIGCODE)
{
char numberString[16];
sprintf(numberString, "%d", int(getTotalPrintTime()));
replaceTagInStart("<__TIME__>", numberString);
sprintf(numberString, "%d", int(getTotalFilamentUsed(0)));
replaceTagInStart("<FILAMENT>", numberString);
sprintf(numberString, "%d", int(getTotalFilamentUsed(1)));
replaceTagInStart("<FILAMEN2>", numberString);
}
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config)
@@ -338,22 +435,22 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config)
GCodePath* ret = &paths[paths.size()-1];
ret->retract = false;
ret->config = config;
ret->extruder = currentExtruder;
ret->extruder = currentExtruder;
ret->done = false;
return ret;
}
void GCodePlanner::forceNewPathStart()
{
if (paths.size() > 0)
if (paths.size() > 0)
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(GCodeExport& gcode, int travelSpeed, int retractionMinimalDistance)
: gcode(gcode), travelConfig(travelSpeed, 0, "travel")
{
lastPosition = gcode.getPositionXY();
comb = NULL;
extrudeSpeedFactor = 100;
comb = nullptr;
extrudeSpeedFactor = 100;
travelSpeedFactor = 100;
extraTime = 0.0;
totalPrintTime = 0.0;
@@ -378,7 +475,7 @@ void GCodePlanner::addTravel(Point p)
path->retract = true;
}
forceRetraction = false;
}else if (comb != NULL)
}else if (comb != nullptr)
{
vector<Point> pointList;
if (comb->calc(lastPosition, p, pointList))
@@ -408,17 +505,17 @@ void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config)
void GCodePlanner::moveInsideCombBoundary(int distance)
{
if (!comb || comb->checkInside(lastPosition)) return;
if (!comb || comb->inside(lastPosition)) return;
Point p = lastPosition;
if (comb->moveInside(&p, distance))
{
//Move inside again, so we move out of tight 90deg corners
comb->moveInside(&p, distance);
if (comb->checkInside(p))
{
//Move inside again, so we move out of tight 90deg corners
comb->moveInside(&p, distance);
if (comb->inside(p))
{
addTravel(p);
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
forceNewPathStart();
forceNewPathStart();
}
}
}
@@ -454,7 +551,7 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
{
Point p0 = gcode.getPositionXY();
double travelTime = 0.0;
double extrudeTime = 0.0;
double extrudeTime = 0.0;
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
@@ -464,11 +561,11 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
if (path->config->lineWidth != 0)
extrudeTime += thisTime;
else
travelTime += thisTime;
travelTime += thisTime;
p0 = path->points[i];
}
}
double totalTime = extrudeTime + travelTime;
}
double totalTime = extrudeTime + travelTime;
if (totalTime < minTime && extrudeTime > 0.0)
{
double minExtrudeTime = minTime - travelTime;
@@ -483,19 +580,19 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
int speed = path->config->speed * factor;
if (speed < minimalSpeed)
factor = double(minimalSpeed) / double(path->config->speed);
}
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
}
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
if (factor * 100 < getExtrudeSpeedFactor())
setExtrudeSpeedFactor(factor * 100);
else
setExtrudeSpeedFactor(factor * 100);
else
factor = getExtrudeSpeedFactor() / 100.0;
if (minTime - (extrudeTime / factor) - travelTime > 0.1)
{
//TODO: Use up this extra time (circle around the print?)
this->extraTime = minTime - (extrudeTime / factor) - travelTime;
}
}
this->totalPrintTime = (extrudeTime / factor) + travelTime;
}else{
this->totalPrintTime = totalTime;
@@ -504,9 +601,9 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
{
GCodePathConfig* lastConfig = NULL;
GCodePathConfig* lastConfig = nullptr;
int extruder = gcode.getExtruderNr();
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
@@ -559,46 +656,46 @@ void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
n = i - 1;
continue;
}
}
bool spiralize = path->config->spiralize;
if (spiralize)
{
//Check if we are the last spiralize path in the list, if not, do not spiralize.
}
bool spiralize = path->config->spiralize;
if (spiralize)
{
//Check if we are the last spiralize path in the list, if not, do not spiralize.
for(unsigned int m=n+1; m<paths.size(); m++)
{
if (paths[m].config->spiralize)
spiralize = false;
}
}
if (spiralize)
{
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
float totalLength = 0.0;
int z = gcode.getPositionZ();
Point p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path->points.size(); i++)
{
Point p1 = path->points[i];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
}
float length = 0.0;
p0 = gcode.getPositionXY();
if (paths[m].config->spiralize)
spiralize = false;
}
}
if (spiralize)
{
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
float totalLength = 0.0;
int z = gcode.getPositionZ();
Point p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path->points.size(); i++)
{
Point p1 = path->points[i];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
{
Point p1 = path->points[i];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
}
float length = 0.0;
p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path->points.size(); i++)
{
Point p1 = path->points[i];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
gcode.writeMove(path->points[i], speed, path->config->lineWidth);
}
}
}else{
for(unsigned int i=0; i<path->points.size(); i++)
{
gcode.writeMove(path->points[i], speed, path->config->lineWidth);
}
}
}
}
@@ -606,10 +703,12 @@ void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
if (liftHeadIfNeeded && extraTime > 0.0)
{
gcode.writeComment("Small layer, adding delay of %f", extraTime);
gcode.writeRetraction();
gcode.writeRetraction(true);
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), travelConfig.speed, 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.speed, 0);
gcode.writeDelay(extraTime);
}
}
}//namespace cura
+56 -42
Ver Arquivo
@@ -1,16 +1,18 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef GCODEEXPORT_H
#define GCODEEXPORT_H
#include <stdio.h>
#include "settings.h"
#include "comb.h"
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "timeEstimate.h"
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
#include <stdio.h>
#include "settings.h"
#include "comb.h"
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "timeEstimate.h"
namespace cura {
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
// Any customizations on GCodes flavors are done in this class.
class GCodeExport
{
@@ -19,22 +21,27 @@ private:
double extrusionAmount;
double extrusionPerMM;
double retractionAmount;
double retractionAmountPrime;
int retractionZHop;
double extruderSwitchRetraction;
double minimalExtrusionBeforeRetraction;
double extrusionAmountAtPreviousRetraction;
Point3 currentPosition;
Point3 startPosition;
Point extruderOffset[MAX_EXTRUDERS];
char extruderCharacter[MAX_EXTRUDERS];
int currentSpeed, retractionSpeed;
int zPos;
bool isRetracted;
int extruderNr;
int currentFanSpeed;
int flavor;
int flavor;
std::string preSwitchExtruderCode;
std::string postSwitchExtruderCode;
double totalFilament[MAX_EXTRUDERS];
double totalPrintTime;
TimeEstimateCalculator estimateCalculator;
TimeEstimateCalculator estimateCalculator;
public:
GCodeExport();
@@ -44,6 +51,7 @@ public:
void replaceTagInStart(const char* tag, const char* replaceValue);
void setExtruderOffset(int id, Point p);
void setSwitchExtruderCode(std::string preSwitchExtruderCode, std::string postSwitchExtruderCode);
void setFlavor(int flavor);
int getFlavor();
@@ -54,12 +62,16 @@ public:
void setExtrusion(int layerThickness, int filamentDiameter, int flow);
void setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop);
void setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop, int retractionAmountPrime);
void setZ(int z);
Point getPositionXY();
void resetStartPosition();
Point getStartPositionXY();
int getPositionZ();
int getExtruderNr();
@@ -67,7 +79,7 @@ public:
double getTotalFilamentUsed(int e);
double getTotalPrintTime();
void updateTotalPrintTime();
void updateTotalPrintTime();
void writeComment(const char* comment, ...);
@@ -79,37 +91,37 @@ public:
void writeMove(Point p, int speed, int lineWidth);
void writeRetraction();
void writeRetraction(bool force=false);
void switchExtruder(int newExtruder);
void writeCode(const char* str);
void writeFanCommand(int speed);
void writeFanCommand(int speed);
void finalize(int maxObjectHeight, int moveSpeed, const char* endCode);
int getFileSize();
void tellFileSize();
};
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
class GCodePathConfig
{
public:
int speed;
int lineWidth;
const char* name;
bool spiralize;
GCodePathConfig() : speed(0), lineWidth(0), name(NULL), spiralize(false) {}
const char* name;
bool spiralize;
GCodePathConfig() : speed(0), lineWidth(0), name(nullptr), spiralize(false) {}
GCodePathConfig(int speed, int lineWidth, const char* name) : speed(speed), lineWidth(lineWidth), name(name), spiralize(false) {}
void setData(int speed, int lineWidth, const char* name)
{
this->speed = speed;
this->lineWidth = lineWidth;
this->name = name;
{
this->speed = speed;
this->lineWidth = lineWidth;
this->name = name;
}
};
@@ -119,12 +131,12 @@ public:
GCodePathConfig* config;
bool retract;
int extruder;
vector<Point> points;
vector<Point> points;
bool done;//Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
};
//The GCodePlanner class stores multiple moves that are planned.
// It facilitates the combing to keep the head inside the print.
//The GCodePlanner class stores multiple moves that are planned.
// It facilitates the combing to keep the head inside the print.
// It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
class GCodePlanner
{
@@ -136,7 +148,7 @@ private:
Comb* comb;
GCodePathConfig travelConfig;
int extrudeSpeedFactor;
int extrudeSpeedFactor;
int travelSpeedFactor;
int currentExtruder;
int retractionMinimalDistance;
@@ -149,19 +161,19 @@ private:
void forceNewPathStart();
public:
GCodePlanner(GCodeExport& gcode, int travelSpeed, int retractionMinimalDistance);
~GCodePlanner();
~GCodePlanner();
bool setExtruder(int extruder)
{
if (extruder == currentExtruder)
return false;
currentExtruder = extruder;
{
if (extruder == currentExtruder)
return false;
currentExtruder = extruder;
return true;
}
int getExtruder()
{
return currentExtruder;
}
int getExtruder()
{
return currentExtruder;
}
void setCombBoundary(Polygons* polygons)
@@ -171,7 +183,7 @@ public:
if (polygons)
comb = new Comb(*polygons);
else
comb = NULL;
comb = nullptr;
}
void setAlwaysRetract(bool alwaysRetract)
@@ -218,4 +230,6 @@ public:
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
};
}//namespace cura
#endif//GCODEEXPORT_H
+104 -76
Ver Arquivo
@@ -1,79 +1,107 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "infill.h"
void generateConcentricInfill(Polygons outline, Polygons& result, int offsets[], int offsetsSize)
{
int step = 0;
while(1)
{
for(unsigned int polygonNr=0; polygonNr<outline.size(); polygonNr++)
result.add(outline[polygonNr]);
outline = outline.offset(-offsets[step]);
if (outline.size() < 1)
break;
step = (step + 1) % offsetsSize;
}
}
int compare_int64_t(const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0) return -1;
if (n > 0) return 1;
return 0;
}
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100);
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
AABB boundary(outline);
boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "infill.h"
namespace cura {
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
PolygonRef r = outline[polyNr];
result.add(r);
}
outline = outline.offset(-inset_value);
}
}
void generateAutomaticInfill(const Polygons& in_outline, Polygons& result,
int extrusionWidth, int lineSpacing,
int infillOverlap, double rotation)
{
if (lineSpacing > extrusionWidth * 4)
{
generateGridInfill(in_outline, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
}
else
{
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
}
}
void generateGridInfill(const Polygons& in_outline, Polygons& result,
int extrusionWidth, int lineSpacing, int infillOverlap,
double rotation)
{
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing * 2,
infillOverlap, rotation);
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing * 2,
infillOverlap, rotation + 90);
}
int compare_int64_t(const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0) return -1;
if (n > 0) return 1;
return 0;
}
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100);
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
AABB boundary(outline);
boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
vector<vector<int64_t> > cutList;
for(int n=0; n<lineCount; n++)
cutList.push_back(vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
Point p1 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p0 = outline[polyNr][i];
int idx0 = (p0.X - boundary.min.X) / lineSpacing;
int idx1 = (p1.X - boundary.min.X) / lineSpacing;
int64_t xMin = p0.X, xMax = p1.X;
if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; }
if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; }
for(int idx = idx0; idx<=idx1; idx++)
{
int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2;
if (x < xMin) continue;
if (x >= xMax) continue;
int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X);
cutList[idx].push_back(y);
}
p1 = p0;
}
}
int idx = 0;
for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing)
{
qsort(cutList[idx].data(), cutList[idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2)
{
if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 5)
continue;
PolygonRef p = result.newPoly();
p.add(matrix.unapply(Point(x, cutList[idx][i])));
p.add(matrix.unapply(Point(x, cutList[idx][i+1])));
}
idx += 1;
}
}
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
Point p1 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p0 = outline[polyNr][i];
int idx0 = (p0.X - boundary.min.X) / lineSpacing;
int idx1 = (p1.X - boundary.min.X) / lineSpacing;
int64_t xMin = p0.X, xMax = p1.X;
if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; }
if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; }
for(int idx = idx0; idx<=idx1; idx++)
{
int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2;
if (x < xMin) continue;
if (x >= xMax) continue;
int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X);
cutList[idx].push_back(y);
}
p1 = p0;
}
}
int idx = 0;
for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing)
{
qsort(cutList[idx].data(), cutList[idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2)
{
if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 5)
continue;
PolygonRef p = result.newPoly();
p.add(matrix.unapply(Point(x, cutList[idx][i])));
p.add(matrix.unapply(Point(x, cutList[idx][i+1])));
}
idx += 1;
}
}
}//namespace cura
+16 -10
Ver Arquivo
@@ -1,10 +1,16 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INFILL_H
#define INFILL_H
#include "utils/polygon.h"
void generateConcentricInfill(Polygons outline, Polygons& result, int offsets[], int offsetsSize);
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
#endif//INFILL_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INFILL_H
#define INFILL_H
#include "utils/polygon.h"
namespace cura {
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateAutomaticInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateGridInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
}//namespace cura
#endif//INFILL_H
+48 -45
Ver Arquivo
@@ -1,45 +1,48 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "polygonOptimizer.h"
void generateInsets(SliceLayerPart* part, int offset, int insetCount)
{
part->combBoundery = part->outline.offset(-offset);
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
part->insets[i] = part->outline.offset(-offset * i - offset/2);
optimizePolygons(part->insets[i]);
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void generateInsets(SliceLayer* layer, int offset, int insetCount)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], offset, insetCount);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "polygonOptimizer.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int offset, int insetCount)
{
part->combBoundery = part->outline.offset(-offset);
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
part->insets[i] = part->outline.offset(-offset * i - offset/2);
optimizePolygons(part->insets[i]);
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void generateInsets(SliceLayer* layer, int offset, int insetCount)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], offset, insetCount);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
+15 -11
Ver Arquivo
@@ -1,11 +1,15 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
void generateInsets(SliceLayerPart* part, int offset, int insetCount);
void generateInsets(SliceLayer* layer, int offset, int insetCount);
#endif//INSET_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int offset, int insetCount);
void generateInsets(SliceLayer* layer, int offset, int insetCount);
}//namespace cura
#endif//INSET_H
+97 -92
Ver Arquivo
@@ -1,92 +1,97 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "layerPart.h"
#include "settings.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
It takes the result of the Slice step, which is an unordered list of polygons, and makes groups of polygons,
each of these groups is called a "part", which sometimes are also known as "islands". These parts represent
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType)
{
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_B)
{
for(unsigned int i=0; i<layer->polygonList.size(); i++)
{
if (layer->polygonList[i].orientation())
layer->polygonList[i].reverse();
}
}
vector<Polygons> result;
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
result = layer->polygonList.offset(1000).splitIntoParts(unionAllType);
else
result = layer->polygonList.splitIntoParts(unionAllType);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.push_back(SliceLayerPart());
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
{
storageLayer.parts[i].outline.add(result[i][0]);
storageLayer.parts[i].outline = storageLayer.parts[i].outline.offset(-1000);
}else
storageLayer.parts[i].outline = result[i];
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType)
{
for(unsigned int layerNr = 0; layerNr < slicer->layers.size(); layerNr++)
{
storage.layers.push_back(SliceLayer());
storage.layers[layerNr].sliceZ = slicer->layers[layerNr].z;
storage.layers[layerNr].printZ = slicer->layers[layerNr].z;
createLayerWithParts(storage.layers[layerNr], &slicer->layers[layerNr], unionAllType);
}
}
void dumpLayerparts(SliceDataStorage& storage, const char* filename)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point3 modelSize = storage.modelSize;
Point3 modelMin = storage.modelMin;
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int layerNr=0;layerNr<storage.volumes[volumeIdx].layers.size(); layerNr++)
{
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
for(unsigned int i=0;i<layer->parts.size();i++)
{
SliceLayerPart* part = &layer->parts[i];
for(unsigned int j=0;j<part->outline.size();j++)
{
fprintf(out, "<polygon points=\"");
for(unsigned int k=0;k<part->outline[j].size();k++)
fprintf(out, "%f,%f ", float(part->outline[j][k].X - modelMin.x)/modelSize.x*500, float(part->outline[j][k].Y - modelMin.y)/modelSize.y*500);
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
}
}
fprintf(out, "</svg>\n");
}
}
fprintf(out, "</body></html>");
fclose(out);
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "layerPart.h"
#include "settings.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
It takes the result of the Slice step, which is an unordered list of polygons, and makes groups of polygons,
each of these groups is called a "part", which sometimes are also known as "islands". These parts represent
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType)
{
storageLayer.openLines = layer->openPolygons;
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_B)
{
for(unsigned int i=0; i<layer->polygonList.size(); i++)
{
if (layer->polygonList[i].orientation())
layer->polygonList[i].reverse();
}
}
vector<Polygons> result;
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
result = layer->polygonList.offset(1000).splitIntoParts(unionAllType);
else
result = layer->polygonList.splitIntoParts(unionAllType);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.push_back(SliceLayerPart());
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
{
storageLayer.parts[i].outline.add(result[i][0]);
storageLayer.parts[i].outline = storageLayer.parts[i].outline.offset(-1000);
}else
storageLayer.parts[i].outline = result[i];
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType)
{
for(unsigned int layerNr = 0; layerNr < slicer->layers.size(); layerNr++)
{
storage.layers.push_back(SliceLayer());
storage.layers[layerNr].sliceZ = slicer->layers[layerNr].z;
storage.layers[layerNr].printZ = slicer->layers[layerNr].z;
createLayerWithParts(storage.layers[layerNr], &slicer->layers[layerNr], unionAllType);
}
}
void dumpLayerparts(SliceDataStorage& storage, const char* filename)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point3 modelSize = storage.modelSize;
Point3 modelMin = storage.modelMin;
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int layerNr=0;layerNr<storage.volumes[volumeIdx].layers.size(); layerNr++)
{
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
for(unsigned int i=0;i<layer->parts.size();i++)
{
SliceLayerPart* part = &layer->parts[i];
for(unsigned int j=0;j<part->outline.size();j++)
{
fprintf(out, "<polygon points=\"");
for(unsigned int k=0;k<part->outline[j].size();k++)
fprintf(out, "%f,%f ", float(part->outline[j][k].X - modelMin.x)/modelSize.x*500, float(part->outline[j][k].Y - modelMin.y)/modelSize.y*500);
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
}
}
fprintf(out, "</svg>\n");
}
}
fprintf(out, "</body></html>");
fclose(out);
}
}//namespace cura
+30 -27
Ver Arquivo
@@ -1,27 +1,30 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef LAYERPART_H
#define LAYERPART_H
#include "sliceDataStorage.h"
#include "slicer.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
It takes the result of the Slice step, which is an unordered list of polygons, and makes groups of polygons,
each of these groups is called a "part", which sometimes are also known as "islands". These parts represent
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType);
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType);
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
#endif//LAYERPART_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef LAYERPART_H
#define LAYERPART_H
#include "sliceDataStorage.h"
#include "slicer.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
It takes the result of the Slice step, which is an unordered list of polygons, and makes groups of polygons,
each of these groups is called a "part", which sometimes are also known as "islands". These parts represent
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType);
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType);
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
}//namespace cura
#endif//LAYERPART_H
+202 -156
Ver Arquivo
@@ -1,156 +1,202 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
#include <execinfo.h>
#include <sys/resource.h>
#endif
#include <stddef.h>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "sliceDataStorage.h"
#include "modelFile/modelFile.h"
#include "settings.h"
#include "optimizedModel.h"
#include "multiVolumes.h"
#include "polygonOptimizer.h"
#include "slicer.h"
#include "layerPart.h"
#include "inset.h"
#include "skin.h"
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOrderOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#include "fffProcessor.h"
void print_usage()
{
log("usage: CuraEngine [-h] [-v] [-m 3x3matrix] [-c <config file>] [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n");
}
//Signal handler for a "floating point exception", which can also be integer division by zero errors.
void signal_FPE(int n)
{
(void)n;
logError("Arithmetic exception.\n");
exit(1);
}
int main(int argc, char **argv)
{
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
//Lower the process priority on linux and mac. On windows this is done on process creation from the GUI.
setpriority(PRIO_PROCESS, 0, 10);
#endif
//Register the exception handling for arithmic exceptions, this prevents the "something went wrong" dialog on windows to pop up on a division by zero.
signal(SIGFPE, signal_FPE);
ConfigSettings config;
fffProcessor processor(config);
logError("Cura_SteamEngine version %s\n", VERSION);
if(!config.readSettings()) {
logError("Default config '%s' not used\n", DEFAULT_CONFIG_PATH);
}
for(int argn = 1; argn < argc; argn++)
log("Arg: %s\n", argv[argn]);
for(int argn = 1; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'h':
print_usage();
exit(1);
case 'v':
increaseVerboseLevel();
break;
case 'p':
enableProgressLogging();
break;
case 'g':
argn++;
//Connect the GUI socket to the given port number.
processor.guiConnect(atoi(argv[argn]));
break;
case 'b':
argn++;
//The binaryMeshBlob is depricated and will be removed in the future.
binaryMeshBlob = fopen(argv[argn], "rb");
break;
case 'o':
argn++;
if (!processor.setTargetFile(argv[argn]))
{
logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 'c':
{
// Read a config file from the given path
argn++;
if(!config.readSettings(argv[argn])) {
logError("Failed to read config '%s'\n", argv[argn]);
}
}
break;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
if (!config.setSetting(argv[argn], valuePtr))
logError("Setting not found: %s %s\n", argv[argn], valuePtr);
}
}
break;
case 'm':
//Read the given rotation/scale matrix
argn++;
sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
&config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
&config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
&config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
break;
default:
logError("Unknown option: %c\n", *str);
break;
}
}
}else{
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
processor.processFile(argv[argn]);
}catch(...){
logError("Unknown exception\n");
exit(1);
}
}
}
//Finalize the processor, this adds the end.gcode. And reports statistics.
processor.finalize();
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
#include <execinfo.h>
#include <sys/resource.h>
#endif
#include <stddef.h>
#include <vector>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "sliceDataStorage.h"
#include "modelFile/modelFile.h"
#include "settings.h"
#include "optimizedModel.h"
#include "multiVolumes.h"
#include "polygonOptimizer.h"
#include "slicer.h"
#include "layerPart.h"
#include "inset.h"
#include "skin.h"
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOrderOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#include "fffProcessor.h"
void print_usage()
{
cura::logError("usage: CuraEngine [-h] [-v] [-m 3x3matrix] [-c <config file>] [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n");
}
//Signal handler for a "floating point exception", which can also be integer division by zero errors.
void signal_FPE(int n)
{
(void)n;
cura::logError("Arithmetic exception.\n");
exit(1);
}
using namespace cura;
int main(int argc, char **argv)
{
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
//Lower the process priority on linux and mac. On windows this is done on process creation from the GUI.
setpriority(PRIO_PROCESS, 0, 10);
#endif
//Register the exception handling for arithmic exceptions, this prevents the "something went wrong" dialog on windows to pop up on a division by zero.
signal(SIGFPE, signal_FPE);
ConfigSettings config;
fffProcessor processor(config);
std::vector<std::string> files;
cura::logError("Cura_SteamEngine version %s\n", VERSION);
cura::logError("Copyright (C) 2014 David Braam\n");
cura::logError("\n");
cura::logError("This program is free software: you can redistribute it and/or modify\n");
cura::logError("it under the terms of the GNU Affero General Public License as published by\n");
cura::logError("the Free Software Foundation, either version 3 of the License, or\n");
cura::logError("(at your option) any later version.\n");
cura::logError("\n");
cura::logError("This program is distributed in the hope that it will be useful,\n");
cura::logError("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
cura::logError("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
cura::logError("GNU Affero General Public License for more details.\n");
cura::logError("\n");
cura::logError("You should have received a copy of the GNU Affero General Public License\n");
cura::logError("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
if(!config.readSettings()) {
cura::logError("Default config '%s' not used\n", DEFAULT_CONFIG_PATH);
}
for(int argn = 1; argn < argc; argn++)
cura::log("Arg: %s\n", argv[argn]);
for(int argn = 1; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'h':
print_usage();
exit(1);
case 'v':
cura::increaseVerboseLevel();
break;
case 'p':
cura::enableProgressLogging();
break;
case 'g':
argn++;
//Connect the GUI socket to the given port number.
processor.guiConnect(atoi(argv[argn]));
break;
case 'b':
argn++;
//The binaryMeshBlob is depricated and will be removed in the future.
binaryMeshBlob = fopen(argv[argn], "rb");
break;
case 'o':
argn++;
if (!processor.setTargetFile(argv[argn]))
{
cura::logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 'c':
{
// Read a config file from the given path
argn++;
if(!config.readSettings(argv[argn])) {
cura::logError("Failed to read config '%s'\n", argv[argn]);
}
}
break;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
if (!config.setSetting(argv[argn], valuePtr))
cura::logError("Setting not found: %s %s\n", argv[argn], valuePtr);
}
}
break;
case 'm':
//Read the given rotation/scale matrix
argn++;
sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
&config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
&config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
&config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
break;
case '-':
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
if (files.size() > 0)
processor.processFile(files);
files.clear();
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
break;
}
}
}else{
if (argv[argn][0] == '$')
{
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
std::vector<std::string> tmp;
tmp.push_back(argv[argn]);
processor.processFile(tmp);
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
}else{
files.push_back(argv[argn]);
}
}
}
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
if (files.size() > 0)
processor.processFile(files);
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
//Finalize the processor, this adds the end.gcode. And reports statistics.
processor.finalize();
return 0;
}
@@ -1,178 +1,187 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <string.h>
#include <stdio.h>
#include "modelFile.h"
#include "../utils/logoutput.h"
FILE* binaryMeshBlob = NULL;
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
void* fgets_(char* ptr, size_t len, FILE* f)
{
while(len && fread(ptr, 1, 1, f) > 0)
{
if (*ptr == '\n' || *ptr == '\r')
{
*ptr = '\0';
return ptr;
}
ptr++;
len--;
}
return NULL;
}
SimpleModel* loadModelSTL_ascii(const char* filename, FMatrix3x3& matrix)
{
SimpleModel* m = new SimpleModel();
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[0];
FILE* f = fopen(filename, "rt");
char buffer[1024];
FPoint3 vertex;
int n = 0;
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
while(fgets_(buffer, sizeof(buffer), f))
{
if (sscanf(buffer, " vertex %lf %lf %lf", &vertex.x, &vertex.y, &vertex.z) == 3)
{
n++;
switch(n)
{
case 1:
v0 = matrix.apply(vertex);
break;
case 2:
v1 = matrix.apply(vertex);
break;
case 3:
v2 = matrix.apply(vertex);
vol->addFace(v0, v1, v2);
n = 0;
break;
}
}
}
fclose(f);
return m;
}
SimpleModel* loadModelSTL_binary(const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return NULL;
}
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return NULL;
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
SimpleModel* m = new SimpleModel();
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[0];
if(vol == NULL)
{
fclose(f);
return NULL;
}
for(unsigned int i=0;i<faceCount;i++)
{
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
{
fclose(f);
return NULL;
}
float v[9];
if (fread(v, sizeof(float) * 9, 1, f) != 1)
{
fclose(f);
return NULL;
}
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
vol->addFace(v0, v1, v2);
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
{
fclose(f);
return NULL;
}
}
fclose(f);
return m;
}
SimpleModel* loadModelSTL(const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
if (f == NULL)
return NULL;
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
return NULL;
}
fclose(f);
buffer[5] = '\0';
if (strcasecmp(buffer, "SOLID") == 0)
{
return loadModelSTL_ascii(filename, matrix);
}
return loadModelSTL_binary(filename, matrix);
}
SimpleModel* loadModelFromFile(const char* filename, FMatrix3x3& matrix)
{
const char* ext = strrchr(filename, '.');
if (ext && strcasecmp(ext, ".stl") == 0)
{
return loadModelSTL(filename, matrix);
}
if (filename[0] == '#' && binaryMeshBlob != NULL)
{
SimpleModel* m = new SimpleModel();
while(*filename == '#')
{
filename++;
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
int32_t n, pNr = 0;
if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1)
return NULL;
log("Reading mesh from binary blob with %i vertexes\n", n);
Point3 v[3];
while(n)
{
float f[3];
if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1)
return NULL;
FPoint3 fp(f[0], f[1], f[2]);
v[pNr++] = matrix.apply(fp);
if (pNr == 3)
{
vol->addFace(v[0], v[1], v[2]);
pNr = 0;
}
n--;
}
}
return m;
}
return NULL;
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include "modelFile.h"
#include "../utils/logoutput.h"
#include "../utils/string.h"
FILE* binaryMeshBlob = nullptr;
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
void* fgets_(char* ptr, size_t len, FILE* f)
{
while(len && fread(ptr, 1, 1, f) > 0)
{
if (*ptr == '\n' || *ptr == '\r')
{
*ptr = '\0';
return ptr;
}
ptr++;
len--;
}
return nullptr;
}
SimpleModel* loadModelSTL_ascii(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
{
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
FILE* f = fopen(filename, "rt");
char buffer[1024];
FPoint3 vertex;
int n = 0;
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
while(fgets_(buffer, sizeof(buffer), f))
{
if (sscanf(buffer, " vertex %lf %lf %lf", &vertex.x, &vertex.y, &vertex.z) == 3)
{
n++;
switch(n)
{
case 1:
v0 = matrix.apply(vertex);
break;
case 2:
v1 = matrix.apply(vertex);
break;
case 3:
v2 = matrix.apply(vertex);
vol->addFace(v0, v1, v2);
n = 0;
break;
}
}
}
fclose(f);
return m;
}
SimpleModel* loadModelSTL_binary(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return nullptr;
}
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return nullptr;
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
if(vol == nullptr)
{
fclose(f);
return nullptr;
}
for(unsigned int i=0;i<faceCount;i++)
{
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
{
fclose(f);
return nullptr;
}
float v[9];
if (fread(v, sizeof(float) * 9, 1, f) != 1)
{
fclose(f);
return nullptr;
}
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
vol->addFace(v0, v1, v2);
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
{
fclose(f);
return nullptr;
}
}
fclose(f);
return m;
}
SimpleModel* loadModelSTL(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
if (f == nullptr)
return nullptr;
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
return nullptr;
}
fclose(f);
buffer[5] = '\0';
if (stringcasecompare(buffer, "solid") == 0)
{
SimpleModel* asciiModel = loadModelSTL_ascii(m, filename, matrix);
if (!asciiModel)
return nullptr;
// This logic is used to handle the case where the file starts with
// "solid" but is a binary file.
if (m->volumes[m->volumes.size()-1].faces.size() < 1)
{
m->volumes.erase(m->volumes.end() - 1);
return loadModelSTL_binary(m, filename, matrix);
}
return asciiModel;
}
return loadModelSTL_binary(m, filename, matrix);
}
SimpleModel* loadModelFromFile(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
{
const char* ext = strrchr(filename, '.');
if (ext && stringcasecompare(ext, ".stl") == 0)
{
return loadModelSTL(m,filename, matrix);
}
if (filename[0] == '#' && binaryMeshBlob != nullptr)
{
while(*filename == '#')
{
filename++;
m->volumes.push_back(SimpleVolume());
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
int32_t n, pNr = 0;
if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1)
return nullptr;
cura::log("Reading mesh from binary blob with %i vertexes\n", n);
Point3 v[3];
while(n)
{
float f[3];
if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1)
return nullptr;
FPoint3 fp(f[0], f[1], f[2]);
v[pNr++] = matrix.apply(fp);
if (pNr == 3)
{
vol->addFace(v[0], v[1], v[2]);
pNr = 0;
}
n--;
}
}
return m;
}
return nullptr;
}
+118 -118
Ver Arquivo
@@ -1,118 +1,118 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MODELFILE_H
#define MODELFILE_H
/**
modelFile contains the model loaders for the slicer. The model loader turns any format that it can read into a list of triangles with 3 X/Y/Z points.
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
**/
#include <vector>
using std::vector;
#include "../utils/intpoint.h"
#include "../utils/floatpoint.h"
extern FILE* binaryMeshBlob;
#define SET_MIN(n, m) do { if ((m) < (n)) n = m; } while(0)
#define SET_MAX(n, m) do { if ((m) > (n)) n = m; } while(0)
/* A SimpleFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers */
class SimpleFace
{
public:
Point3 v[3];
SimpleFace(Point3& v0, Point3& v1, Point3& v2) { v[0] = v0; v[1] = v1; v[2] = v2; }
};
/* A SimpleVolume is the most basic reprisentation of a 3D model. It contains all the faces as SimpleTriangles, with nothing fancy. */
class SimpleVolume
{
public:
vector<SimpleFace> faces;
void addFace(Point3& v0, Point3& v1, Point3& v2)
{
faces.push_back(SimpleFace(v0, v1, v2));
}
Point3 min()
{
if (faces.size() < 1)
return Point3(0, 0, 0);
Point3 ret = faces[0].v[0];
for(unsigned int i=0; i<faces.size(); i++)
{
SET_MIN(ret.x, faces[i].v[0].x);
SET_MIN(ret.y, faces[i].v[0].y);
SET_MIN(ret.z, faces[i].v[0].z);
SET_MIN(ret.x, faces[i].v[1].x);
SET_MIN(ret.y, faces[i].v[1].y);
SET_MIN(ret.z, faces[i].v[1].z);
SET_MIN(ret.x, faces[i].v[2].x);
SET_MIN(ret.y, faces[i].v[2].y);
SET_MIN(ret.z, faces[i].v[2].z);
}
return ret;
}
Point3 max()
{
if (faces.size() < 1)
return Point3(0, 0, 0);
Point3 ret = faces[0].v[0];
for(unsigned int i=0; i<faces.size(); i++)
{
SET_MAX(ret.x, faces[i].v[0].x);
SET_MAX(ret.y, faces[i].v[0].y);
SET_MAX(ret.z, faces[i].v[0].z);
SET_MAX(ret.x, faces[i].v[1].x);
SET_MAX(ret.y, faces[i].v[1].y);
SET_MAX(ret.z, faces[i].v[1].z);
SET_MAX(ret.x, faces[i].v[2].x);
SET_MAX(ret.y, faces[i].v[2].y);
SET_MAX(ret.z, faces[i].v[2].z);
}
return ret;
}
};
//A SimpleModel is a 3D model with 1 or more 3D volumes.
class SimpleModel
{
public:
vector<SimpleVolume> volumes;
Point3 min()
{
if (volumes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = volumes[0].min();
for(unsigned int i=0; i<volumes.size(); i++)
{
Point3 v = volumes[i].min();
SET_MIN(ret.x, v.x);
SET_MIN(ret.y, v.y);
SET_MIN(ret.z, v.z);
}
return ret;
}
Point3 max()
{
if (volumes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = volumes[0].max();
for(unsigned int i=0; i<volumes.size(); i++)
{
Point3 v = volumes[i].max();
SET_MAX(ret.x, v.x);
SET_MAX(ret.y, v.y);
SET_MAX(ret.z, v.z);
}
return ret;
}
};
SimpleModel* loadModelFromFile(const char* filename, FMatrix3x3& matrix);
#endif//MODELFILE_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MODELFILE_H
#define MODELFILE_H
/**
modelFile contains the model loaders for the slicer. The model loader turns any format that it can read into a list of triangles with 3 X/Y/Z points.
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
**/
#include <vector>
using std::vector;
#include "../utils/intpoint.h"
#include "../utils/floatpoint.h"
extern FILE* binaryMeshBlob;
#define SET_MIN(n, m) do { if ((m) < (n)) n = m; } while(0)
#define SET_MAX(n, m) do { if ((m) > (n)) n = m; } while(0)
/* A SimpleFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers */
class SimpleFace
{
public:
Point3 v[3];
SimpleFace(Point3& v0, Point3& v1, Point3& v2) { v[0] = v0; v[1] = v1; v[2] = v2; }
};
/* A SimpleVolume is the most basic reprisentation of a 3D model. It contains all the faces as SimpleTriangles, with nothing fancy. */
class SimpleVolume
{
public:
vector<SimpleFace> faces;
void addFace(Point3& v0, Point3& v1, Point3& v2)
{
faces.push_back(SimpleFace(v0, v1, v2));
}
Point3 min()
{
if (faces.size() < 1)
return Point3(0, 0, 0);
Point3 ret = faces[0].v[0];
for(unsigned int i=0; i<faces.size(); i++)
{
SET_MIN(ret.x, faces[i].v[0].x);
SET_MIN(ret.y, faces[i].v[0].y);
SET_MIN(ret.z, faces[i].v[0].z);
SET_MIN(ret.x, faces[i].v[1].x);
SET_MIN(ret.y, faces[i].v[1].y);
SET_MIN(ret.z, faces[i].v[1].z);
SET_MIN(ret.x, faces[i].v[2].x);
SET_MIN(ret.y, faces[i].v[2].y);
SET_MIN(ret.z, faces[i].v[2].z);
}
return ret;
}
Point3 max()
{
if (faces.size() < 1)
return Point3(0, 0, 0);
Point3 ret = faces[0].v[0];
for(unsigned int i=0; i<faces.size(); i++)
{
SET_MAX(ret.x, faces[i].v[0].x);
SET_MAX(ret.y, faces[i].v[0].y);
SET_MAX(ret.z, faces[i].v[0].z);
SET_MAX(ret.x, faces[i].v[1].x);
SET_MAX(ret.y, faces[i].v[1].y);
SET_MAX(ret.z, faces[i].v[1].z);
SET_MAX(ret.x, faces[i].v[2].x);
SET_MAX(ret.y, faces[i].v[2].y);
SET_MAX(ret.z, faces[i].v[2].z);
}
return ret;
}
};
//A SimpleModel is a 3D model with 1 or more 3D volumes.
class SimpleModel
{
public:
vector<SimpleVolume> volumes;
Point3 min()
{
if (volumes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = volumes[0].min();
for(unsigned int i=0; i<volumes.size(); i++)
{
Point3 v = volumes[i].min();
SET_MIN(ret.x, v.x);
SET_MIN(ret.y, v.y);
SET_MIN(ret.z, v.z);
}
return ret;
}
Point3 max()
{
if (volumes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = volumes[0].max();
for(unsigned int i=0; i<volumes.size(); i++)
{
Point3 v = volumes[i].max();
SET_MAX(ret.x, v.x);
SET_MAX(ret.y, v.y);
SET_MAX(ret.z, v.z);
}
return ret;
}
};
SimpleModel* loadModelFromFile(SimpleModel*m,const char* filename, FMatrix3x3& matrix);
#endif//MODELFILE_H
+62 -59
Ver Arquivo
@@ -1,59 +1,62 @@
#ifndef MULTIVOLUMES_H
#define MULTIVOLUMES_H
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
void carveMultipleVolumes(vector<SliceVolumeStorage> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int idx=0; idx < volumes.size(); idx++)
{
for(unsigned int idx2=0; idx2<idx; idx2++)
{
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
{
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
}
}
}
}
}
}
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(vector<SliceVolumeStorage> &volumes, int overlap)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20));
}
}
fullLayer = fullLayer.offset(-20);
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
}
}
}
}
#endif//MULTIVOLUMES_H
#ifndef MULTIVOLUMES_H
#define MULTIVOLUMES_H
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
namespace cura {
void carveMultipleVolumes(vector<SliceVolumeStorage> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int idx=0; idx < volumes.size(); idx++)
{
for(unsigned int idx2=0; idx2<idx; idx2++)
{
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
{
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
}
}
}
}
}
}
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(vector<SliceVolumeStorage> &volumes, int overlap)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20));
}
}
fullLayer = fullLayer.offset(-20);
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
}
}
}
}
}//namespace cura
#endif//MULTIVOLUMES_H
+123 -121
Ver Arquivo
@@ -1,121 +1,123 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "optimizedModel.h"
#define MELD_DIST MM2INT(0.03)
OptimizedVolume::OptimizedVolume(SimpleVolume* volume, OptimizedModel* model)
: model(model)
{
points.reserve(volume->faces.size() * 3);
faces.reserve(volume->faces.size());
std::map<uint32_t, std::vector<uint32_t> > indexMap;
double t = getTime();
for(uint32_t i=0; i<volume->faces.size(); i++)
{
OptimizedFace f;
if((i%1000==0) && (getTime()-t)>2.0) logProgress("optimized", i + 1, volume->faces.size());
for(uint32_t j=0; j<3; j++)
{
Point3 p = volume->faces[i].v[j];
int hash = ((p.x + MELD_DIST/2) / MELD_DIST) ^ (((p.y + MELD_DIST/2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST/2) / MELD_DIST) << 20);
uint32_t idx;
bool add = true;
for(unsigned int n = 0; n < indexMap[hash].size(); n++)
{
if ((points[indexMap[hash][n]].p - p).testLength(MELD_DIST))
{
idx = indexMap[hash][n];
add = false;
break;
}
}
if (add)
{
indexMap[hash].push_back(points.size());
idx = points.size();
points.push_back(p);
}
f.index[j] = idx;
}
if (f.index[0] != f.index[1] && f.index[0] != f.index[2] && f.index[1] != f.index[2])
{
//Check if there is a face with the same points
bool duplicate = false;
for(unsigned int _idx0 = 0; _idx0 < points[f.index[0]].faceIndexList.size(); _idx0++)
{
for(unsigned int _idx1 = 0; _idx1 < points[f.index[1]].faceIndexList.size(); _idx1++)
{
for(unsigned int _idx2 = 0; _idx2 < points[f.index[2]].faceIndexList.size(); _idx2++)
{
if (points[f.index[0]].faceIndexList[_idx0] == points[f.index[1]].faceIndexList[_idx1] && points[f.index[0]].faceIndexList[_idx0] == points[f.index[2]].faceIndexList[_idx2])
duplicate = true;
}
}
}
if (!duplicate)
{
points[f.index[0]].faceIndexList.push_back(faces.size());
points[f.index[1]].faceIndexList.push_back(faces.size());
points[f.index[2]].faceIndexList.push_back(faces.size());
faces.push_back(f);
}
}
}
//fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t));
int openFacesCount = 0;
for(unsigned int i=0;i<faces.size();i++)
{
OptimizedFace* f = &faces[i];
f->touching[0] = getFaceIdxWithPoints(f->index[0], f->index[1], i);
f->touching[1] = getFaceIdxWithPoints(f->index[1], f->index[2], i);
f->touching[2] = getFaceIdxWithPoints(f->index[2], f->index[0], i);
if (f->touching[0] == -1)
openFacesCount++;
if (f->touching[1] == -1)
openFacesCount++;
if (f->touching[2] == -1)
openFacesCount++;
}
//fprintf(stdout, " Number of open faces: %i\n", openFacesCount);
}
void OptimizedModel::saveDebugSTL(const char* filename)
{
char buffer[80] = "Cura_Engine_STL_export";
uint32_t n;
uint16_t s;
float flt;
OptimizedVolume* vol = &volumes[0];
FILE* f = fopen(filename, "wb");
fwrite(buffer, 80, 1, f);
n = vol->faces.size();
fwrite(&n, sizeof(n), 1, f);
for(unsigned int i=0;i<vol->faces.size();i++)
{
flt = 0;
s = 0;
fwrite(&flt, sizeof(flt), 1, f);
fwrite(&flt, sizeof(flt), 1, f);
fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.z); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.z); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.z); fwrite(&flt, sizeof(flt), 1, f);
fwrite(&s, sizeof(s), 1, f);
}
fclose(f);
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "optimizedModel.h"
#define MELD_DIST MM2INT(0.03)
OptimizedVolume::OptimizedVolume(SimpleVolume* volume, OptimizedModel* model)
: model(model)
{
points.reserve(volume->faces.size() * 3);
faces.reserve(volume->faces.size());
std::map<uint32_t, std::vector<uint32_t> > indexMap;
double t = getTime();
for(uint32_t i=0; i<volume->faces.size(); i++)
{
OptimizedFace f;
if((i%1000==0) && (getTime()-t)>2.0) cura::logProgress("optimized", i + 1, volume->faces.size());
for(uint32_t j=0; j<3; j++)
{
Point3 p = volume->faces[i].v[j];
int hash = ((p.x + MELD_DIST/2) / MELD_DIST) ^ (((p.y + MELD_DIST/2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST/2) / MELD_DIST) << 20);
uint32_t idx;
bool add = true;
for(unsigned int n = 0; n < indexMap[hash].size(); n++)
{
if ((points[indexMap[hash][n]].p - p).testLength(MELD_DIST))
{
idx = indexMap[hash][n];
add = false;
break;
}
}
if (add)
{
indexMap[hash].push_back(points.size());
idx = points.size();
points.push_back(p);
}
f.index[j] = idx;
}
if (f.index[0] != f.index[1] && f.index[0] != f.index[2] && f.index[1] != f.index[2])
{
/*
//Check if there is a face with the same points
bool duplicate = false;
for(unsigned int _idx0 = 0; _idx0 < points[f.index[0]].faceIndexList.size(); _idx0++)
{
for(unsigned int _idx1 = 0; _idx1 < points[f.index[1]].faceIndexList.size(); _idx1++)
{
for(unsigned int _idx2 = 0; _idx2 < points[f.index[2]].faceIndexList.size(); _idx2++)
{
if (points[f.index[0]].faceIndexList[_idx0] == points[f.index[1]].faceIndexList[_idx1] && points[f.index[0]].faceIndexList[_idx0] == points[f.index[2]].faceIndexList[_idx2])
duplicate = true;
}
}
}
if (!duplicate)
{
*/
points[f.index[0]].faceIndexList.push_back(faces.size());
points[f.index[1]].faceIndexList.push_back(faces.size());
points[f.index[2]].faceIndexList.push_back(faces.size());
faces.push_back(f);
//}
}
}
//fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t));
int openFacesCount = 0;
for(unsigned int i=0;i<faces.size();i++)
{
OptimizedFace* f = &faces[i];
f->touching[0] = getFaceIdxWithPoints(f->index[0], f->index[1], i);
f->touching[1] = getFaceIdxWithPoints(f->index[1], f->index[2], i);
f->touching[2] = getFaceIdxWithPoints(f->index[2], f->index[0], i);
if (f->touching[0] == -1)
openFacesCount++;
if (f->touching[1] == -1)
openFacesCount++;
if (f->touching[2] == -1)
openFacesCount++;
}
//fprintf(stdout, " Number of open faces: %i\n", openFacesCount);
}
void OptimizedModel::saveDebugSTL(const char* filename)
{
char buffer[80] = "Cura_Engine_STL_export";
uint32_t n;
uint16_t s;
float flt;
OptimizedVolume* vol = &volumes[0];
FILE* f = fopen(filename, "wb");
fwrite(buffer, 80, 1, f);
n = vol->faces.size();
fwrite(&n, sizeof(n), 1, f);
for(unsigned int i=0;i<vol->faces.size();i++)
{
flt = 0;
s = 0;
fwrite(&flt, sizeof(flt), 1, f);
fwrite(&flt, sizeof(flt), 1, f);
fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.z); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.z); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.x); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.y); fwrite(&flt, sizeof(flt), 1, f);
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.z); fwrite(&flt, sizeof(flt), 1, f);
fwrite(&s, sizeof(s), 1, f);
}
fclose(f);
}
+85 -77
Ver Arquivo
@@ -1,77 +1,85 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef OPTIMIZED_MODEL_H
#define OPTIMIZED_MODEL_H
#include <map>
#include "modelFile/modelFile.h"
class OptimizedFace
{
public:
int index[3];
int touching[3];
};
class OptimizedPoint3
{
public:
Point3 p;
vector<uint32_t> faceIndexList;
OptimizedPoint3(Point3 p): p(p) {}
};
class OptimizedModel;
class OptimizedVolume
{
public:
OptimizedModel* model;
vector<OptimizedPoint3> points;
vector<OptimizedFace> faces;
OptimizedVolume(SimpleVolume* volume, OptimizedModel* model);
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
{
for(unsigned int i=0;i<points[idx0].faceIndexList.size();i++)
{
int f0 = points[idx0].faceIndexList[i];
if (f0 == notFaceIdx) continue;
for(unsigned int j=0;j<points[idx1].faceIndexList.size();j++)
{
int f1 = points[idx1].faceIndexList[j];
if (f1 == notFaceIdx) continue;
if (f0 == f1) return f0;
}
}
return -1;
}
};
class OptimizedModel
{
public:
vector<OptimizedVolume> volumes;
Point3 modelSize;
Point3 vMin, vMax;
OptimizedModel(SimpleModel* model, Point3 center)
{
for(unsigned int i=0; i<model->volumes.size(); i++)
volumes.push_back(OptimizedVolume(&model->volumes[i], this));
vMin = model->min();
vMax = model->max();
Point3 vOffset((vMin.x + vMax.x) / 2, (vMin.y + vMax.y) / 2, vMin.z);
vOffset -= center;
for(unsigned int i=0; i<volumes.size(); i++)
for(unsigned int n=0; n<volumes[i].points.size(); n++)
volumes[i].points[n].p -= vOffset;
modelSize = vMax - vMin;
vMin -= vOffset;
vMax -= vOffset;
}
void saveDebugSTL(const char* filename);
};
#endif//OPTIMIZED_MODEL_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef OPTIMIZED_MODEL_H
#define OPTIMIZED_MODEL_H
#include <map>
#include "modelFile/modelFile.h"
#include "settings.h"
class OptimizedFace
{
public:
int index[3];
int touching[3];
};
class OptimizedPoint3
{
public:
Point3 p;
vector<uint32_t> faceIndexList;
OptimizedPoint3(Point3 p): p(p) {}
};
class OptimizedModel;
class OptimizedVolume
{
public:
OptimizedModel* model;
vector<OptimizedPoint3> points;
vector<OptimizedFace> faces;
OptimizedVolume(SimpleVolume* volume, OptimizedModel* model);
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
{
for(unsigned int i=0;i<points[idx0].faceIndexList.size();i++)
{
int f0 = points[idx0].faceIndexList[i];
if (f0 == notFaceIdx) continue;
for(unsigned int j=0;j<points[idx1].faceIndexList.size();j++)
{
int f1 = points[idx1].faceIndexList[j];
if (f1 == notFaceIdx) continue;
if (f0 == f1) return f0;
}
}
return -1;
}
};
class OptimizedModel
{
public:
vector<OptimizedVolume> volumes;
Point3 modelSize;
Point3 vMin, vMax;
OptimizedModel(SimpleModel* model, Point3 center)
{
for(unsigned int i=0; i<model->volumes.size(); i++)
volumes.push_back(OptimizedVolume(&model->volumes[i], this));
vMin = model->min();
vMax = model->max();
Point3 vOffset((vMin.x + vMax.x) / 2, (vMin.y + vMax.y) / 2, vMin.z);
if(ConfigSettings::config->autoCenter != 1)
{
vOffset.x = 0;
vOffset.y = 0;
if(ConfigSettings::config->autoCenter == 2)
vOffset.z = 0;
}
vOffset -= center;
for(unsigned int i=0; i<volumes.size(); i++)
for(unsigned int n=0; n<volumes[i].points.size(); n++)
volumes[i].points[n].p -= vOffset;
modelSize = vMax - vMin;
vMin -= vOffset;
vMax -= vOffset;
}
void saveDebugSTL(const char* filename);
};
#endif//OPTIMIZED_MODEL_H
+160
Ver Arquivo
@@ -0,0 +1,160 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <map>
#include "pathOrderOptimizer.h"
namespace cura {
static uint32_t hashPoint(const Point& p)
{
return (p.X / 20000) ^ (p.Y / 20000) << 8;
}
void PathOrderOptimizer::optimize()
{
const float incommingPerpundicularNormalScale = 0.0001f;
std::map<uint32_t, std::vector<unsigned int>> location_to_polygon_map;
std::vector<bool> picked;
for(unsigned int i=0;i<polygons.size(); i++)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
PolygonRef poly = polygons[i];
for(unsigned int j=0; j<poly.size(); j++)
{
float dist = vSize2f(poly[j] - startPoint);
if (dist < bestDist)
{
best = j;
bestDist = dist;
}
}
polyStart.push_back(best);
picked.push_back(false);
if (poly.size() == 2)
{
location_to_polygon_map[hashPoint(poly[0])].push_back(i);
location_to_polygon_map[hashPoint(poly[1])].push_back(i);
}
}
Point incommingPerpundicularNormal(0, 0);
Point p0 = startPoint;
for(unsigned int n=0; n<polygons.size(); n++)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
for(unsigned int i : location_to_polygon_map[hashPoint(p0)])
{
if (picked[i] || polygons[i].size() < 1)
continue;
float dist = vSize2f(polygons[i][0] - p0);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale;
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 0;
}
dist = vSize2f(polygons[i][1] - p0);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale;
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 1;
}
}
if (best == -1)
{
for(unsigned int i=0;i<polygons.size(); i++)
{
if (picked[i] || polygons[i].size() < 1)
continue;
if (polygons[i].size() == 2)
{
float dist = vSize2f(polygons[i][0] - p0);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale;
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 0;
}
dist = vSize2f(polygons[i][1] - p0);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale;
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 1;
}
}else{
float dist = vSize2f(polygons[i][polyStart[i]] - p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
}
}
}
}
if (best > -1)
{
if (polygons[best].size() == 2)
{
int endIdx = (polyStart[best] + 1) % 2;
p0 = polygons[best][endIdx];
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
}else{
p0 = polygons[best][polyStart[best]];
incommingPerpundicularNormal = Point(0, 0);
}
picked[best] = true;
polyOrder.push_back(best);
}
}
p0 = startPoint;
for(int nr : polyOrder)
{
PolygonRef poly = polygons[nr];
if (poly.size() > 2)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
bool orientation = poly.orientation();
for(unsigned int i=0;i<poly.size(); i++)
{
const int64_t dot_score_scale = 2000;
float dist = vSize2f(polygons[nr][i] - p0);
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], dot_score_scale);
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], dot_score_scale);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
if (orientation)
dot_score = -dot_score;
dist += dot_score;
if (dist < bestDist)
{
best = i;
bestDist = dist;
}
}
polyStart[nr] = best;
}
if (poly.size() <= 2)
{
p0 = poly[(polyStart[nr] + 1) % 2];
}else{
p0 = poly[polyStart[nr]];
}
}
}
}//namespace cura
+39 -35
Ver Arquivo
@@ -1,35 +1,39 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATHOPTIMIZER_H
#define PATHOPTIMIZER_H
#include <stdint.h>
#include "utils/polygon.h"
class PathOrderOptimizer
{
public:
Point startPoint;
vector<PolygonRef> polygons;
vector<int> polyStart;
vector<int> polyOrder;
PathOrderOptimizer(Point startPoint)
{
this->startPoint = startPoint;
}
void addPolygon(PolygonRef polygon)
{
this->polygons.push_back(polygon);
}
void addPolygons(Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(polygons[i]);
}
void optimize();
};
#endif//PATHOPTIMIZER_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATHOPTIMIZER_H
#define PATHOPTIMIZER_H
#include <stdint.h>
#include "utils/polygon.h"
namespace cura {
class PathOrderOptimizer
{
public:
Point startPoint;
vector<PolygonRef> polygons;
vector<int> polyStart;
vector<int> polyOrder;
PathOrderOptimizer(Point startPoint)
{
this->startPoint = startPoint;
}
void addPolygon(PolygonRef polygon)
{
this->polygons.push_back(polygon);
}
void addPolygons(Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(polygons[i]);
}
void optimize();
};
}//namespace cura
#endif//PATHOPTIMIZER_H
+54 -48
Ver Arquivo
@@ -1,48 +1,54 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "polygonOptimizer.h"
void optimizePolygon(PolygonRef poly)
{
Point p0 = poly[poly.size()-1];
for(unsigned int i=0;i<poly.size();i++)
{
Point p1 = poly[i];
if (shorterThen(p0 - p1, MICRON2INT(10)))
{
poly.remove(i);
i --;
}else{
/*
Point p2;
if (i < poly.size() - 1)
p2 = poly[i+1];
else
p2 = poly[0];
Point diff0 = normal(p1 - p0, 10000000);
Point diff2 = normal(p1 - p2, 10000000);
int64_t d = dot(diff0, diff2);
if (d < -99999999999999LL)
{
poly.remove(i);
i --;
}else{*/
p0 = p1;
/*}*/
}
}
}
void optimizePolygons(Polygons& polys)
{
for(unsigned int n=0;n<polys.size();n++)
{
optimizePolygon(polys[n]);
if (polys[n].size() < 3)
{
polys.remove(n);
n--;
}
}
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "polygonOptimizer.h"
namespace cura {
void optimizePolygon(PolygonRef poly)
{
Point p0 = poly[poly.size()-1];
for(unsigned int i=0;i<poly.size();i++)
{
Point p1 = poly[i];
if (shorterThen(p0 - p1, MICRON2INT(10)))
{
poly.remove(i);
i --;
}else if (shorterThen(p0 - p1, MICRON2INT(500)))
{
Point p2;
if (i < poly.size() - 1)
p2 = poly[i+1];
else
p2 = poly[0];
Point diff0 = normal(p1 - p0, 10000000);
Point diff2 = normal(p1 - p2, 10000000);
int64_t d = dot(diff0, diff2);
if (d < -99999999999999LL)
{
poly.remove(i);
i --;
}else{
p0 = p1;
}
}else{
p0 = p1;
}
}
}
void optimizePolygons(Polygons& polys)
{
for(unsigned int n=0;n<polys.size();n++)
{
optimizePolygon(polys[n]);
if (polys[n].size() < 3)
{
polys.remove(n);
n--;
}
}
}
}//namespace cura
+15 -11
Ver Arquivo
@@ -1,11 +1,15 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef POLYGON_OPTIMIZER_H
#define POLYGON_OPTIMIZER_H
#include "utils/polygon.h"
void optimizePolygon(PolygonRef poly);
void optimizePolygons(Polygons& polys);
#endif//POLYGON_OPTIMIZER_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef POLYGON_OPTIMIZER_H
#define POLYGON_OPTIMIZER_H
#include "utils/polygon.h"
namespace cura {
void optimizePolygon(PolygonRef poly);
void optimizePolygons(Polygons& polys);
}//namespace cura
#endif//POLYGON_OPTIMIZER_H
+24 -20
Ver Arquivo
@@ -1,20 +1,24 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "raft.h"
#include "support.h"
void generateRaft(SliceDataStorage& storage, int distance)
{
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
storage.raftOutline = storage.raftOutline.unionPolygons(layer->parts[i].outline.offset(distance));
}
}
SupportPolyGenerator supportGenerator(storage.support, 0);
storage.raftOutline = storage.raftOutline.unionPolygons(supportGenerator.polygons.offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.wipeTower.offset(distance));
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "raft.h"
#include "support.h"
namespace cura {
void generateRaft(SliceDataStorage& storage, int distance)
{
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
storage.raftOutline = storage.raftOutline.unionPolygons(layer->parts[i].outline.offset(distance));
}
}
SupportPolyGenerator supportGenerator(storage.support, 0);
storage.raftOutline = storage.raftOutline.unionPolygons(supportGenerator.polygons.offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.wipeTower.offset(distance));
}
}//namespace cura
+13 -9
Ver Arquivo
@@ -1,9 +1,13 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef RAFT_H
#define RAFT_H
#include "sliceDataStorage.h"
void generateRaft(SliceDataStorage& storage, int distance);
#endif//RAFT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef RAFT_H
#define RAFT_H
#include "sliceDataStorage.h"
namespace cura {
void generateRaft(SliceDataStorage& storage, int distance);
}//namespace cura
#endif//RAFT_H
+273 -214
Ver Arquivo
@@ -1,217 +1,276 @@
#include <cctype>
#include <fstream>
#include <stdio.h>
#include "utils/logoutput.h"
#include "settings.h"
#define LTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[0])) { (s).erase(0, 1); } } while(0)
#define RTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[(s).length() - 1])) { (s).erase((s).length() - 1); } } while(0)
#define TRIM_STRING(s) do { LTRIM_STRING(s); RTRIM_STRING(s); } while(0)
#define STRINGIFY(_s) #_s
#define SETTING(name, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); name = (default); } while(0)
#define SETTING2(name, altname, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); _index.push_back(_ConfigSettingIndex(STRINGIFY(altname), &name)); name = (default); } while(0)
ConfigSettings::ConfigSettings()
{
SETTING(layerThickness, 100);
SETTING(initialLayerThickness, 300);
SETTING(filamentDiameter, 2890);
SETTING(filamentFlow, 100);
SETTING(extrusionWidth, 400);
SETTING(insetCount, 2);
SETTING(downSkinCount, 6);
SETTING(upSkinCount, 6);
SETTING(sparseInfillLineDistance, 100 * extrusionWidth / 20);
SETTING(infillOverlap, 15);
SETTING(skirtDistance, 6000);
SETTING(skirtLineCount, 1);
SETTING(skirtMinLength, 0);
SETTING(initialSpeedupLayers, 4);
SETTING(initialLayerSpeed, 20);
SETTING(printSpeed, 50);
SETTING(infillSpeed, 50);
SETTING(inset0Speed, 50);
SETTING(insetXSpeed, 50);
SETTING(moveSpeed, 150);
SETTING(fanFullOnLayerNr, 2);
SETTING(supportType, 0);
SETTING(supportAngle, -1);
SETTING(supportEverywhere, 0);
SETTING(supportLineDistance, sparseInfillLineDistance);
SETTING(supportXYDistance, 700);
SETTING(supportZDistance, 150);
SETTING(supportExtruder, -1);
SETTING(retractionAmount, 4500);
SETTING(retractionSpeed, 45);
SETTING(retractionAmountExtruderSwitch, 14500);
SETTING(retractionMinimalDistance, 1500);
SETTING(minimalExtrusionBeforeRetraction, 100);
SETTING(retractionZHop, 0);
SETTING(enableCombing, 1);
SETTING(enableOozeShield, 0);
SETTING(wipeTowerSize, 0);
SETTING(multiVolumeOverlap, 0);
SETTING2(objectPosition.X, posx, 102500);
SETTING2(objectPosition.Y, posy, 102500);
SETTING(objectSink, 0);
SETTING(raftMargin, 5000);
SETTING(raftLineSpacing, 1000);
SETTING(raftBaseThickness, 0);
SETTING(raftBaseLinewidth, 0);
SETTING(raftInterfaceThickness, 0);
SETTING(raftInterfaceLinewidth, 0);
SETTING(minimalLayerTime, 5);
SETTING(minimalFeedrate, 10);
SETTING(coolHeadLift, 0);
SETTING(fanSpeedMin, 100);
SETTING(fanSpeedMax, 100);
SETTING(fixHorrible, 0);
SETTING(spiralizeMode, 0);
SETTING(gcodeFlavor, GCODE_FLAVOR_REPRAP);
memset(extruderOffset, 0, sizeof(extruderOffset));
SETTING(extruderOffset[1].X, 0);
SETTING(extruderOffset[1].Y, 0);
SETTING(extruderOffset[2].X, 0);
SETTING(extruderOffset[2].Y, 0);
SETTING(extruderOffset[3].X, 0);
SETTING(extruderOffset[3].Y, 0);
startCode =
"M109 S210 ;Heatup to 210C\n"
"G21 ;metric values\n"
"G90 ;absolute positioning\n"
"G28 ;Home\n"
"G1 Z15.0 F300 ;move the platform down 15mm\n"
"G92 E0 ;zero the extruded length\n"
"G1 F200 E5 ;extrude 5mm of feed stock\n"
"G92 E0 ;zero the extruded length again\n";
endCode =
"M104 S0 ;extruder heater off\n"
"M140 S0 ;heated bed heater off (if you have it)\n"
"G91 ;relative positioning\n"
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n"
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\n"
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n"
"M84 ;steppers off\n"
"G90 ;absolute positioning\n";
}
#undef STRINGIFY
#undef SETTING
bool ConfigSettings::setSetting(const char* key, const char* value)
{
for(unsigned int n=0; n < _index.size(); n++)
{
if (strcasecmp(key, _index[n].key) == 0)
{
*_index[n].ptr = atoi(value);
return true;
}
}
if (strcasecmp(key, "startCode") == 0)
{
this->startCode = value;
return true;
}
if (strcasecmp(key, "endCode") == 0)
{
this->endCode = value;
return true;
}
return false;
}
bool ConfigSettings::readSettings(void) {
return readSettings(DEFAULT_CONFIG_PATH);
}
bool ConfigSettings::readSettings(const char* path) {
std::ifstream config(path);
std::string line;
size_t line_number = 0;
if(!config.good()) return false;
#include <cctype>
#include <fstream>
#include <stdio.h>
#include "utils/logoutput.h"
#include "utils/string.h"
#include "settings.h"
#define LTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[0])) { (s).erase(0, 1); } } while(0)
#define RTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[(s).length() - 1])) { (s).erase((s).length() - 1); } } while(0)
#define TRIM_STRING(s) do { LTRIM_STRING(s); RTRIM_STRING(s); } while(0)
#define STRINGIFY(_s) #_s
#define SETTING(name, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); name = (default); } while(0)
#define SETTING2(name, altname, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); _index.push_back(_ConfigSettingIndex(STRINGIFY(altname), &name)); name = (default); } while(0)
ConfigSettings *ConfigSettings::config = NULL;
ConfigSettings::ConfigSettings()
{
config = this;
SETTING(layerThickness, 100);
SETTING(initialLayerThickness, 300);
SETTING(filamentDiameter, 2890);
SETTING(filamentFlow, 100);
SETTING(layer0extrusionWidth, 600);
SETTING(extrusionWidth, 400);
SETTING(insetCount, 2);
SETTING(downSkinCount, 6);
SETTING(upSkinCount, 6);
SETTING(skirtDistance, 6000);
SETTING(skirtLineCount, 1);
SETTING(skirtMinLength, 0);
SETTING(initialSpeedupLayers, 4);
SETTING(initialLayerSpeed, 20);
SETTING(printSpeed, 50);
SETTING(inset0Speed, 50);
SETTING(insetXSpeed, 50);
SETTING(moveSpeed, 150);
SETTING(fanFullOnLayerNr, 2);
SETTING(sparseInfillLineDistance, 100 * extrusionWidth / 20);
SETTING(infillOverlap, 15);
SETTING(infillSpeed, 50);
SETTING(infillPattern, INFILL_AUTOMATIC);
SETTING(skinSpeed, 50);
SETTING(supportType, SUPPORT_TYPE_GRID);
SETTING(supportAngle, -1);
SETTING(supportEverywhere, 0);
SETTING(supportLineDistance, sparseInfillLineDistance);
SETTING(supportXYDistance, 700);
SETTING(supportZDistance, 150);
SETTING(supportExtruder, -1);
SETTING(retractionAmount, 4500);
SETTING(retractionAmountPrime, 0);
SETTING(retractionSpeed, 45);
SETTING(retractionAmountExtruderSwitch, 14500);
SETTING(retractionMinimalDistance, 1500);
SETTING(minimalExtrusionBeforeRetraction, 100);
SETTING(retractionZHop, 0);
SETTING(enableCombing, COMBING_ALL);
SETTING(enableOozeShield, 0);
SETTING(wipeTowerSize, 0);
SETTING(multiVolumeOverlap, 0);
SETTING2(objectPosition.X, posx, 102500);
SETTING2(objectPosition.Y, posy, 102500);
SETTING(objectSink, 0);
SETTING(autoCenter, 1);
SETTING(raftMargin, 5000);
SETTING(raftLineSpacing, 1000);
SETTING(raftBaseThickness, 0);
SETTING(raftBaseLinewidth, 0);
SETTING(raftInterfaceThickness, 0);
SETTING(raftInterfaceLinewidth, 0);
SETTING(raftInterfaceLineSpacing, 0);
SETTING(raftAirGap, 0);
SETTING(raftAirGapLayer0, 0);
SETTING(raftBaseSpeed, 0);
SETTING(raftFanSpeed, 0);
SETTING(raftSurfaceThickness, 0);
SETTING(raftSurfaceLinewidth, 0);
SETTING(raftSurfaceLineSpacing, 0);
SETTING(raftSurfaceLayers, 0);
SETTING(raftSurfaceSpeed, 0);
SETTING(minimalLayerTime, 5);
SETTING(minimalFeedrate, 10);
SETTING(coolHeadLift, 0);
SETTING(fanSpeedMin, 100);
SETTING(fanSpeedMax, 100);
SETTING(fixHorrible, 0);
SETTING(spiralizeMode, 0);
SETTING(simpleMode, 0);
SETTING(gcodeFlavor, GCODE_FLAVOR_REPRAP);
memset(extruderOffset, 0, sizeof(extruderOffset));
SETTING(extruderOffset[0].X, 0); // No one says that extruder 0 can not have an offset!
SETTING(extruderOffset[0].Y, 0);
SETTING(extruderOffset[1].X, 0);
SETTING(extruderOffset[1].Y, 0);
SETTING(extruderOffset[2].X, 0);
SETTING(extruderOffset[2].Y, 0);
SETTING(extruderOffset[3].X, 0);
SETTING(extruderOffset[3].Y, 0);
SETTING(extruderOffset[4].X, 0);
SETTING(extruderOffset[4].Y, 0);
SETTING(extruderOffset[5].X, 0);
SETTING(extruderOffset[5].Y, 0);
SETTING(extruderOffset[6].X, 0);
SETTING(extruderOffset[6].Y, 0);
SETTING(extruderOffset[7].X, 0);
SETTING(extruderOffset[7].Y, 0);
SETTING(extruderOffset[8].X, 0);
SETTING(extruderOffset[8].Y, 0);
SETTING(extruderOffset[9].X, 0);
SETTING(extruderOffset[9].Y, 0);
SETTING(extruderOffset[10].X, 0);
SETTING(extruderOffset[10].Y, 0);
SETTING(extruderOffset[11].X, 0);
SETTING(extruderOffset[11].Y, 0);
SETTING(extruderOffset[12].X, 0);
SETTING(extruderOffset[12].Y, 0);
SETTING(extruderOffset[13].X, 0);
SETTING(extruderOffset[13].Y, 0);
SETTING(extruderOffset[14].X, 0);
SETTING(extruderOffset[14].Y, 0);
SETTING(extruderOffset[15].X, 0);
SETTING(extruderOffset[15].Y, 0);
startCode =
"M109 S210 ;Heatup to 210C\n"
"G21 ;metric values\n"
"G90 ;absolute positioning\n"
"G28 ;Home\n"
"G1 Z15.0 F300 ;move the platform down 15mm\n"
"G92 E0 ;zero the extruded length\n"
"G1 F200 E5 ;extrude 5mm of feed stock\n"
"G92 E0 ;zero the extruded length again\n";
endCode =
"M104 S0 ;extruder heater off\n"
"M140 S0 ;heated bed heater off (if you have it)\n"
"G91 ;relative positioning\n"
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n"
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\n"
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n"
"M84 ;steppers off\n"
"G90 ;absolute positioning\n";
}
#undef STRINGIFY
#undef SETTING
bool ConfigSettings::setSetting(const char* key, const char* value)
{
for(unsigned int n=0; n < _index.size(); n++)
{
if (stringcasecompare(key, _index[n].key) == 0)
{
*_index[n].ptr = atoi(value);
return true;
}
}
if (stringcasecompare(key, "startCode") == 0)
{
this->startCode = value;
return true;
}
if (stringcasecompare(key, "endCode") == 0)
{
this->endCode = value;
return true;
}
if (stringcasecompare(key, "preSwitchExtruderCode") == 0)
{
this->preSwitchExtruderCode = value;
return true;
}
if (stringcasecompare(key, "postSwitchExtruderCode") == 0)
{
this->postSwitchExtruderCode = value;
return true;
}
return false;
}
bool ConfigSettings::readSettings(void) {
return readSettings(DEFAULT_CONFIG_PATH);
}
bool ConfigSettings::readSettings(const char* path) {
std::ifstream config(path);
std::string line;
size_t line_number = 0;
if(!config.good()) return false;
while(config.good()) {
size_t pos = std::string::npos;
std::getline(config, line);
line_number += 1;
// De-comment and trim, skipping anything that shows up empty
pos = line.find_first_of('#');
if(pos != std::string::npos) line.erase(pos);
TRIM_STRING(line);
if(line.length() == 0) continue;
// Split into key = val
std::string key(""), val("");
pos = line.find_first_of('=');
if(pos != std::string::npos && line.length() > (pos + 1)) {
key = line.substr(0, pos);
val = line.substr(pos + 1);
TRIM_STRING(key);
TRIM_STRING(val);
}
// Are we about to read a multiline string?
if(val == CONFIG_MULTILINE_SEPARATOR) {
bool multilineContent = false;
size_t pos = std::string::npos;
std::getline(config, line);
line_number += 1;
// De-comment and trim, skipping anything that shows up empty
pos = line.find_first_of('#');
if(pos != std::string::npos) line.erase(pos);
TRIM_STRING(line);
if(line.length() == 0) continue;
// Split into key = val
std::string key(""), val("");
pos = line.find_first_of('=');
if(pos != std::string::npos && line.length() > (pos + 1)) {
key = line.substr(0, pos);
val = line.substr(pos + 1);
TRIM_STRING(key);
TRIM_STRING(val);
}
// Are we about to read a multiline string?
if(val == CONFIG_MULTILINE_SEPARATOR) {
val = "";
bool done_multiline = false;
while(config.good() && !done_multiline) {
std::getline(config, line);
line_number += 1;
// We RTRIM the line for two reasons:
//
// 1) Make sure that a direct == comparison with '"""' works without
// worrying about trailing space.
// 2) Nobody likes trailing whitespace anyway
RTRIM_STRING(line);
// Either accumuliate or terminate
if(line == CONFIG_MULTILINE_SEPARATOR) {
done_multiline = true;
// Make sure we don't add an extra trailing newline
// to the parsed value
RTRIM_STRING(val);
}
multilineContent = true;
bool done_multiline = false;
while(config.good() && !done_multiline) {
std::getline(config, line);
line_number += 1;
// We RTRIM the line for two reasons:
//
// 1) Make sure that a direct == comparison with '"""' works without
// worrying about trailing space.
// 2) Nobody likes trailing whitespace anyway
RTRIM_STRING(line);
// Either accumuliate or terminate
if(line == CONFIG_MULTILINE_SEPARATOR) {
done_multiline = true;
// Make sure we don't add an extra trailing newline
// to the parsed value
RTRIM_STRING(val);
}
else {
line += "\n";
val += line;
}
}
// If we drop out but didn't finish reading, something failed
if(!done_multiline) {
logError("Config(%s):L%zd: Failed while reading multiline string.\n", path, line_number);
return false;
}
}
// Fail if we don't get a key and val
if(key.length() == 0 || val.length() == 0) {
logError("Config(%s): Line %zd: No key value pair found\n", path, line_number);
return false;
}
// Set a config setting for the current K=V
if(!setSetting(key.c_str(), val.c_str())) {
logError("Config(%s):L%zd: Failed to set '%s' to '%s'\n", path, line_number, key.c_str(), val.c_str());
return false;
}
}
return true;
}
line += "\n";
val += line;
}
}
// If we drop out but didn't finish reading, something failed
if(!done_multiline) {
cura::logError("Config(%s):L%zd: Failed while reading multiline string.\n", path, line_number);
return false;
}
}
// Fail if we don't get a key and val
if(key.length() == 0 || (val.length() == 0 && !multilineContent)) {
cura::logError("Config(%s): Line %zd: No key value pair found\n", path, line_number);
return false;
}
// Set a config setting for the current K=V
if(!setSetting(key.c_str(), val.c_str())) {
cura::logError("Config(%s):L%zd: Failed to set '%s' to '%s'\n", path, line_number, key.c_str(), val.c_str());
return false;
}
}
return true;
}
+227 -156
Ver Arquivo
@@ -1,156 +1,227 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <vector>
#include "utils/floatpoint.h"
#ifndef VERSION
#define VERSION "DEV"
#endif
#define FIX_HORRIBLE_UNION_ALL_TYPE_A 0x01
#define FIX_HORRIBLE_UNION_ALL_TYPE_B 0x02
#define FIX_HORRIBLE_EXTENSIVE_STITCHING 0x04
#define FIX_HORRIBLE_UNION_ALL_TYPE_C 0x08
#define FIX_HORRIBLE_KEEP_NONE_CLOSED 0x10
/**
* Type of support material.
* Grid is a X/Y grid with an outline, which is very strong, provides good support. But in some cases is hard to remove.
* Lines give a row of lines which break off one at a time, making them easier to remove, but they do not support as good as the grid support.
*/
#define SUPPORT_TYPE_GRID 0
#define SUPPORT_TYPE_LINES 1
#ifndef DEFAULT_CONFIG_PATH
#define DEFAULT_CONFIG_PATH "default.cfg"
#endif
#define CONFIG_MULTILINE_SEPARATOR "\"\"\""
/**
* RepRap flavored GCode is Marlin/Sprinter/Repetier based GCode.
* This is the most commonly used GCode set.
* G0 for moves, G1 for extrusion.
* E values give mm of filament extrusion.
* Retraction is done on E values with G1. Start/end code is added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
#define GCODE_FLAVOR_REPRAP 0
/**
* UltiGCode flavored is Marlin based GCode.
* UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode.
* G0 for moves, G1 for extrusion.
* E values give mm^3 of filament extrusion. Ignores the filament diameter setting.
* Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction.
* Start/end code is not added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
#define GCODE_FLAVOR_ULTIGCODE 1
/**
* Makerbot flavored GCode.
* Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files.
* Heating needs to be done with M104 Sxxx T0
* No G21 or G90
* Fan ON is M126 T0 (No fan strength control?)
* Fan OFF is M127 T0
* Homing is done with G162 X Y F2000
**/
#define GCODE_FLAVOR_MAKERBOT 2
#define MAX_EXTRUDERS 16
class _ConfigSettingIndex
{
public:
const char* key;
int* ptr;
_ConfigSettingIndex(const char* key, int* ptr) : key(key), ptr(ptr) {}
};
class ConfigSettings
{
private:
std::vector<_ConfigSettingIndex> _index;
public:
int layerThickness;
int initialLayerThickness;
int filamentDiameter;
int filamentFlow;
int extrusionWidth;
int insetCount;
int downSkinCount;
int upSkinCount;
int sparseInfillLineDistance;
int infillOverlap;
int skirtDistance;
int skirtLineCount;
int skirtMinLength;
//Retraction settings
int retractionAmount;
int retractionAmountExtruderSwitch;
int retractionSpeed;
int retractionMinimalDistance;
int minimalExtrusionBeforeRetraction;
int retractionZHop;
int enableCombing;
int enableOozeShield;
int wipeTowerSize;
int multiVolumeOverlap;
int initialSpeedupLayers;
int initialLayerSpeed;
int printSpeed;
int infillSpeed;
int inset0Speed;
int insetXSpeed;
int moveSpeed;
int fanFullOnLayerNr;
//Support material
int supportType;
int supportAngle;
int supportEverywhere;
int supportLineDistance;
int supportXYDistance;
int supportZDistance;
int supportExtruder;
//Cool settings
int minimalLayerTime;
int minimalFeedrate;
int coolHeadLift;
int fanSpeedMin;
int fanSpeedMax;
//Raft settings
int raftMargin;
int raftLineSpacing;
int raftBaseThickness;
int raftBaseLinewidth;
int raftInterfaceThickness;
int raftInterfaceLinewidth;
FMatrix3x3 matrix;
IntPoint objectPosition;
int objectSink;
int fixHorrible;
int spiralizeMode;
int gcodeFlavor;
IntPoint extruderOffset[MAX_EXTRUDERS];
std::string startCode;
std::string endCode;
ConfigSettings();
bool setSetting(const char* key, const char* value);
bool readSettings(void);
bool readSettings(const char* path);
};
#endif//SETTINGS_H
#ifndef SETTINGS_H
#define SETTINGS_H
#include <vector>
#include "utils/floatpoint.h"
#ifndef VERSION
#define VERSION "DEV"
#endif
#define FIX_HORRIBLE_UNION_ALL_TYPE_A 0x01
#define FIX_HORRIBLE_UNION_ALL_TYPE_B 0x02
#define FIX_HORRIBLE_EXTENSIVE_STITCHING 0x04
#define FIX_HORRIBLE_UNION_ALL_TYPE_C 0x08
#define FIX_HORRIBLE_KEEP_NONE_CLOSED 0x10
/**
* Type of support material.
* Grid is a X/Y grid with an outline, which is very strong, provides good support. But in some cases is hard to remove.
* Lines give a row of lines which break off one at a time, making them easier to remove, but they do not support as good as the grid support.
*/
enum Support_Pattern
{
SUPPORT_TYPE_GRID = 0,
SUPPORT_TYPE_LINES = 1
};
#ifndef DEFAULT_CONFIG_PATH
#define DEFAULT_CONFIG_PATH "default.cfg"
#endif
#define CONFIG_MULTILINE_SEPARATOR "\"\"\""
enum GCode_Flavor
{
/**
* RepRap flavored GCode is Marlin/Sprinter/Repetier based GCode.
* This is the most commonly used GCode set.
* G0 for moves, G1 for extrusion.
* E values give mm of filament extrusion.
* Retraction is done on E values with G1. Start/end code is added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_REPRAP = 0,
/**
* UltiGCode flavored is Marlin based GCode.
* UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode.
* G0 for moves, G1 for extrusion.
* E values give mm^3 of filament extrusion. Ignores the filament diameter setting.
* Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction.
* Start/end code is not added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_ULTIGCODE = 1,
/**
* Makerbot flavored GCode.
* Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files.
* Heating needs to be done with M104 Sxxx T0
* No G21 or G90
* Fan ON is M126 T0 (No fan strength control?)
* Fan OFF is M127 T0
* Homing is done with G162 X Y F2000
**/
GCODE_FLAVOR_MAKERBOT = 2,
/**
* Bits From Bytes GCode.
* BFB machines use RPM instead of E. Which is coupled to the F instead of independed. (M108 S[deciRPM])
* Need X,Y,Z,F on every line.
* Needs extruder ON/OFF (M101, M103), has auto-retrection (M227 S[2560*mm] P[2560*mm])
**/
GCODE_FLAVOR_BFB = 3,
/**
* MACH3 GCode
* MACH3 is CNC control software, which expects A/B/C/D for extruders, instead of E.
**/
GCODE_FLAVOR_MACH3 = 4,
/**
* RepRap volumatric flavored GCode is Marlin based GCode.
* Volumatric uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode.
* G0 for moves, G1 for extrusion.
* E values give mm^3 of filament extrusion. Ignores the filament diameter setting.
* Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_REPRAP_VOLUMATRIC = 5,
};
#define MAX_EXTRUDERS 16
/**
* Type of infill pattern.
*/
enum Infill_Pattern
{
INFILL_AUTOMATIC = 0,
INFILL_GRID = 1,
INFILL_LINES = 2,
INFILL_CONCENTRIC = 3,
};
/**
* Where to use the combing feature
*/
enum Combing_Feature
{
COMBING_OFF = 0,
COMBING_ALL = 1,
COMBING_NOSKIN = 2,
};
class _ConfigSettingIndex
{
public:
const char* key;
int* ptr;
_ConfigSettingIndex(const char* key, int* ptr) : key(key), ptr(ptr) {}
};
class ConfigSettings
{
private:
std::vector<_ConfigSettingIndex> _index;
public:
static ConfigSettings *config; // allow access to config settings from everywhere
int layerThickness;
int initialLayerThickness;
int filamentDiameter;
int filamentFlow;
int layer0extrusionWidth;
int extrusionWidth;
int insetCount;
int downSkinCount;
int upSkinCount;
int skirtDistance;
int skirtLineCount;
int skirtMinLength;
//Retraction settings
int retractionAmount;
int retractionAmountPrime;
int retractionAmountExtruderSwitch;
int retractionSpeed;
int retractionMinimalDistance;
int minimalExtrusionBeforeRetraction;
int retractionZHop;
int enableCombing;
int enableOozeShield;
int wipeTowerSize;
int multiVolumeOverlap;
int initialSpeedupLayers;
int initialLayerSpeed;
int printSpeed;
int inset0Speed;
int insetXSpeed;
int moveSpeed;
int fanFullOnLayerNr;
//Infill settings
int sparseInfillLineDistance;
int infillOverlap;
int infillSpeed;
int infillPattern;
int skinSpeed;
//Support material
int supportType;
int supportAngle;
int supportEverywhere;
int supportLineDistance;
int supportXYDistance;
int supportZDistance;
int supportExtruder;
//Cool settings
int minimalLayerTime;
int minimalFeedrate;
int coolHeadLift;
int fanSpeedMin;
int fanSpeedMax;
//Raft settings
int raftMargin;
int raftLineSpacing;
int raftBaseThickness;
int raftBaseLinewidth;
int raftBaseSpeed;
int raftInterfaceThickness;
int raftInterfaceLinewidth;
int raftInterfaceLineSpacing;
int raftFanSpeed;
int raftSurfaceThickness;
int raftSurfaceLinewidth;
int raftSurfaceLineSpacing;
int raftSurfaceLayers;
int raftSurfaceSpeed;
int raftAirGap;
int raftAirGapLayer0;
FMatrix3x3 matrix;
IntPoint objectPosition;
int objectSink;
int autoCenter;
int fixHorrible;
int spiralizeMode;
int simpleMode;
int gcodeFlavor;
IntPoint extruderOffset[MAX_EXTRUDERS];
std::string startCode;
std::string endCode;
std::string preSwitchExtruderCode;
std::string postSwitchExtruderCode;
ConfigSettings();
bool setSetting(const char* key, const char* value);
bool readSettings(void);
bool readSettings(const char* path);
};
#endif//SETTINGS_H
+120 -116
Ver Arquivo
@@ -1,116 +1,120 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skin.h"
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap)
{
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons upskin = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
Polygons downskin = upskin;
if (part->insets.size() > 1)
{
//Add thin wall filling by taking the area between the insets.
Polygons thinWalls = part->insets[0].offset(-extrusionWidth / 2 - extrusionWidth * infillOverlap / 100).difference(part->insets[1].offset(extrusionWidth * 6 / 10));
upskin.add(thinWalls);
downskin.add(thinWalls);
}
if (int(layerNr - downSkinCount) >= 0)
{
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
if (int(layerNr + upSkinCount) < (int)storage.layers.size())
{
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
part->skinOutline = upskin.unionPolygons(downskin);
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3;
for(unsigned int i=0; i<part->skinOutline.size(); i++)
{
double area = INT2MM(INT2MM(fabs(part->skinOutline[i].area())));
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
{
part->skinOutline.remove(i);
i -= 1;
}
}
}
}
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons sparse = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
Polygons downskin = sparse;
Polygons upskin = sparse;
if (int(layerNr - downSkinCount) >= 0)
{
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
{
if (layer2->parts[partNr2].insets.size() > 1)
{
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
}else{
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
}
}
if (int(layerNr + upSkinCount) < (int)storage.layers.size())
{
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
{
if (layer2->parts[partNr2].insets.size() > 1)
{
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
}else{
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
}
}
Polygons result = upskin.unionPolygons(downskin);
double minAreaSize = 3.0;//(2 * M_PI * INT2MM(config.extrusionWidth) * INT2MM(config.extrusionWidth)) * 3;
for(unsigned int i=0; i<result.size(); i++)
{
double area = INT2MM(INT2MM(fabs(result[i].area())));
if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
{
result.remove(i);
i -= 1;
}
}
part->sparseOutline = sparse.difference(result);
}
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skin.h"
namespace cura {
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap)
{
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons upskin = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
Polygons downskin = upskin;
if (part->insets.size() > 1)
{
//Add thin wall filling by taking the area between the insets.
Polygons thinWalls = part->insets[0].offset(-extrusionWidth / 2 - extrusionWidth * infillOverlap / 100).difference(part->insets[1].offset(extrusionWidth * 6 / 10));
upskin.add(thinWalls);
downskin.add(thinWalls);
}
if (static_cast<int>(layerNr - downSkinCount) >= 0)
{
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
{
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
part->skinOutline = upskin.unionPolygons(downskin);
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3;
for(unsigned int i=0; i<part->skinOutline.size(); i++)
{
double area = INT2MM(INT2MM(fabs(part->skinOutline[i].area())));
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
{
part->skinOutline.remove(i);
i -= 1;
}
}
}
}
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons sparse = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
Polygons downskin = sparse;
Polygons upskin = sparse;
if (static_cast<int>(layerNr - downSkinCount) >= 0)
{
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
{
if (layer2->parts[partNr2].insets.size() > 1)
{
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
}else{
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
}
}
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
{
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
{
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
{
if (layer2->parts[partNr2].insets.size() > 1)
{
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
}else{
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
}
}
}
}
Polygons result = upskin.unionPolygons(downskin);
double minAreaSize = 3.0;//(2 * M_PI * INT2MM(config.extrusionWidth) * INT2MM(config.extrusionWidth)) * 3;
for(unsigned int i=0; i<result.size(); i++)
{
double area = INT2MM(INT2MM(fabs(result[i].area())));
if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
{
result.remove(i);
i -= 1;
}
}
part->sparseOutline = sparse.difference(result);
}
}
}//namespace cura
+14 -10
Ver Arquivo
@@ -1,10 +1,14 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIN_H
#define SKIN_H
#include "sliceDataStorage.h"
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap);
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
#endif//SKIN_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIN_H
#define SKIN_H
#include "sliceDataStorage.h"
namespace cura {
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap);
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
}//namespace cura
#endif//SKIN_H
+56 -47
Ver Arquivo
@@ -1,47 +1,56 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skirt.h"
#include "support.h"
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight)
{
bool externalOnly = (distance > 0);
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2;
Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance));
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
if (externalOnly)
{
Polygons p;
p.add(layer->parts[i].outline[0]);
skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance));
}
else
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(offsetDistance));
}
}
SupportPolyGenerator supportGenerator(storage.support, initialLayerHeight);
skirtPolygons = skirtPolygons.unionPolygons(supportGenerator.polygons.offset(offsetDistance));
//Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for(unsigned int n=0; n<skirtPolygons.size(); n++)
{
double area = skirtPolygons[n].area();
if (area < 0 && area > -extrusionWidth * extrusionWidth * 100)
skirtPolygons.remove(n--);
}
storage.skirt.add(skirtPolygons);
int lenght = storage.skirt.polygonLength();
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength)
count++;
}
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skirt.h"
#include "support.h"
namespace cura {
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight)
{
bool externalOnly = (distance > 0);
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2;
SupportPolyGenerator supportGenerator(storage.support, initialLayerHeight);
Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance));
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
if (externalOnly)
{
Polygons p;
p.add(layer->parts[i].outline[0]);
skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance));
}
else
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(offsetDistance));
supportGenerator.polygons = supportGenerator.polygons.difference(layer->parts[i].outline);
}
}
//Contract and expand the suppory polygons so small sections are removed and the final polygon is smoothed a bit.
supportGenerator.polygons = supportGenerator.polygons.offset(-extrusionWidth * 3);
supportGenerator.polygons = supportGenerator.polygons.offset(extrusionWidth * 3);
skirtPolygons = skirtPolygons.unionPolygons(supportGenerator.polygons.offset(offsetDistance));
//Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for(unsigned int n=0; n<skirtPolygons.size(); n++)
{
double area = skirtPolygons[n].area();
if (area < 0 && area > -extrusionWidth * extrusionWidth * 100)
skirtPolygons.remove(n--);
}
storage.skirt.add(skirtPolygons);
int lenght = storage.skirt.polygonLength();
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength)
count++;
}
}
}//namespace cura
+13 -9
Ver Arquivo
@@ -1,9 +1,13 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIRT_H
#define SKIRT_H
#include "sliceDataStorage.h"
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight);
#endif//SKIRT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIRT_H
#define SKIRT_H
#include "sliceDataStorage.h"
namespace cura {
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight);
}//namespace cura
#endif//SKIRT_H
+88 -85
Ver Arquivo
@@ -1,85 +1,88 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SLICE_DATA_STORAGE_H
#define SLICE_DATA_STORAGE_H
#include "utils/intpoint.h"
#include "utils/polygon.h"
/*
SliceData
+ Layers[]
+ LayerParts[]
+ OutlinePolygons[]
+ Insets[]
+ Polygons[]
+ SkinPolygons[]
*/
class SliceLayerPart
{
public:
AABB boundaryBox;
Polygons outline;
Polygons combBoundery;
vector<Polygons> insets;
Polygons skinOutline;
Polygons sparseOutline;
int bridgeAngle;
};
class SliceLayer
{
public:
int sliceZ;
int printZ;
vector<SliceLayerPart> parts;
};
/******************/
class SupportPoint
{
public:
int32_t z;
double cosAngle;
SupportPoint(int32_t z, double cosAngle) : z(z), cosAngle(cosAngle) {}
};
class SupportStorage
{
public:
bool generated;
int angle;
bool everywhere;
int XYDistance;
int ZDistance;
Point gridOffset;
int32_t gridScale;
int32_t gridWidth, gridHeight;
vector<SupportPoint>* grid;
SupportStorage(){grid = NULL;}
~SupportStorage(){if(grid) delete [] grid;}
};
/******************/
class SliceVolumeStorage
{
public:
vector<SliceLayer> layers;
};
class SliceDataStorage
{
public:
Point3 modelSize, modelMin, modelMax;
Polygons skirt;
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
vector<Polygons> oozeShield; //oozeShield per layer
vector<SliceVolumeStorage> volumes;
SupportStorage support;
Polygons wipeTower;
Point wipePoint;
};
#endif//SLICE_DATA_STORAGE_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SLICE_DATA_STORAGE_H
#define SLICE_DATA_STORAGE_H
#include "utils/intpoint.h"
#include "utils/polygon.h"
/*
SliceData
+ Layers[]
+ LayerParts[]
+ OutlinePolygons[]
+ Insets[]
+ Polygons[]
+ SkinPolygons[]
*/
namespace cura {
class SliceLayerPart
{
public:
AABB boundaryBox;
Polygons outline;
Polygons combBoundery;
vector<Polygons> insets;
Polygons skinOutline;
Polygons sparseOutline;
};
class SliceLayer
{
public:
int sliceZ;
int printZ;
vector<SliceLayerPart> parts;
Polygons openLines;
};
/******************/
class SupportPoint
{
public:
int32_t z;
double cosAngle;
SupportPoint(int32_t z, double cosAngle) : z(z), cosAngle(cosAngle) {}
};
class SupportStorage
{
public:
bool generated;
int angle;
bool everywhere;
int XYDistance;
int ZDistance;
Point gridOffset;
int32_t gridScale;
int32_t gridWidth, gridHeight;
vector<SupportPoint>* grid;
SupportStorage(){grid = nullptr;}
~SupportStorage(){if(grid) delete [] grid;}
};
/******************/
class SliceVolumeStorage
{
public:
vector<SliceLayer> layers;
};
class SliceDataStorage
{
public:
Point3 modelSize, modelMin, modelMax;
Polygons skirt;
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
vector<Polygons> oozeShield; //oozeShield per layer
vector<SliceVolumeStorage> volumes;
SupportStorage support;
Polygons wipeTower;
Point wipePoint;
};
}//namespace cura
#endif//SLICE_DATA_STORAGE_H
+414 -419
Ver Arquivo
@@ -1,419 +1,414 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "slicer.h"
#include "polygonOptimizer.h"
void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching)
{
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
{
if (segmentList[startSegment].addedToPolygon)
continue;
Polygon poly;
poly.add(segmentList[startSegment].start);
unsigned int segmentIndex = startSegment;
bool canClose;
while(true)
{
canClose = false;
segmentList[segmentIndex].addedToPolygon = true;
Point p0 = segmentList[segmentIndex].end;
poly.add(p0);
int nextIndex = -1;
OptimizedFace* face = &ov->faces[segmentList[segmentIndex].faceIndex];
for(unsigned int i=0;i<3;i++)
{
if (face->touching[i] > -1 && faceToSegmentIndex.find(face->touching[i]) != faceToSegmentIndex.end())
{
Point p1 = segmentList[faceToSegmentIndex[face->touching[i]]].start;
Point diff = p0 - p1;
if (shorterThen(diff, MM2INT(0.01)))
{
if (faceToSegmentIndex[face->touching[i]] == (int)startSegment)
canClose = true;
if (segmentList[faceToSegmentIndex[face->touching[i]]].addedToPolygon)
continue;
nextIndex = faceToSegmentIndex[face->touching[i]];
}
}
}
if (nextIndex == -1)
break;
segmentIndex = nextIndex;
}
if (canClose)
polygonList.add(poly);
else
openPolygonList.add(poly);
}
//Clear the segmentList to save memory, it is no longer needed after this point.
segmentList.clear();
//Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
//First link up polygon ends that are within 2 microns.
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
{
if (openPolygonList[j].size() < 1) continue;
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < MM2INT(0.02) * MM2INT(0.02))
{
if (i == j)
{
polygonList.add(openPolygonList[i]);
openPolygonList[i].clear();
break;
}else{
for(unsigned int n=0; n<openPolygonList[j].size(); n++)
openPolygonList[i].add(openPolygonList[j][n]);
openPolygonList[j].clear();
}
}
}
}
//Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
while(1)
{
int64_t bestScore = MM2INT(10.0) * MM2INT(10.0);
unsigned int bestA = -1;
unsigned int bestB = -1;
bool reversed = false;
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
{
if (openPolygonList[j].size() < 1) continue;
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
{
bestScore = distSquared;
bestA = i;
bestB = j;
reversed = false;
}
if (i != j)
{
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
{
bestScore = distSquared;
bestA = i;
bestB = j;
reversed = true;
}
}
}
}
if (bestScore >= MM2INT(10.0) * MM2INT(10.0))
break;
if (bestA == bestB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}else{
if (reversed)
{
if (openPolygonList[bestA].polygonLength() > openPolygonList[bestB].polygonLength())
{
for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
}else{
for(unsigned int n=openPolygonList[bestA].size()-1; int(n)>=0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
}else{
for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
}
}
}
if (extensiveStitching)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
// Then find the sortest path over this polygon that can be used to connect the open polygons,
// And generate a path over this shortest bit to link up the 2 open polygons.
// (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
while(1)
{
unsigned int bestA = -1;
unsigned int bestB = -1;
gapCloserResult bestResult;
bestResult.len = LLONG_MAX;
bestResult.polygonIdx = -1;
bestResult.pointIdxA = -1;
bestResult.pointIdxB = -1;
for(unsigned int i=0; i<openPolygonList.size(); i++)
{
if (openPolygonList[i].size() < 1) continue;
{
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = i;
bestResult = res;
}
}
for(unsigned int j=0; j<openPolygonList.size(); j++)
{
if (openPolygonList[j].size() < 1 || i == j) continue;
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = j;
bestResult = res;
}
}
}
if (bestResult.len < LLONG_MAX)
{
if (bestA == bestB)
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
PolygonRef poly = polygonList.newPoly();
for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][j]);
for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
poly.add(openPolygonList[bestA][j]);
openPolygonList[bestA].clear();
}
else
{
unsigned int n = polygonList.size();
polygonList.add(openPolygonList[bestA]);
for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].add(polygonList[bestResult.polygonIdx][j]);
openPolygonList[bestA].clear();
}
}
else
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
Polygon poly;
for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
openPolygonList[bestB].add(poly[n]);
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else
{
for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
openPolygonList[bestB].add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
}
}
else
{
break;
}
}
}
/*
int q=0;
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() < 2) continue;
if (!q) log("***\n");
log("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y));
log("E: %f %f\n", float(openPolygonList[i][openPolygonList[i].size()-1].X), float(openPolygonList[i][openPolygonList[i].size()-1].Y));
q = 1;
}
*/
//if (q) exit(1);
if (keepNoneClosed)
{
for(unsigned int n=0; n<openPolygonList.size(); n++)
{
if (openPolygonList[n].size() > 0)
polygonList.add(openPolygonList[n]);
}
}
//Clear the openPolygonList to save memory, the only reason to keep it after this is for debugging.
//openPolygonList.clear();
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
int snapDistance = MM2INT(1.0);
for(unsigned int i=0;i<polygonList.size();i++)
{
int length = 0;
for(unsigned int n=1; n<polygonList[i].size(); n++)
{
length += vSize(polygonList[i][n] - polygonList[i][n-1]);
if (length > snapDistance)
break;
}
if (length < snapDistance)
{
polygonList.remove(i);
i--;
}
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
optimizePolygons(polygonList);
}
Slicer::Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching)
{
modelSize = ov->model->modelSize;
modelMin = ov->model->vMin;
int layerCount = (modelSize.z - initial) / thickness + 1;
log("Layer count: %i\n", layerCount);
layers.resize(layerCount);
for(int32_t layerNr = 0; layerNr < layerCount; layerNr++)
{
layers[layerNr].z = initial + thickness * layerNr;
}
for(unsigned int i=0; i<ov->faces.size(); i++)
{
Point3 p0 = ov->points[ov->faces[i].index[0]].p;
Point3 p1 = ov->points[ov->faces[i].index[1]].p;
Point3 p2 = ov->points[ov->faces[i].index[2]].p;
int32_t minZ = p0.z;
int32_t maxZ = p0.z;
if (p1.z < minZ) minZ = p1.z;
if (p2.z < minZ) minZ = p2.z;
if (p1.z > maxZ) maxZ = p1.z;
if (p2.z > maxZ) maxZ = p2.z;
for(int32_t layerNr = (minZ - initial) / thickness; layerNr <= (maxZ - initial) / thickness; layerNr++)
{
int32_t z = layerNr * thickness + initial;
if (z < minZ) continue;
if (layerNr < 0) continue;
SlicerSegment s;
if (p0.z < z && p1.z >= z && p2.z >= z)
s = project2D(p0, p2, p1, z);
else if (p0.z > z && p1.z < z && p2.z < z)
s = project2D(p0, p1, p2, z);
else if (p1.z < z && p0.z >= z && p2.z >= z)
s = project2D(p1, p0, p2, z);
else if (p1.z > z && p0.z < z && p2.z < z)
s = project2D(p1, p2, p0, z);
else if (p2.z < z && p1.z >= z && p0.z >= z)
s = project2D(p2, p1, p0, z);
else if (p2.z > z && p1.z < z && p0.z < z)
s = project2D(p2, p0, p1, z);
else
{
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
// on the slice would create two segments
continue;
}
layers[layerNr].faceToSegmentIndex[i] = layers[layerNr].segmentList.size();
s.faceIndex = i;
s.addedToPolygon = false;
layers[layerNr].segmentList.push_back(s);
}
}
for(unsigned int layerNr=0; layerNr<layers.size(); layerNr++)
{
layers[layerNr].makePolygons(ov, keepNoneClosed, extensiveStitching);
}
}
void Slicer::dumpSegmentsToHTML(const char* filename)
{
float scale = std::max(modelSize.x, modelSize.y) / 1500;
FILE* f = fopen(filename, "w");
fprintf(f, "<!DOCTYPE html><html><body>\n");
for(unsigned int i=0; i<layers.size(); i++)
{
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(modelSize.x / scale), int(modelSize.y / scale));
fprintf(f, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
fprintf(f, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
fprintf(f, "</marker>");
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
fprintf(f, "<path marker-mid='url(#MidMarker)' d=\"");
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
{
PolygonRef p = layers[i].polygonList[j];
for(unsigned int n=0; n<p.size(); n++)
{
if (n == 0)
fprintf(f, "M");
else
fprintf(f, "L");
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
}
fprintf(f, "Z\n");
}
fprintf(f, "\"/>");
fprintf(f, "</g>\n");
for(unsigned int j=0; j<layers[i].openPolygonList.size(); j++)
{
PolygonRef p = layers[i].openPolygonList[j];
if (p.size() < 1) continue;
fprintf(f, "<polyline marker-mid='url(#MidMarker)' points=\"");
for(unsigned int n=0; n<p.size(); n++)
{
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
}
fprintf(f, "\" style=\"fill: none; stroke:red;stroke-width:1\" />\n");
}
fprintf(f, "</svg>\n");
}
fprintf(f, "</body></html>");
fclose(f);
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "slicer.h"
#include "polygonOptimizer.h"
namespace cura {
void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching)
{
Polygons openPolygonList;
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
{
if (segmentList[startSegment].addedToPolygon)
continue;
Polygon poly;
poly.add(segmentList[startSegment].start);
unsigned int segmentIndex = startSegment;
bool canClose;
while(true)
{
canClose = false;
segmentList[segmentIndex].addedToPolygon = true;
Point p0 = segmentList[segmentIndex].end;
poly.add(p0);
int nextIndex = -1;
OptimizedFace* face = &ov->faces[segmentList[segmentIndex].faceIndex];
for(unsigned int i=0;i<3;i++)
{
if (face->touching[i] > -1 && faceToSegmentIndex.find(face->touching[i]) != faceToSegmentIndex.end())
{
Point p1 = segmentList[faceToSegmentIndex[face->touching[i]]].start;
Point diff = p0 - p1;
if (shorterThen(diff, MM2INT(0.01)))
{
if (faceToSegmentIndex[face->touching[i]] == static_cast<int>(startSegment))
canClose = true;
if (segmentList[faceToSegmentIndex[face->touching[i]]].addedToPolygon)
continue;
nextIndex = faceToSegmentIndex[face->touching[i]];
}
}
}
if (nextIndex == -1)
break;
segmentIndex = nextIndex;
}
if (canClose)
polygonList.add(poly);
else
openPolygonList.add(poly);
}
//Clear the segmentList to save memory, it is no longer needed after this point.
segmentList.clear();
//Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
//First link up polygon ends that are within 2 microns.
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
{
if (openPolygonList[j].size() < 1) continue;
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < MM2INT(0.02) * MM2INT(0.02))
{
if (i == j)
{
polygonList.add(openPolygonList[i]);
openPolygonList[i].clear();
break;
}else{
for(unsigned int n=0; n<openPolygonList[j].size(); n++)
openPolygonList[i].add(openPolygonList[j][n]);
openPolygonList[j].clear();
}
}
}
}
//Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
while(1)
{
int64_t bestScore = MM2INT(10.0) * MM2INT(10.0);
unsigned int bestA = -1;
unsigned int bestB = -1;
bool reversed = false;
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
{
if (openPolygonList[j].size() < 1) continue;
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
{
bestScore = distSquared;
bestA = i;
bestB = j;
reversed = false;
}
if (i != j)
{
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
{
bestScore = distSquared;
bestA = i;
bestB = j;
reversed = true;
}
}
}
}
if (bestScore >= MM2INT(10.0) * MM2INT(10.0))
break;
if (bestA == bestB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}else{
if (reversed)
{
if (openPolygonList[bestA].polygonLength() > openPolygonList[bestB].polygonLength())
{
for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
}else{
for(unsigned int n=openPolygonList[bestA].size()-1; int(n)>=0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
}else{
for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
}
}
}
if (extensiveStitching)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
// Then find the sortest path over this polygon that can be used to connect the open polygons,
// And generate a path over this shortest bit to link up the 2 open polygons.
// (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
while(1)
{
unsigned int bestA = -1;
unsigned int bestB = -1;
gapCloserResult bestResult;
bestResult.len = POINT_MAX;
bestResult.polygonIdx = -1;
bestResult.pointIdxA = -1;
bestResult.pointIdxB = -1;
for(unsigned int i=0; i<openPolygonList.size(); i++)
{
if (openPolygonList[i].size() < 1) continue;
{
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = i;
bestResult = res;
}
}
for(unsigned int j=0; j<openPolygonList.size(); j++)
{
if (openPolygonList[j].size() < 1 || i == j) continue;
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = j;
bestResult = res;
}
}
}
if (bestResult.len < POINT_MAX)
{
if (bestA == bestB)
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
PolygonRef poly = polygonList.newPoly();
for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][j]);
for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
poly.add(openPolygonList[bestA][j]);
openPolygonList[bestA].clear();
}
else
{
unsigned int n = polygonList.size();
polygonList.add(openPolygonList[bestA]);
for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].add(polygonList[bestResult.polygonIdx][j]);
openPolygonList[bestA].clear();
}
}
else
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
Polygon poly;
for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
openPolygonList[bestB].add(poly[n]);
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else
{
for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
openPolygonList[bestB].add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
}
}
else
{
break;
}
}
}
if (keepNoneClosed)
{
for(unsigned int n=0; n<openPolygonList.size(); n++)
{
if (openPolygonList[n].size() > 0)
polygonList.add(openPolygonList[n]);
}
}
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() > 0)
openPolygons.newPoly() = openPolygonList[i];
}
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
int snapDistance = MM2INT(1.0);
for(unsigned int i=0;i<polygonList.size();i++)
{
int length = 0;
for(unsigned int n=1; n<polygonList[i].size(); n++)
{
length += vSize(polygonList[i][n] - polygonList[i][n-1]);
if (length > snapDistance)
break;
}
if (length < snapDistance)
{
polygonList.remove(i);
i--;
}
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
optimizePolygons(polygonList);
}
Slicer::Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching)
{
modelSize = ov->model->modelSize;
modelMin = ov->model->vMin;
int layerCount = (modelSize.z - initial) / thickness + 1;
cura::log("Layer count: %i\n", layerCount);
layers.resize(layerCount);
for(int32_t layerNr = 0; layerNr < layerCount; layerNr++)
{
layers[layerNr].z = initial + thickness * layerNr;
}
for(unsigned int i=0; i<ov->faces.size(); i++)
{
Point3 p0 = ov->points[ov->faces[i].index[0]].p;
Point3 p1 = ov->points[ov->faces[i].index[1]].p;
Point3 p2 = ov->points[ov->faces[i].index[2]].p;
int32_t minZ = p0.z;
int32_t maxZ = p0.z;
if (p1.z < minZ) minZ = p1.z;
if (p2.z < minZ) minZ = p2.z;
if (p1.z > maxZ) maxZ = p1.z;
if (p2.z > maxZ) maxZ = p2.z;
for(int32_t layerNr = (minZ - initial) / thickness; layerNr <= (maxZ - initial) / thickness; layerNr++)
{
int32_t z = layerNr * thickness + initial;
if (z < minZ) continue;
if (layerNr < 0) continue;
SlicerSegment s;
if (p0.z < z && p1.z >= z && p2.z >= z)
s = project2D(p0, p2, p1, z);
else if (p0.z > z && p1.z < z && p2.z < z)
s = project2D(p0, p1, p2, z);
else if (p1.z < z && p0.z >= z && p2.z >= z)
s = project2D(p1, p0, p2, z);
else if (p1.z > z && p0.z < z && p2.z < z)
s = project2D(p1, p2, p0, z);
else if (p2.z < z && p1.z >= z && p0.z >= z)
s = project2D(p2, p1, p0, z);
else if (p2.z > z && p1.z < z && p0.z < z)
s = project2D(p2, p0, p1, z);
else
{
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
// on the slice would create two segments
continue;
}
layers[layerNr].faceToSegmentIndex[i] = layers[layerNr].segmentList.size();
s.faceIndex = i;
s.addedToPolygon = false;
layers[layerNr].segmentList.push_back(s);
}
}
for(unsigned int layerNr=0; layerNr<layers.size(); layerNr++)
{
layers[layerNr].makePolygons(ov, keepNoneClosed, extensiveStitching);
}
}
void Slicer::dumpSegmentsToHTML(const char* filename)
{
float scale = std::max(modelSize.x, modelSize.y) / 1500;
FILE* f = fopen(filename, "w");
fprintf(f, "<!DOCTYPE html><html><body>\n");
for(unsigned int i=0; i<layers.size(); i++)
{
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(modelSize.x / scale), int(modelSize.y / scale));
fprintf(f, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
fprintf(f, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
fprintf(f, "</marker>");
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
fprintf(f, "<path marker-mid='url(#MidMarker)' d=\"");
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
{
PolygonRef p = layers[i].polygonList[j];
for(unsigned int n=0; n<p.size(); n++)
{
if (n == 0)
fprintf(f, "M");
else
fprintf(f, "L");
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
}
fprintf(f, "Z\n");
}
fprintf(f, "\"/>");
fprintf(f, "</g>\n");
for(unsigned int j=0; j<layers[i].openPolygons.size(); j++)
{
PolygonRef p = layers[i].openPolygons[j];
fprintf(f, "<polyline marker-mid='url(#MidMarker)' points=\"");
for(unsigned int n=0; n<p.size(); n++)
{
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
}
fprintf(f, "\" style=\"fill: none; stroke:red;stroke-width:1\" />\n");
}
fprintf(f, "</svg>\n");
}
fprintf(f, "</body></html>");
fclose(f);
}
}//namespace cura
+164 -161
Ver Arquivo
@@ -1,161 +1,164 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SLICER_H
#define SLICER_H
#include "optimizedModel.h"
#include "utils/polygon.h"
/*
The Slicer creates layers of polygons from an optimized 3D model.
The result of the Slicer is a list of polygons without any order or structure.
*/
class SlicerSegment
{
public:
Point start, end;
int faceIndex;
bool addedToPolygon;
};
class closePolygonResult
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
//The line on which the point lays is between pointIdx-1 and pointIdx
public:
Point intersectionPoint;
int polygonIdx;
unsigned int pointIdx;
};
class gapCloserResult
{
public:
int64_t len;
int polygonIdx;
unsigned int pointIdxA;
unsigned int pointIdxB;
bool AtoB;
};
class SlicerLayer
{
public:
std::vector<SlicerSegment> segmentList;
std::map<int, int> faceToSegmentIndex;
int z;
Polygons polygonList;
Polygons openPolygonList;
void makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching);
private:
gapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
{
gapCloserResult ret;
closePolygonResult c1 = findPolygonPointClosestTo(ip0);
closePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
return ret;
}
ret.polygonIdx = c1.polygonIdx;
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
ret.len = vSize(ip0 - ip1);
}else{
//Find out if we have should go from A to B or the other way around.
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
int64_t lenA = vSize(p0 - ip0);
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenA += vSize(p0 - p1);
p0 = p1;
}
lenA += vSize(p0 - ip1);
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
int64_t lenB = vSize(p0 - ip1);
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenB += vSize(p0 - p1);
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
ret.len = lenA;
}else{
ret.AtoB = false;
ret.len = lenB;
}
}
return ret;
}
closePolygonResult findPolygonPointClosestTo(Point input)
{
closePolygonResult ret;
for(unsigned int n=0; n<polygonList.size(); n++)
{
Point p0 = polygonList[n][polygonList[n].size()-1];
for(unsigned int i=0; i<polygonList[n].size(); i++)
{
Point p1 = polygonList[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
if (lineLength > 1)
{
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
if (distOnLine >= 0 && distOnLine <= lineLength)
{
Point q = p0 + pDiff * distOnLine / lineLength;
if (shorterThen(q - input, 100))
{
ret.intersectionPoint = q;
ret.polygonIdx = n;
ret.pointIdx = i;
return ret;
}
}
}
p0 = p1;
}
}
ret.polygonIdx = -1;
return ret;
}
};
class Slicer
{
public:
std::vector<SlicerLayer> layers;
Point3 modelSize, modelMin;
Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching);
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
{
SlicerSegment seg;
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
return seg;
}
void dumpSegmentsToHTML(const char* filename);
};
#endif//SLICER_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SLICER_H
#define SLICER_H
#include "optimizedModel.h"
#include "utils/polygon.h"
/*
The Slicer creates layers of polygons from an optimized 3D model.
The result of the Slicer is a list of polygons without any order or structure.
*/
namespace cura {
class SlicerSegment
{
public:
Point start, end;
int faceIndex;
bool addedToPolygon;
};
class closePolygonResult
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
//The line on which the point lays is between pointIdx-1 and pointIdx
public:
Point intersectionPoint;
int polygonIdx;
unsigned int pointIdx;
};
class gapCloserResult
{
public:
int64_t len;
int polygonIdx;
unsigned int pointIdxA;
unsigned int pointIdxB;
bool AtoB;
};
class SlicerLayer
{
public:
std::vector<SlicerSegment> segmentList;
std::map<int, int> faceToSegmentIndex;
int z;
Polygons polygonList;
Polygons openPolygons;
void makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching);
private:
gapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
{
gapCloserResult ret;
closePolygonResult c1 = findPolygonPointClosestTo(ip0);
closePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
return ret;
}
ret.polygonIdx = c1.polygonIdx;
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
ret.len = vSize(ip0 - ip1);
}else{
//Find out if we have should go from A to B or the other way around.
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
int64_t lenA = vSize(p0 - ip0);
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenA += vSize(p0 - p1);
p0 = p1;
}
lenA += vSize(p0 - ip1);
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
int64_t lenB = vSize(p0 - ip1);
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenB += vSize(p0 - p1);
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
ret.len = lenA;
}else{
ret.AtoB = false;
ret.len = lenB;
}
}
return ret;
}
closePolygonResult findPolygonPointClosestTo(Point input)
{
closePolygonResult ret;
for(unsigned int n=0; n<polygonList.size(); n++)
{
Point p0 = polygonList[n][polygonList[n].size()-1];
for(unsigned int i=0; i<polygonList[n].size(); i++)
{
Point p1 = polygonList[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
if (lineLength > 1)
{
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
if (distOnLine >= 0 && distOnLine <= lineLength)
{
Point q = p0 + pDiff * distOnLine / lineLength;
if (shorterThen(q - input, 100))
{
ret.intersectionPoint = q;
ret.polygonIdx = n;
ret.pointIdx = i;
return ret;
}
}
}
p0 = p1;
}
}
ret.polygonIdx = -1;
return ret;
}
};
class Slicer
{
public:
std::vector<SlicerLayer> layers;
Point3 modelSize, modelMin;
Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching);
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
{
SlicerSegment seg;
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
return seg;
}
void dumpSegmentsToHTML(const char* filename);
};
}//namespace cura
#endif//SLICER_H
+190 -187
Ver Arquivo
@@ -1,187 +1,190 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "support.h"
template<typename T> inline void swap(T& p0, T& p1)
{
T tmp = p0;
p0 = p1;
p1 = tmp;
}
int cmp_SupportPoint(const void* a, const void* b)
{
return ((SupportPoint*)a)->z - ((SupportPoint*)b)->z;
}
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance)
{
storage.generated = false;
if (supportAngle < 0)
return;
storage.generated = true;
storage.gridOffset.X = om->vMin.x;
storage.gridOffset.Y = om->vMin.y;
storage.gridScale = 200;
storage.gridWidth = (om->modelSize.x / storage.gridScale) + 1;
storage.gridHeight = (om->modelSize.y / storage.gridScale) + 1;
storage.grid = new vector<SupportPoint>[storage.gridWidth * storage.gridHeight];
storage.angle = supportAngle;
storage.everywhere = supportEverywhere;
storage.XYDistance = supportXYDistance;
storage.ZDistance = supportZDistance;
for(unsigned int volumeIdx = 0; volumeIdx < om->volumes.size(); volumeIdx++)
{
OptimizedVolume* vol = &om->volumes[volumeIdx];
for(unsigned int faceIdx = 0; faceIdx < vol->faces.size(); faceIdx++)
{
OptimizedFace* face = &vol->faces[faceIdx];
Point3 v0 = vol->points[face->index[0]].p;
Point3 v1 = vol->points[face->index[1]].p;
Point3 v2 = vol->points[face->index[2]].p;
Point3 normal = (v1 - v0).cross(v2 - v0);
int32_t normalSize = normal.vSize();
double cosAngle = fabs(double(normal.z) / double(normalSize));
v0.x = (v0.x - storage.gridOffset.X) / storage.gridScale;
v0.y = (v0.y - storage.gridOffset.Y) / storage.gridScale;
v1.x = (v1.x - storage.gridOffset.X) / storage.gridScale;
v1.y = (v1.y - storage.gridOffset.Y) / storage.gridScale;
v2.x = (v2.x - storage.gridOffset.X) / storage.gridScale;
v2.y = (v2.y - storage.gridOffset.Y) / storage.gridScale;
if (v0.x > v1.x) swap(v0, v1);
if (v1.x > v2.x) swap(v1, v2);
if (v0.x > v1.x) swap(v0, v1);
for(int64_t x=v0.x; x<v1.x; x++)
{
int64_t y0 = v0.y + (v1.y - v0.y) * (x - v0.x) / (v1.x - v0.x);
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
int64_t z0 = v0.z + (v1.z - v0.z) * (x - v0.x) / (v1.x - v0.x);
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
for(int64_t y=y0; y<y1; y++)
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
}
for(int64_t x=v1.x; x<v2.x; x++)
{
int64_t y0 = v1.y + (v2.y - v1.y) * (x - v1.x) / (v2.x - v1.x);
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
int64_t z0 = v1.z + (v2.z - v1.z) * (x - v1.x) / (v2.x - v1.x);
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
for(int64_t y=y0; y<y1; y++)
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
}
}
}
for(int32_t x=0; x<storage.gridWidth; x++)
{
for(int32_t y=0; y<storage.gridHeight; y++)
{
unsigned int n = x+y*storage.gridWidth;
qsort(storage.grid[n].data(), storage.grid[n].size(), sizeof(SupportPoint), cmp_SupportPoint);
}
}
storage.gridOffset.X += storage.gridScale / 2;
storage.gridOffset.Y += storage.gridScale / 2;
}
bool SupportPolyGenerator::needSupportAt(Point p)
{
if (p.X < 1) return false;
if (p.Y < 1) return false;
if (p.X >= storage.gridWidth - 1) return false;
if (p.Y >= storage.gridHeight - 1) return false;
if (done[p.X + p.Y * storage.gridWidth]) return false;
unsigned int n = p.X+p.Y*storage.gridWidth;
if (everywhere)
{
bool ok = false;
for(unsigned int i=0; i<storage.grid[n].size(); i+=2)
{
if (storage.grid[n][i].cosAngle >= cosAngle && storage.grid[n][i].z - supportZDistance >= z && (i == 0 || storage.grid[n][i-1].z + supportZDistance < z))
{
ok = true;
break;
}
}
if (!ok) return false;
}else{
if (storage.grid[n].size() < 1) return false;
if (storage.grid[n][0].cosAngle < cosAngle) return false;
if (storage.grid[n][0].z - supportZDistance < z) return false;
}
return true;
}
void SupportPolyGenerator::lazyFill(Point startPoint)
{
static int nr = 0;
nr++;
PolygonRef poly = polygons.newPoly();
Polygon tmpPoly;
while(1)
{
Point p = startPoint;
done[p.X + p.Y * storage.gridWidth] = nr;
while(needSupportAt(p + Point(1, 0)))
{
p.X ++;
done[p.X + p.Y * storage.gridWidth] = nr;
}
tmpPoly.add(startPoint * storage.gridScale + storage.gridOffset - Point(storage.gridScale/2, 0));
poly.add(p * storage.gridScale + storage.gridOffset);
startPoint.Y++;
while(!needSupportAt(startPoint) && startPoint.X <= p.X)
startPoint.X ++;
if (startPoint.X > p.X)
{
for(unsigned int n=0;n<tmpPoly.size();n++)
{
poly.add(tmpPoly[tmpPoly.size()-n-1]);
}
polygons.add(poly);
return;
}
while(needSupportAt(startPoint - Point(1, 0)) && startPoint.X > 1)
startPoint.X --;
}
}
SupportPolyGenerator::SupportPolyGenerator(SupportStorage& storage, int32_t z)
: storage(storage), z(z), everywhere(storage.everywhere)
{
if (!storage.generated)
return;
cosAngle = cos(double(90 - storage.angle) / 180.0 * M_PI) - 0.01;
this->supportZDistance = storage.ZDistance;
done = new int[storage.gridWidth*storage.gridHeight];
memset(done, 0, sizeof(int) * storage.gridWidth*storage.gridHeight);
for(int32_t y=1; y<storage.gridHeight; y++)
{
for(int32_t x=1; x<storage.gridWidth; x++)
{
if (!needSupportAt(Point(x, y)) || done[x + y * storage.gridWidth]) continue;
lazyFill(Point(x, y));
}
}
delete[] done;
polygons = polygons.offset(storage.XYDistance);
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "support.h"
namespace cura {
template<typename T> inline void swap(T& p0, T& p1)
{
T tmp = p0;
p0 = p1;
p1 = tmp;
}
int cmp_SupportPoint(const void* a, const void* b)
{
return ((SupportPoint*)a)->z - ((SupportPoint*)b)->z;
}
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance)
{
storage.generated = false;
if (supportAngle < 0)
return;
storage.generated = true;
storage.gridOffset.X = om->vMin.x;
storage.gridOffset.Y = om->vMin.y;
storage.gridScale = 200;
storage.gridWidth = (om->modelSize.x / storage.gridScale) + 1;
storage.gridHeight = (om->modelSize.y / storage.gridScale) + 1;
storage.grid = new vector<SupportPoint>[storage.gridWidth * storage.gridHeight];
storage.angle = supportAngle;
storage.everywhere = supportEverywhere;
storage.XYDistance = supportXYDistance;
storage.ZDistance = supportZDistance;
for(unsigned int volumeIdx = 0; volumeIdx < om->volumes.size(); volumeIdx++)
{
OptimizedVolume* vol = &om->volumes[volumeIdx];
for(unsigned int faceIdx = 0; faceIdx < vol->faces.size(); faceIdx++)
{
OptimizedFace* face = &vol->faces[faceIdx];
Point3 v0 = vol->points[face->index[0]].p;
Point3 v1 = vol->points[face->index[1]].p;
Point3 v2 = vol->points[face->index[2]].p;
Point3 normal = (v1 - v0).cross(v2 - v0);
int32_t normalSize = normal.vSize();
double cosAngle = fabs(double(normal.z) / double(normalSize));
v0.x = (v0.x - storage.gridOffset.X) / storage.gridScale;
v0.y = (v0.y - storage.gridOffset.Y) / storage.gridScale;
v1.x = (v1.x - storage.gridOffset.X) / storage.gridScale;
v1.y = (v1.y - storage.gridOffset.Y) / storage.gridScale;
v2.x = (v2.x - storage.gridOffset.X) / storage.gridScale;
v2.y = (v2.y - storage.gridOffset.Y) / storage.gridScale;
if (v0.x > v1.x) swap(v0, v1);
if (v1.x > v2.x) swap(v1, v2);
if (v0.x > v1.x) swap(v0, v1);
for(int64_t x=v0.x; x<v1.x; x++)
{
int64_t y0 = v0.y + (v1.y - v0.y) * (x - v0.x) / (v1.x - v0.x);
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
int64_t z0 = v0.z + (v1.z - v0.z) * (x - v0.x) / (v1.x - v0.x);
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
for(int64_t y=y0; y<y1; y++)
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
}
for(int64_t x=v1.x; x<v2.x; x++)
{
int64_t y0 = v1.y + (v2.y - v1.y) * (x - v1.x) / (v2.x - v1.x);
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
int64_t z0 = v1.z + (v2.z - v1.z) * (x - v1.x) / (v2.x - v1.x);
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
for(int64_t y=y0; y<y1; y++)
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
}
}
}
for(int32_t x=0; x<storage.gridWidth; x++)
{
for(int32_t y=0; y<storage.gridHeight; y++)
{
unsigned int n = x+y*storage.gridWidth;
qsort(storage.grid[n].data(), storage.grid[n].size(), sizeof(SupportPoint), cmp_SupportPoint);
}
}
storage.gridOffset.X += storage.gridScale / 2;
storage.gridOffset.Y += storage.gridScale / 2;
}
bool SupportPolyGenerator::needSupportAt(Point p)
{
if (p.X < 1) return false;
if (p.Y < 1) return false;
if (p.X >= storage.gridWidth - 1) return false;
if (p.Y >= storage.gridHeight - 1) return false;
if (done[p.X + p.Y * storage.gridWidth]) return false;
unsigned int n = p.X+p.Y*storage.gridWidth;
if (everywhere)
{
bool ok = false;
for(unsigned int i=0; i<storage.grid[n].size(); i+=2)
{
if (storage.grid[n][i].cosAngle >= cosAngle && storage.grid[n][i].z - supportZDistance >= z && (i == 0 || storage.grid[n][i-1].z + supportZDistance < z))
{
ok = true;
break;
}
}
if (!ok) return false;
}else{
if (storage.grid[n].size() < 1) return false;
if (storage.grid[n][0].cosAngle < cosAngle) return false;
if (storage.grid[n][0].z - supportZDistance < z) return false;
}
return true;
}
void SupportPolyGenerator::lazyFill(Point startPoint)
{
static int nr = 0;
nr++;
PolygonRef poly = polygons.newPoly();
Polygon tmpPoly;
while(1)
{
Point p = startPoint;
done[p.X + p.Y * storage.gridWidth] = nr;
while(needSupportAt(p + Point(1, 0)))
{
p.X ++;
done[p.X + p.Y * storage.gridWidth] = nr;
}
tmpPoly.add(startPoint * storage.gridScale + storage.gridOffset - Point(storage.gridScale/2, 0));
poly.add(p * storage.gridScale + storage.gridOffset);
startPoint.Y++;
while(!needSupportAt(startPoint) && startPoint.X <= p.X)
startPoint.X ++;
if (startPoint.X > p.X)
{
for(unsigned int n=0;n<tmpPoly.size();n++)
{
poly.add(tmpPoly[tmpPoly.size()-n-1]);
}
polygons.add(poly);
return;
}
while(needSupportAt(startPoint - Point(1, 0)) && startPoint.X > 1)
startPoint.X --;
}
}
SupportPolyGenerator::SupportPolyGenerator(SupportStorage& storage, int32_t z)
: storage(storage), z(z), everywhere(storage.everywhere)
{
if (!storage.generated)
return;
cosAngle = cos(double(90 - storage.angle) / 180.0 * M_PI) - 0.01;
this->supportZDistance = storage.ZDistance;
done = new int[storage.gridWidth*storage.gridHeight];
memset(done, 0, sizeof(int) * storage.gridWidth*storage.gridHeight);
for(int32_t y=1; y<storage.gridHeight; y++)
{
for(int32_t x=1; x<storage.gridWidth; x++)
{
if (!needSupportAt(Point(x, y)) || done[x + y * storage.gridWidth]) continue;
lazyFill(Point(x, y));
}
}
delete[] done;
polygons = polygons.offset(storage.XYDistance);
}
}//namespace cura
+34 -30
Ver Arquivo
@@ -1,30 +1,34 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SUPPORT_H
#define SUPPORT_H
#include "sliceDataStorage.h"
#include "optimizedModel.h"
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance);
class SupportPolyGenerator
{
public:
Polygons polygons;
private:
SupportStorage& storage;
double cosAngle;
int32_t z;
int supportZDistance;
bool everywhere;
int* done;
bool needSupportAt(Point p);
void lazyFill(Point startPoint);
public:
SupportPolyGenerator(SupportStorage& storage, int32_t z);
};
#endif//SUPPORT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SUPPORT_H
#define SUPPORT_H
#include "sliceDataStorage.h"
#include "optimizedModel.h"
namespace cura {
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance);
class SupportPolyGenerator
{
public:
Polygons polygons;
private:
SupportStorage& storage;
double cosAngle;
int32_t z;
int supportZDistance;
bool everywhere;
int* done;
bool needSupportAt(Point p);
void lazyFill(Point startPoint);
public:
SupportPolyGenerator(SupportStorage& storage, int32_t z);
};
}//namespace cura
#endif//SUPPORT_H
+307 -305
Ver Arquivo
@@ -1,305 +1,307 @@
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "timeEstimate.h"
#define MINIMUM_PLANNER_SPEED 0.05// (mm/sec)
const double max_feedrate[TimeEstimateCalculator::NUM_AXIS] = {600, 600, 40, 25};
const double minimumfeedrate = 0.01;
const double acceleration = 3000;
const double max_acceleration[TimeEstimateCalculator::NUM_AXIS] = {9000,9000,100,10000};
const double max_xy_jerk = 20.0;
const double max_z_jerk = 0.4;
const double max_e_jerk = 5.0;
template<typename T> const T square(const T& a) { return a * a; }
void TimeEstimateCalculator::setPosition(Position newPos)
{
currentPosition = newPos;
}
void TimeEstimateCalculator::reset()
{
blocks.clear();
}
// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
// acceleration within the allotted distance.
static inline double max_allowable_speed(double acceleration, double target_velocity, double distance)
{
return sqrt(target_velocity*target_velocity-2*acceleration*distance);
}
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration:
static inline float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration)
{
if (acceleration == 0)
return 0.0;
return (square(target_rate)-square(initial_rate)) / (2.0*acceleration);
}
// This function gives you the point at which you must start braking (at the rate of -acceleration) if
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
// a total travel of distance. This can be used to compute the intersection point between acceleration and
// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
static inline double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance)
{
if (acceleration == 0.0)
return 0.0;
return (2.0*acceleration*distance-square(initial_rate)+square(final_rate)) / (4.0*acceleration);
}
// This function gives the time it needs to accelerate from an initial speed to reach a final distance.
static inline double acceleration_time_from_distance(double initial_feedrate, double distance, double acceleration)
{
double discriminant = sqrt(square(initial_feedrate) - 2 * acceleration * -distance);
return (-initial_feedrate + discriminant) / acceleration;
}
// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors.
void TimeEstimateCalculator::calculate_trapezoid_for_block(Block *block, double entry_factor, double exit_factor)
{
double initial_feedrate = block->nominal_feedrate*entry_factor;
double final_feedrate = block->nominal_feedrate*exit_factor;
double acceleration = block->acceleration;
double accelerate_distance = estimate_acceleration_distance(initial_feedrate, block->nominal_feedrate, acceleration);
double decelerate_distance = estimate_acceleration_distance(block->nominal_feedrate, final_feedrate, -acceleration);
// Calculate the size of Plateau of Nominal Rate.
double plateau_distance = block->distance-accelerate_distance - decelerate_distance;
// Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will
// have to use intersection_distance() to calculate when to abort acceleration and start braking
// in order to reach the final_rate exactly at the end of this block.
if (plateau_distance < 0)
{
accelerate_distance = intersection_distance(initial_feedrate, final_feedrate, acceleration, block->distance);
accelerate_distance = std::max(accelerate_distance, 0.0); // Check limits due to numerical round-off
accelerate_distance = std::min(accelerate_distance, block->distance);//(We can cast here to unsigned, because the above line ensures that we are above zero)
plateau_distance = 0;
}
block->accelerate_until = accelerate_distance;
block->decelerate_after = accelerate_distance+plateau_distance;
block->initial_feedrate = initial_feedrate;
block->final_feedrate = final_feedrate;
}
void TimeEstimateCalculator::plan(Position newPos, double feedrate)
{
Block block;
memset(&block, 0, sizeof(block));
block.maxTravel = 0;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
block.delta[n] = newPos[n] - currentPosition[n];
block.absDelta[n] = fabs(block.delta[n]);
block.maxTravel = std::max(block.maxTravel, block.absDelta[n]);
}
if (block.maxTravel <= 0)
return;
if (feedrate < minimumfeedrate)
feedrate = minimumfeedrate;
block.distance = sqrtf(square(block.absDelta[0]) + square(block.absDelta[1]) + square(block.absDelta[2]));
if (block.distance == 0.0)
block.distance = block.absDelta[3];
block.nominal_feedrate = feedrate;
Position current_feedrate;
Position current_abs_feedrate;
double feedrate_factor = 1.0;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
current_feedrate[n] = block.delta[n] * feedrate / block.distance;
current_abs_feedrate[n] = abs(current_feedrate[n]);
if (current_abs_feedrate[n] > max_feedrate[n])
feedrate_factor = std::min(feedrate_factor, max_feedrate[n] / current_abs_feedrate[n]);
}
//TODO: XY_FREQUENCY_LIMIT
if(feedrate_factor < 1.0)
{
for(unsigned int n=0; n<NUM_AXIS; n++)
{
current_feedrate[n] *= feedrate_factor;
current_abs_feedrate[n] *= feedrate_factor;
}
block.nominal_feedrate *= feedrate_factor;
}
block.acceleration = acceleration;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
if (block.acceleration * (block.absDelta[n] / block.distance) > max_acceleration[n])
block.acceleration = max_acceleration[n];
}
double vmax_junction = max_xy_jerk/2;
double vmax_junction_factor = 1.0;
if(current_abs_feedrate[Z_AXIS] > max_z_jerk/2)
vmax_junction = std::min(vmax_junction, max_z_jerk/2);
if(current_abs_feedrate[E_AXIS] > max_e_jerk/2)
vmax_junction = std::min(vmax_junction, max_e_jerk/2);
vmax_junction = std::min(vmax_junction, block.nominal_feedrate);
double safe_speed = vmax_junction;
if ((blocks.size() > 0) && (previous_nominal_feedrate > 0.0001))
{
double xy_jerk = sqrt(square(current_feedrate[X_AXIS]-previous_feedrate[X_AXIS])+square(current_feedrate[Y_AXIS]-previous_feedrate[Y_AXIS]));
vmax_junction = block.nominal_feedrate;
if (xy_jerk > max_xy_jerk) {
vmax_junction_factor = (max_xy_jerk/xy_jerk);
}
if(fabs(current_feedrate[Z_AXIS] - previous_feedrate[Z_AXIS]) > max_z_jerk) {
vmax_junction_factor = std::min(vmax_junction_factor, (max_z_jerk/fabs(current_feedrate[Z_AXIS] - previous_feedrate[Z_AXIS])));
}
if(fabs(current_feedrate[E_AXIS] - previous_feedrate[E_AXIS]) > max_e_jerk) {
vmax_junction_factor = std::min(vmax_junction_factor, (max_e_jerk/fabs(current_feedrate[E_AXIS] - previous_feedrate[E_AXIS])));
}
vmax_junction = std::min(previous_nominal_feedrate, vmax_junction * vmax_junction_factor); // Limit speed to max previous speed
}
block.max_entry_speed = vmax_junction;
double v_allowable = max_allowable_speed(-block.acceleration, MINIMUM_PLANNER_SPEED, block.distance);
block.entry_speed = std::min(vmax_junction, v_allowable);
block.nominal_length_flag = block.nominal_feedrate <= v_allowable;
block.recalculate_flag = true; // Always calculate trapezoid for new block
previous_feedrate = current_feedrate;
previous_nominal_feedrate = block.nominal_feedrate;
currentPosition = newPos;
calculate_trapezoid_for_block(&block, block.entry_speed/block.nominal_feedrate, safe_speed/block.nominal_feedrate);
blocks.push_back(block);
}
double TimeEstimateCalculator::calculate()
{
reverse_pass();
forward_pass();
recalculate_trapezoids();
double totalTime = 0;
for(unsigned int n=0; n<blocks.size(); n++)
{
double plateau_distance = blocks[n].decelerate_after - blocks[n].accelerate_until;
totalTime += acceleration_time_from_distance(blocks[n].initial_feedrate, blocks[n].accelerate_until, blocks[n].acceleration);
totalTime += plateau_distance / blocks[n].nominal_feedrate;
totalTime += acceleration_time_from_distance(blocks[n].final_feedrate, (blocks[n].distance - blocks[n].decelerate_after), blocks[n].acceleration);
}
return totalTime;
}
// The kernel called by accelerationPlanner::calculate() when scanning the plan from last to first entry.
void TimeEstimateCalculator::planner_reverse_pass_kernel(Block *previous, Block *current, Block *next)
{
if(!current || !next)
return;
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
if (current->entry_speed != current->max_entry_speed)
{
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
// for max allowable speed if block is decelerating and nominal length is false.
if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed))
{
current->entry_speed = std::min(current->max_entry_speed, max_allowable_speed(-current->acceleration, next->entry_speed, current->distance));
} else {
current->entry_speed = current->max_entry_speed;
}
current->recalculate_flag = true;
}
}
void TimeEstimateCalculator::reverse_pass()
{
Block* block[3] = {NULL, NULL, NULL};
for(unsigned int n=blocks.size()-1; int(n)>=0; n--)
{
block[2]= block[1];
block[1]= block[0];
block[0] = &blocks[n];
planner_reverse_pass_kernel(block[0], block[1], block[2]);
}
}
// The kernel called by accelerationPlanner::calculate() when scanning the plan from first to last entry.
void TimeEstimateCalculator::planner_forward_pass_kernel(Block *previous, Block *current, Block *next)
{
if(!previous)
return;
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
// If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
if (!previous->nominal_length_flag)
{
if (previous->entry_speed < current->entry_speed)
{
double entry_speed = std::min(current->entry_speed, max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->distance) );
// Check for junction speed change
if (current->entry_speed != entry_speed)
{
current->entry_speed = entry_speed;
current->recalculate_flag = true;
}
}
}
}
void TimeEstimateCalculator::forward_pass()
{
Block* block[3] = {NULL, NULL, NULL};
for(unsigned int n=0; n<blocks.size(); n++)
{
block[0]= block[1];
block[1]= block[2];
block[2] = &blocks[n];
planner_forward_pass_kernel(block[0], block[1], block[2]);
}
planner_forward_pass_kernel(block[1], block[2], NULL);
}
// Recalculates the trapezoid speed profiles for all blocks in the plan according to the
// entry_factor for each junction. Must be called by planner_recalculate() after
// updating the blocks.
void TimeEstimateCalculator::recalculate_trapezoids()
{
Block *current;
Block *next = NULL;
for(unsigned int n=0; n<blocks.size(); n--)
{
current = next;
next = &blocks[n];
if (current)
{
// Recalculate if current block entry or exit junction speed has changed.
if (current->recalculate_flag || next->recalculate_flag)
{
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_feedrate, next->entry_speed/current->nominal_feedrate);
current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
}
}
}
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
if(next != NULL)
{
calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_feedrate, MINIMUM_PLANNER_SPEED/next->nominal_feedrate);
next->recalculate_flag = false;
}
}
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "timeEstimate.h"
#define MINIMUM_PLANNER_SPEED 0.05// (mm/sec)
const double max_feedrate[TimeEstimateCalculator::NUM_AXIS] = {600, 600, 40, 25};
const double minimumfeedrate = 0.01;
const double acceleration = 3000;
const double max_acceleration[TimeEstimateCalculator::NUM_AXIS] = {9000,9000,100,10000};
const double max_xy_jerk = 20.0;
const double max_z_jerk = 0.4;
const double max_e_jerk = 5.0;
template<typename T> const T square(const T& a) { return a * a; }
void TimeEstimateCalculator::setPosition(Position newPos)
{
currentPosition = newPos;
}
void TimeEstimateCalculator::reset()
{
blocks.clear();
}
// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
// acceleration within the allotted distance.
static inline double max_allowable_speed(double acceleration, double target_velocity, double distance)
{
return sqrt(target_velocity*target_velocity-2*acceleration*distance);
}
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration:
static inline float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration)
{
if (acceleration == 0)
return 0.0;
return (square(target_rate)-square(initial_rate)) / (2.0*acceleration);
}
// This function gives you the point at which you must start braking (at the rate of -acceleration) if
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
// a total travel of distance. This can be used to compute the intersection point between acceleration and
// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
static inline double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance)
{
if (acceleration == 0.0)
return 0.0;
return (2.0*acceleration*distance-square(initial_rate)+square(final_rate)) / (4.0*acceleration);
}
// This function gives the time it needs to accelerate from an initial speed to reach a final distance.
static inline double acceleration_time_from_distance(double initial_feedrate, double distance, double acceleration)
{
double discriminant = sqrt(square(initial_feedrate) - 2 * acceleration * -distance);
return (-initial_feedrate + discriminant) / acceleration;
}
// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors.
void TimeEstimateCalculator::calculate_trapezoid_for_block(Block *block, double entry_factor, double exit_factor)
{
double initial_feedrate = block->nominal_feedrate*entry_factor;
double final_feedrate = block->nominal_feedrate*exit_factor;
double acceleration = block->acceleration;
double accelerate_distance = estimate_acceleration_distance(initial_feedrate, block->nominal_feedrate, acceleration);
double decelerate_distance = estimate_acceleration_distance(block->nominal_feedrate, final_feedrate, -acceleration);
// Calculate the size of Plateau of Nominal Rate.
double plateau_distance = block->distance-accelerate_distance - decelerate_distance;
// Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will
// have to use intersection_distance() to calculate when to abort acceleration and start braking
// in order to reach the final_rate exactly at the end of this block.
if (plateau_distance < 0)
{
accelerate_distance = intersection_distance(initial_feedrate, final_feedrate, acceleration, block->distance);
accelerate_distance = std::max(accelerate_distance, 0.0); // Check limits due to numerical round-off
accelerate_distance = std::min(accelerate_distance, block->distance);//(We can cast here to unsigned, because the above line ensures that we are above zero)
plateau_distance = 0;
}
block->accelerate_until = accelerate_distance;
block->decelerate_after = accelerate_distance+plateau_distance;
block->initial_feedrate = initial_feedrate;
block->final_feedrate = final_feedrate;
}
void TimeEstimateCalculator::plan(Position newPos, double feedrate)
{
Block block;
memset(&block, 0, sizeof(block));
block.maxTravel = 0;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
block.delta[n] = newPos[n] - currentPosition[n];
block.absDelta[n] = fabs(block.delta[n]);
block.maxTravel = std::max(block.maxTravel, block.absDelta[n]);
}
if (block.maxTravel <= 0)
return;
if (feedrate < minimumfeedrate)
feedrate = minimumfeedrate;
block.distance = sqrtf(square(block.absDelta[0]) + square(block.absDelta[1]) + square(block.absDelta[2]));
if (block.distance == 0.0)
block.distance = block.absDelta[3];
block.nominal_feedrate = feedrate;
Position current_feedrate;
Position current_abs_feedrate;
double feedrate_factor = 1.0;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
current_feedrate[n] = block.delta[n] * feedrate / block.distance;
current_abs_feedrate[n] = fabs(current_feedrate[n]);
if (current_abs_feedrate[n] > max_feedrate[n])
feedrate_factor = std::min(feedrate_factor, max_feedrate[n] / current_abs_feedrate[n]);
}
//TODO: XY_FREQUENCY_LIMIT
if(feedrate_factor < 1.0)
{
for(unsigned int n=0; n<NUM_AXIS; n++)
{
current_feedrate[n] *= feedrate_factor;
current_abs_feedrate[n] *= feedrate_factor;
}
block.nominal_feedrate *= feedrate_factor;
}
block.acceleration = acceleration;
for(unsigned int n=0; n<NUM_AXIS; n++)
{
if (block.acceleration * (block.absDelta[n] / block.distance) > max_acceleration[n])
block.acceleration = max_acceleration[n];
}
double vmax_junction = max_xy_jerk/2;
double vmax_junction_factor = 1.0;
if(current_abs_feedrate[Z_AXIS] > max_z_jerk/2)
vmax_junction = std::min(vmax_junction, max_z_jerk/2);
if(current_abs_feedrate[E_AXIS] > max_e_jerk/2)
vmax_junction = std::min(vmax_junction, max_e_jerk/2);
vmax_junction = std::min(vmax_junction, block.nominal_feedrate);
double safe_speed = vmax_junction;
if ((blocks.size() > 0) && (previous_nominal_feedrate > 0.0001))
{
double xy_jerk = sqrt(square(current_feedrate[X_AXIS]-previous_feedrate[X_AXIS])+square(current_feedrate[Y_AXIS]-previous_feedrate[Y_AXIS]));
vmax_junction = block.nominal_feedrate;
if (xy_jerk > max_xy_jerk) {
vmax_junction_factor = (max_xy_jerk/xy_jerk);
}
if(fabs(current_feedrate[Z_AXIS] - previous_feedrate[Z_AXIS]) > max_z_jerk) {
vmax_junction_factor = std::min(vmax_junction_factor, (max_z_jerk/fabs(current_feedrate[Z_AXIS] - previous_feedrate[Z_AXIS])));
}
if(fabs(current_feedrate[E_AXIS] - previous_feedrate[E_AXIS]) > max_e_jerk) {
vmax_junction_factor = std::min(vmax_junction_factor, (max_e_jerk/fabs(current_feedrate[E_AXIS] - previous_feedrate[E_AXIS])));
}
vmax_junction = std::min(previous_nominal_feedrate, vmax_junction * vmax_junction_factor); // Limit speed to max previous speed
}
block.max_entry_speed = vmax_junction;
double v_allowable = max_allowable_speed(-block.acceleration, MINIMUM_PLANNER_SPEED, block.distance);
block.entry_speed = std::min(vmax_junction, v_allowable);
block.nominal_length_flag = block.nominal_feedrate <= v_allowable;
block.recalculate_flag = true; // Always calculate trapezoid for new block
previous_feedrate = current_feedrate;
previous_nominal_feedrate = block.nominal_feedrate;
currentPosition = newPos;
calculate_trapezoid_for_block(&block, block.entry_speed/block.nominal_feedrate, safe_speed/block.nominal_feedrate);
blocks.push_back(block);
}
double TimeEstimateCalculator::calculate()
{
reverse_pass();
forward_pass();
recalculate_trapezoids();
double totalTime = 0;
for(unsigned int n=0; n<blocks.size(); n++)
{
double plateau_distance = blocks[n].decelerate_after - blocks[n].accelerate_until;
totalTime += acceleration_time_from_distance(blocks[n].initial_feedrate, blocks[n].accelerate_until, blocks[n].acceleration);
totalTime += plateau_distance / blocks[n].nominal_feedrate;
totalTime += acceleration_time_from_distance(blocks[n].final_feedrate, (blocks[n].distance - blocks[n].decelerate_after), blocks[n].acceleration);
}
return totalTime;
}
// The kernel called by accelerationPlanner::calculate() when scanning the plan from last to first entry.
void TimeEstimateCalculator::planner_reverse_pass_kernel(Block *previous, Block *current, Block *next)
{
(void)previous;
if(!current || !next)
return;
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
if (current->entry_speed != current->max_entry_speed)
{
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
// for max allowable speed if block is decelerating and nominal length is false.
if ((!current->nominal_length_flag) && (current->max_entry_speed > next->entry_speed))
{
current->entry_speed = std::min(current->max_entry_speed, max_allowable_speed(-current->acceleration, next->entry_speed, current->distance));
} else {
current->entry_speed = current->max_entry_speed;
}
current->recalculate_flag = true;
}
}
void TimeEstimateCalculator::reverse_pass()
{
Block* block[3] = {nullptr, nullptr, nullptr};
for(unsigned int n=blocks.size()-1; int(n)>=0; n--)
{
block[2]= block[1];
block[1]= block[0];
block[0] = &blocks[n];
planner_reverse_pass_kernel(block[0], block[1], block[2]);
}
}
// The kernel called by accelerationPlanner::calculate() when scanning the plan from first to last entry.
void TimeEstimateCalculator::planner_forward_pass_kernel(Block *previous, Block *current, Block *next)
{
(void)next;
if(!previous)
return;
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
// If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
if (!previous->nominal_length_flag)
{
if (previous->entry_speed < current->entry_speed)
{
double entry_speed = std::min(current->entry_speed, max_allowable_speed(-previous->acceleration,previous->entry_speed,previous->distance) );
// Check for junction speed change
if (current->entry_speed != entry_speed)
{
current->entry_speed = entry_speed;
current->recalculate_flag = true;
}
}
}
}
void TimeEstimateCalculator::forward_pass()
{
Block* block[3] = {nullptr, nullptr, nullptr};
for(unsigned int n=0; n<blocks.size(); n++)
{
block[0]= block[1];
block[1]= block[2];
block[2] = &blocks[n];
planner_forward_pass_kernel(block[0], block[1], block[2]);
}
planner_forward_pass_kernel(block[1], block[2], nullptr);
}
// Recalculates the trapezoid speed profiles for all blocks in the plan according to the
// entry_factor for each junction. Must be called by planner_recalculate() after
// updating the blocks.
void TimeEstimateCalculator::recalculate_trapezoids()
{
Block *current;
Block *next = nullptr;
for(unsigned int n=0; n<blocks.size(); n--)
{
current = next;
next = &blocks[n];
if (current)
{
// Recalculate if current block entry or exit junction speed has changed.
if (current->recalculate_flag || next->recalculate_flag)
{
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
calculate_trapezoid_for_block(current, current->entry_speed/current->nominal_feedrate, next->entry_speed/current->nominal_feedrate);
current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
}
}
}
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
if(next != nullptr)
{
calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_feedrate, MINIMUM_PLANNER_SPEED/next->nominal_feedrate);
next->recalculate_flag = false;
}
}
+76 -76
Ver Arquivo
@@ -1,76 +1,76 @@
#ifndef TIME_ESTIMATE_H
#define TIME_ESTIMATE_H
#include <stdint.h>
#include <vector>
/**
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
Some of this code has been addapted from the Marlin sources.
*/
class TimeEstimateCalculator
{
public:
const static unsigned int NUM_AXIS = 4;
const static unsigned int X_AXIS = 0;
const static unsigned int Y_AXIS = 1;
const static unsigned int Z_AXIS = 2;
const static unsigned int E_AXIS = 3;
class Position
{
public:
Position() {for(unsigned int n=0;n<NUM_AXIS;n++) axis[n] = 0;}
Position(double x, double y, double z, double e) {axis[0] = x;axis[1] = y;axis[2] = z;axis[3] = e;}
double axis[NUM_AXIS];
double& operator[](const int n) { return axis[n]; }
};
class Block
{
public:
bool recalculate_flag;
double accelerate_until;
double decelerate_after;
double initial_feedrate;
double final_feedrate;
double entry_speed;
double max_entry_speed;
bool nominal_length_flag;
double nominal_feedrate;
double maxTravel;
double distance;
double acceleration;
Position delta;
Position absDelta;
};
private:
Position previous_feedrate;
double previous_nominal_feedrate;
Position currentPosition;
std::vector<Block> blocks;
public:
void setPosition(Position newPos);
void plan(Position newPos, double feedRate);
void reset();
double calculate();
private:
void reverse_pass();
void forward_pass();
void recalculate_trapezoids();
void calculate_trapezoid_for_block(Block *block, double entry_factor, double exit_factor);
void planner_reverse_pass_kernel(Block *previous, Block *current, Block *next);
void planner_forward_pass_kernel(Block *previous, Block *current, Block *next);
};
#endif//TIME_ESTIMATE_H
#ifndef TIME_ESTIMATE_H
#define TIME_ESTIMATE_H
#include <stdint.h>
#include <vector>
/**
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
Some of this code has been addapted from the Marlin sources.
*/
class TimeEstimateCalculator
{
public:
const static unsigned int NUM_AXIS = 4;
const static unsigned int X_AXIS = 0;
const static unsigned int Y_AXIS = 1;
const static unsigned int Z_AXIS = 2;
const static unsigned int E_AXIS = 3;
class Position
{
public:
Position() {for(unsigned int n=0;n<NUM_AXIS;n++) axis[n] = 0;}
Position(double x, double y, double z, double e) {axis[0] = x;axis[1] = y;axis[2] = z;axis[3] = e;}
double axis[NUM_AXIS];
double& operator[](const int n) { return axis[n]; }
};
class Block
{
public:
bool recalculate_flag;
double accelerate_until;
double decelerate_after;
double initial_feedrate;
double final_feedrate;
double entry_speed;
double max_entry_speed;
bool nominal_length_flag;
double nominal_feedrate;
double maxTravel;
double distance;
double acceleration;
Position delta;
Position absDelta;
};
private:
Position previous_feedrate;
double previous_nominal_feedrate;
Position currentPosition;
std::vector<Block> blocks;
public:
void setPosition(Position newPos);
void plan(Position newPos, double feedRate);
void reset();
double calculate();
private:
void reverse_pass();
void forward_pass();
void recalculate_trapezoids();
void calculate_trapezoid_for_block(Block *block, double entry_factor, double exit_factor);
void planner_reverse_pass_kernel(Block *previous, Block *current, Block *next);
void planner_forward_pass_kernel(Block *previous, Block *current, Block *next);
};
#endif//TIME_ESTIMATE_H
+83 -83
Ver Arquivo
@@ -1,83 +1,83 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef FLOAT_POINT_H
#define FLOAT_POINT_H
/*
Floating point 3D points are used during model loading as 3D vectors.
They represent millimeters in 3D space.
*/
#include "intpoint.h"
#include <stdint.h>
#include <math.h>
class FPoint3
{
public:
double x,y,z;
FPoint3() {}
FPoint3(double _x, double _y, double _z): x(_x), y(_y), z(_z) {}
FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); }
FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); }
FPoint3 operator*(const double f) const { return FPoint3(x*f, y*f, z*f); }
FPoint3 operator/(const double f) const { return FPoint3(x/f, y/f, z/f); }
FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; }
FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
double max()
{
if (x > y && x > z) return x;
if (y > z) return y;
return z;
}
bool testLength(double len)
{
return vSize2() <= len*len;
}
double vSize2()
{
return x*x+y*y+z*z;
}
double vSize()
{
return sqrt(vSize2());
}
};
class FMatrix3x3
{
public:
double m[3][3];
FMatrix3x3()
{
m[0][0] = 1.0;
m[1][0] = 0.0;
m[2][0] = 0.0;
m[0][1] = 0.0;
m[1][1] = 1.0;
m[2][1] = 0.0;
m[0][2] = 0.0;
m[1][2] = 0.0;
m[2][2] = 1.0;
}
Point3 apply(FPoint3 p)
{
return Point3(
MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]),
MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1]),
MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2]));
}
};
#endif//INT_POINT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef FLOAT_POINT_H
#define FLOAT_POINT_H
/*
Floating point 3D points are used during model loading as 3D vectors.
They represent millimeters in 3D space.
*/
#include "intpoint.h"
#include <stdint.h>
#include <math.h>
class FPoint3
{
public:
double x,y,z;
FPoint3() {}
FPoint3(double _x, double _y, double _z): x(_x), y(_y), z(_z) {}
FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); }
FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); }
FPoint3 operator*(const double f) const { return FPoint3(x*f, y*f, z*f); }
FPoint3 operator/(const double f) const { return FPoint3(x/f, y/f, z/f); }
FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; }
FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
double max()
{
if (x > y && x > z) return x;
if (y > z) return y;
return z;
}
bool testLength(double len)
{
return vSize2() <= len*len;
}
double vSize2()
{
return x*x+y*y+z*z;
}
double vSize()
{
return sqrt(vSize2());
}
};
class FMatrix3x3
{
public:
double m[3][3];
FMatrix3x3()
{
m[0][0] = 1.0;
m[1][0] = 0.0;
m[2][0] = 0.0;
m[0][1] = 0.0;
m[1][1] = 1.0;
m[2][1] = 0.0;
m[0][2] = 0.0;
m[1][2] = 0.0;
m[2][2] = 1.0;
}
Point3 apply(FPoint3 p)
{
return Point3(
MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]),
MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1]),
MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2]));
}
};
#endif//INT_POINT_H
+14 -14
Ver Arquivo
@@ -1,14 +1,14 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "gettime.h"
TimeKeeper::TimeKeeper()
{
restart();
}
double TimeKeeper::restart()
{
double ret = getTime() - startTime;
startTime = getTime();
return ret;
}
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "gettime.h"
TimeKeeper::TimeKeeper()
{
restart();
}
double TimeKeeper::restart()
{
double ret = getTime() - startTime;
startTime = getTime();
return ret;
}
+3 -3
Ver Arquivo
@@ -2,7 +2,7 @@
#ifndef GETTIME_H
#define GETTIME_H
#ifdef WIN32
#ifdef __WIN32
#include <windows.h>
#else
#include <sys/time.h>
@@ -11,11 +11,11 @@
static inline double getTime()
{
#ifdef WIN32
#ifdef __WIN32
return double(GetTickCount()) / 1000.0;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
gettimeofday(&tv, nullptr);
return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0;
#endif
}
+203 -189
Ver Arquivo
@@ -1,189 +1,203 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INT_POINT_H
#define INT_POINT_H
/*
The integer point classes are used as soon as possible and represent microns in 2D or 3D space.
Integer points are used to avoid floating point rounding errors, and because ClipperLib uses them.
*/
#define INLINE static inline
//Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition.
#include "../clipper/clipper.hpp"
#include <limits.h>
#include <stdint.h>
#include <math.h>
#define INT2MM(n) (double(n) / 1000.0)
#define MM2INT(n) (int64_t((n) * 1000))
#define INT2MICRON(n) ((n) / 1)
#define MICRON2INT(n) ((n) * 1)
class Point3
{
public:
int32_t x,y,z;
Point3() {}
Point3(const int32_t _x, const int32_t _y, const int32_t _z): x(_x), y(_y), z(_z) {}
Point3 operator+(const Point3& p) const { return Point3(x+p.x, y+p.y, z+p.z); }
Point3 operator-(const Point3& p) const { return Point3(x-p.x, y-p.y, z-p.z); }
Point3 operator/(const int32_t i) const { return Point3(x/i, y/i, z/i); }
Point3& operator += (const Point3& p) { x += p.x; y += p.y; z += p.z; return *this; }
Point3& operator -= (const Point3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
bool operator==(const Point3& p) const { return x==p.x&&y==p.y&&z==p.z; }
bool operator!=(const Point3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
int32_t max()
{
if (x > y && x > z) return x;
if (y > z) return y;
return z;
}
bool testLength(int32_t len)
{
if (x > len || x < -len)
return false;
if (y > len || y < -len)
return false;
if (z > len || z < -len)
return false;
return vSize2() <= len*len;
}
int64_t vSize2()
{
return int64_t(x)*int64_t(x)+int64_t(y)*int64_t(y)+int64_t(z)*int64_t(z);
}
int32_t vSize()
{
return sqrt(vSize2());
}
Point3 cross(const Point3& p)
{
return Point3(
y*p.z-z*p.y,
z*p.x-x*p.z,
x*p.y-y*p.x);
}
};
/* 64bit Points are used mostly troughout the code, these are the 2D points from ClipperLib */
typedef ClipperLib::IntPoint Point;
class IntPoint {
public:
int X, Y;
Point p() { return Point(X, Y); }
};
/* Extra operators to make it easier to do math with the 64bit Point objects */
INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); }
INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); }
INLINE Point operator*(const Point& p0, const int32_t i) { return Point(p0.X*i, p0.Y*i); }
INLINE Point operator/(const Point& p0, const int32_t i) { return Point(p0.X/i, p0.Y/i); }
//Point& operator += (const Point& p) { x += p.x; y += p.y; return *this; }
//Point& operator -= (const Point& p) { x -= p.x; y -= p.y; return *this; }
INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; }
INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; }
INLINE int64_t vSize2(const Point& p0)
{
return p0.X*p0.X+p0.Y*p0.Y;
}
INLINE float vSize2f(const Point& p0)
{
return float(p0.X)*float(p0.X)+float(p0.Y)*float(p0.Y);
}
INLINE bool shorterThen(const Point& p0, int32_t len)
{
if (p0.X > len || p0.X < -len)
return false;
if (p0.Y > len || p0.Y < -len)
return false;
return vSize2(p0) <= len*len;
}
INLINE int64_t vSize(const Point& p0)
{
return sqrt(vSize2(p0));
}
INLINE double vSizeMM(const Point& p0)
{
double fx = INT2MM(p0.X);
double fy = INT2MM(p0.Y);
return sqrt(fx*fx+fy*fy);
}
INLINE Point normal(const Point& p0, int64_t len)
{
int64_t _len = vSize(p0);
if (_len < 1)
return Point(len, 0);
return p0 * len / _len;
}
INLINE Point crossZ(const Point& p0)
{
return Point(-p0.Y, p0.X);
}
INLINE int64_t dot(const Point& p0, const Point& p1)
{
return p0.X * p1.X + p0.Y * p1.Y;
}
class PointMatrix
{
public:
double matrix[4];
PointMatrix()
{
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
}
PointMatrix(double rotation)
{
rotation = rotation / 180 * M_PI;
matrix[0] = cos(rotation);
matrix[1] = -sin(rotation);
matrix[2] = -matrix[1];
matrix[3] = matrix[0];
}
PointMatrix(const Point p)
{
matrix[0] = p.X;
matrix[1] = p.Y;
double f = sqrt((matrix[0] * matrix[0]) + (matrix[1] * matrix[1]));
matrix[0] /= f;
matrix[1] /= f;
matrix[2] = -matrix[1];
matrix[3] = matrix[0];
}
Point apply(const Point p) const
{
return Point(p.X * matrix[0] + p.Y * matrix[1], p.X * matrix[2] + p.Y * matrix[3]);
}
Point unapply(const Point p) const
{
return Point(p.X * matrix[0] + p.Y * matrix[2], p.X * matrix[1] + p.Y * matrix[3]);
}
};
#endif//INT_POINT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INT_POINT_H
#define INT_POINT_H
/*
The integer point classes are used as soon as possible and represent microns in 2D or 3D space.
Integer points are used to avoid floating point rounding errors, and because ClipperLib uses them.
*/
#define INLINE static inline
//Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition.
#include <clipper/clipper.hpp>
#include <limits>
#include <stdint.h>
#include <cmath>
#define INT2MM(n) (double(n) / 1000.0)
#define MM2INT(n) (int64_t((n) * 1000))
#define INT2MICRON(n) ((n) / 1)
#define MICRON2INT(n) ((n) * 1)
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
class Point3
{
public:
int32_t x,y,z;
Point3() {}
Point3(const int32_t _x, const int32_t _y, const int32_t _z): x(_x), y(_y), z(_z) {}
Point3 operator+(const Point3& p) const { return Point3(x+p.x, y+p.y, z+p.z); }
Point3 operator-(const Point3& p) const { return Point3(x-p.x, y-p.y, z-p.z); }
Point3 operator/(const int32_t i) const { return Point3(x/i, y/i, z/i); }
Point3& operator += (const Point3& p) { x += p.x; y += p.y; z += p.z; return *this; }
Point3& operator -= (const Point3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
bool operator==(const Point3& p) const { return x==p.x&&y==p.y&&z==p.z; }
bool operator!=(const Point3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
int32_t max()
{
if (x > y && x > z) return x;
if (y > z) return y;
return z;
}
bool testLength(int32_t len)
{
if (x > len || x < -len)
return false;
if (y > len || y < -len)
return false;
if (z > len || z < -len)
return false;
return vSize2() <= len*len;
}
int64_t vSize2()
{
return int64_t(x)*int64_t(x)+int64_t(y)*int64_t(y)+int64_t(z)*int64_t(z);
}
int32_t vSize()
{
return sqrt(vSize2());
}
Point3 cross(const Point3& p)
{
return Point3(
y*p.z-z*p.y,
z*p.x-x*p.z,
x*p.y-y*p.x);
}
};
/* 64bit Points are used mostly troughout the code, these are the 2D points from ClipperLib */
typedef ClipperLib::IntPoint Point;
class IntPoint {
public:
int X, Y;
Point p() { return Point(X, Y); }
};
#define POINT_MIN std::numeric_limits<ClipperLib::cInt>::min()
#define POINT_MAX std::numeric_limits<ClipperLib::cInt>::max()
/* Extra operators to make it easier to do math with the 64bit Point objects */
INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); }
INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); }
INLINE Point operator*(const Point& p0, const int32_t i) { return Point(p0.X*i, p0.Y*i); }
INLINE Point operator/(const Point& p0, const int32_t i) { return Point(p0.X/i, p0.Y/i); }
//Point& operator += (const Point& p) { x += p.x; y += p.y; return *this; }
//Point& operator -= (const Point& p) { x -= p.x; y -= p.y; return *this; }
INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; }
INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; }
INLINE int64_t vSize2(const Point& p0)
{
return p0.X*p0.X+p0.Y*p0.Y;
}
INLINE float vSize2f(const Point& p0)
{
return float(p0.X)*float(p0.X)+float(p0.Y)*float(p0.Y);
}
INLINE bool shorterThen(const Point& p0, int32_t len)
{
if (p0.X > len || p0.X < -len)
return false;
if (p0.Y > len || p0.Y < -len)
return false;
return vSize2(p0) <= len*len;
}
INLINE int64_t vSize(const Point& p0)
{
return sqrt(vSize2(p0));
}
INLINE double vSizeMM(const Point& p0)
{
double fx = INT2MM(p0.X);
double fy = INT2MM(p0.Y);
return sqrt(fx*fx+fy*fy);
}
INLINE Point normal(const Point& p0, int64_t len)
{
int64_t _len = vSize(p0);
if (_len < 1)
return Point(len, 0);
return p0 * len / _len;
}
INLINE Point crossZ(const Point& p0)
{
return Point(-p0.Y, p0.X);
}
INLINE int64_t dot(const Point& p0, const Point& p1)
{
return p0.X * p1.X + p0.Y * p1.Y;
}
INLINE int angle(const Point& p)
{
double angle = std::atan2(p.X, p.Y) / M_PI * 180.0;
if (angle < 0.0) angle += 360.0;
return angle;
}
class PointMatrix
{
public:
double matrix[4];
PointMatrix()
{
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 1;
}
PointMatrix(double rotation)
{
rotation = rotation / 180 * M_PI;
matrix[0] = cos(rotation);
matrix[1] = -sin(rotation);
matrix[2] = -matrix[1];
matrix[3] = matrix[0];
}
PointMatrix(const Point p)
{
matrix[0] = p.X;
matrix[1] = p.Y;
double f = sqrt((matrix[0] * matrix[0]) + (matrix[1] * matrix[1]));
matrix[0] /= f;
matrix[1] /= f;
matrix[2] = -matrix[1];
matrix[3] = matrix[0];
}
Point apply(const Point p) const
{
return Point(p.X * matrix[0] + p.Y * matrix[1], p.X * matrix[2] + p.Y * matrix[3]);
}
Point unapply(const Point p) const
{
return Point(p.X * matrix[0] + p.Y * matrix[2], p.X * matrix[1] + p.Y * matrix[3]);
}
};
#endif//INT_POINT_H
+5 -1
Ver Arquivo
@@ -4,6 +4,8 @@
#include "logoutput.h"
namespace cura {
static int verbose_level;
static bool progressLogging;
@@ -26,7 +28,7 @@ void logError(const char* fmt, ...)
fflush(stderr);
}
void _log(const char* fmt, ...)
void log(const char* fmt, ...)
{
if (verbose_level < 1)
return;
@@ -45,3 +47,5 @@ void logProgress(const char* type, int value, int maxValue)
fprintf(stderr, "Progress:%s:%i:%i\n", type, value, maxValue);
fflush(stderr);
}
}//namespace cura
+5 -2
Ver Arquivo
@@ -2,16 +2,19 @@
#ifndef LOGOUTPUT_H
#define LOGOUTPUT_H
namespace cura {
void increaseVerboseLevel();
void enableProgressLogging();
//Report an error message (always reported, independed of verbose level)
void logError(const char* fmt, ...);
//Report a message if the verbose level is 1 or higher. (defined as _log to prevent clash with log() function from <math.h>)
void _log(const char* fmt, ...);
#define log _log
void log(const char* fmt, ...);
//Report engine progress to interface if any. Only if "enableProgressLogging()" has been called.
void logProgress(const char* type, int value, int maxValue);
}//namespace cura
#endif//LOGOUTPUT_H
+405 -337
Ver Arquivo
@@ -1,337 +1,405 @@
#ifndef UTILS_POLYGON_H
#define UTILS_POLYGON_H
#include <vector>
#include <assert.h>
#include <float.h>
using std::vector;
#include "../clipper/clipper.hpp"
#include "intpoint.h"
//#define CHECK_POLY_ACCESS
#ifdef CHECK_POLY_ACCESS
#define POLY_ASSERT(e) assert(e)
#else
#define POLY_ASSERT(e) do {} while(0)
#endif
#define CLIPPER_INIT (0)
class PolygonRef
{
ClipperLib::Path* polygon;
PolygonRef()
: polygon(NULL)
{}
public:
PolygonRef(ClipperLib::Path& polygon)
: polygon(&polygon)
{}
unsigned int size() const
{
return polygon->size();
}
Point operator[] (unsigned int index) const
{
POLY_ASSERT(index < size());
return (*polygon)[index];
}
void* data()
{
return polygon->data();
}
void add(const Point p)
{
polygon->push_back(p);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
polygon->erase(polygon->begin() + index);
}
void clear()
{
polygon->clear();
}
bool orientation() const
{
return ClipperLib::Orientation(*polygon);
}
void reverse()
{
ClipperLib::ReversePath(*polygon);
}
int64_t polygonLength() const
{
int64_t length = 0;
Point p0 = (*polygon)[polygon->size()-1];
for(unsigned int n=0; n<polygon->size(); n++)
{
Point p1 = (*polygon)[n];
length += vSize(p0 - p1);
p0 = p1;
}
return length;
}
double area() const
{
return ClipperLib::Area(*polygon);
}
Point centerOfMass() const
{
double x = 0, y = 0;
Point p0 = (*polygon)[polygon->size()-1];
for(unsigned int n=0; n<polygon->size(); n++)
{
Point p1 = (*polygon)[n];
double second_factor = (p0.X * p1.Y) - (p1.X * p0.Y);
x += double(p0.X + p1.X) * second_factor;
y += double(p0.Y + p1.Y) * second_factor;
p0 = p1;
}
double area = Area(*polygon);
x = x / 6 / area;
y = y / 6 / area;
if (x < 0)
{
x = -x;
y = -y;
}
return Point(x, y);
}
Point closestPointTo(Point p)
{
Point ret = p;
float bestDist = FLT_MAX;
for(unsigned int n=0; n<polygon->size(); n++)
{
float dist = vSize2f(p - (*polygon)[n]);
if (dist < bestDist)
{
ret = (*polygon)[n];
bestDist = dist;
}
}
return ret;
}
friend class Polygons;
};
class _Polygon : public PolygonRef
{
ClipperLib::Path poly;
public:
_Polygon()
: PolygonRef(poly)
{
}
};
//<windows.h> defines a Polygon structure, to prevent a clash define Polygon as _Polygon.
#define Polygon _Polygon
class Polygons
{
private:
ClipperLib::Paths polygons;
public:
unsigned int size()
{
return polygons.size();
}
PolygonRef operator[] (unsigned int index)
{
POLY_ASSERT(index < size());
return PolygonRef(polygons[index]);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
polygons.erase(polygons.begin() + index);
}
void clear()
{
polygons.clear();
}
void add(const PolygonRef& poly)
{
polygons.push_back(*poly.polygon);
}
void add(const Polygons& other)
{
for(unsigned int n=0; n<other.polygons.size(); n++)
polygons.push_back(other.polygons[n]);
}
PolygonRef newPoly()
{
polygons.push_back(ClipperLib::Path());
return PolygonRef(polygons[polygons.size()-1]);
}
Polygons() {}
Polygons(const Polygons& other) { polygons = other.polygons; }
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
Polygons difference(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(CLIPPER_INIT);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, ret.polygons);
return ret;
}
Polygons unionPolygons(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(CLIPPER_INIT);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptSubject, true);
clipper.Execute(ClipperLib::ctUnion, ret.polygons, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
return ret;
}
Polygons intersection(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(CLIPPER_INIT);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctIntersection, ret.polygons);
return ret;
}
Polygons offset(int distance) const
{
Polygons ret;
ClipperLib::ClipperOffset clipper;
clipper.AddPaths(polygons, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
clipper.MiterLimit = 2.0;
clipper.Execute(ret.polygons, distance);
return ret;
}
vector<Polygons> splitIntoParts(bool unionAll = false) const
{
vector<Polygons> ret;
ClipperLib::Clipper clipper(CLIPPER_INIT);
ClipperLib::PolyTree resultPolyTree;
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
_processPolyTreeNode(&resultPolyTree, ret);
return ret;
}
private:
void _processPolyTreeNode(ClipperLib::PolyNode* node, vector<Polygons>& ret) const
{
for(int n=0; n<node->ChildCount(); n++)
{
ClipperLib::PolyNode* child = node->Childs[n];
Polygons polygons;
polygons.add(child->Contour);
for(int i=0; i<child->ChildCount(); i++)
{
polygons.add(child->Childs[i]->Contour);
_processPolyTreeNode(child->Childs[i], ret);
}
ret.push_back(polygons);
}
}
public:
Polygons processEvenOdd() const
{
Polygons ret;
ClipperLib::Clipper clipper(CLIPPER_INIT);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.Execute(ClipperLib::ctUnion, ret.polygons);
return ret;
}
int64_t polygonLength() const
{
int64_t length = 0;
for(unsigned int i=0; i<polygons.size(); i++)
{
Point p0 = polygons[i][polygons[i].size()-1];
for(unsigned int n=0; n<polygons[i].size(); n++)
{
Point p1 = polygons[i][n];
length += vSize(p0 - p1);
p0 = p1;
}
}
return length;
}
void applyMatrix(const PointMatrix& matrix)
{
for(unsigned int i=0; i<polygons.size(); i++)
{
for(unsigned int j=0; j<polygons[i].size(); j++)
{
polygons[i][j] = matrix.apply(polygons[i][j]);
}
}
}
};
/* Axis aligned boundary box */
class AABB
{
public:
Point min, max;
AABB()
: min(LLONG_MIN, LLONG_MIN), max(LLONG_MIN, LLONG_MIN)
{
}
AABB(Polygons polys)
: min(LLONG_MIN, LLONG_MIN), max(LLONG_MIN, LLONG_MIN)
{
calculate(polys);
}
void calculate(Polygons polys)
{
min = Point(LLONG_MAX, LLONG_MAX);
max = Point(LLONG_MIN, LLONG_MIN);
for(unsigned int i=0; i<polys.size(); i++)
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
}
}
}
bool hit(const AABB& other) const
{
if (max.X < other.min.X) return false;
if (min.X > other.max.X) return false;
if (max.Y < other.min.Y) return false;
if (min.Y > other.max.Y) return false;
return true;
}
};
#endif//UTILS_POLYGON_H
#ifndef UTILS_POLYGON_H
#define UTILS_POLYGON_H
#include <vector>
#include <assert.h>
#include <float.h>
using std::vector;
#include <clipper/clipper.hpp>
#include "intpoint.h"
//#define CHECK_POLY_ACCESS
#ifdef CHECK_POLY_ACCESS
#define POLY_ASSERT(e) assert(e)
#else
#define POLY_ASSERT(e) do {} while(0)
#endif
namespace cura {
const static int clipper_init = (0);
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
class PolygonRef
{
ClipperLib::Path* polygon;
PolygonRef()
: polygon(nullptr)
{}
public:
PolygonRef(ClipperLib::Path& polygon)
: polygon(&polygon)
{}
unsigned int size() const
{
return polygon->size();
}
Point operator[] (unsigned int index) const
{
POLY_ASSERT(index < size());
return (*polygon)[index];
}
void* data()
{
return polygon->data();
}
void add(const Point p)
{
polygon->push_back(p);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
polygon->erase(polygon->begin() + index);
}
void clear()
{
polygon->clear();
}
bool orientation() const
{
return ClipperLib::Orientation(*polygon);
}
void reverse()
{
ClipperLib::ReversePath(*polygon);
}
int64_t polygonLength() const
{
int64_t length = 0;
Point p0 = (*polygon)[polygon->size()-1];
for(unsigned int n=0; n<polygon->size(); n++)
{
Point p1 = (*polygon)[n];
length += vSize(p0 - p1);
p0 = p1;
}
return length;
}
double area() const
{
return ClipperLib::Area(*polygon);
}
Point centerOfMass() const
{
double x = 0, y = 0;
Point p0 = (*polygon)[polygon->size()-1];
for(unsigned int n=0; n<polygon->size(); n++)
{
Point p1 = (*polygon)[n];
double second_factor = (p0.X * p1.Y) - (p1.X * p0.Y);
x += double(p0.X + p1.X) * second_factor;
y += double(p0.Y + p1.Y) * second_factor;
p0 = p1;
}
double area = Area(*polygon);
x = x / 6 / area;
y = y / 6 / area;
if (x < 0)
{
x = -x;
y = -y;
}
return Point(x, y);
}
Point closestPointTo(Point p)
{
Point ret = p;
float bestDist = FLT_MAX;
for(unsigned int n=0; n<polygon->size(); n++)
{
float dist = vSize2f(p - (*polygon)[n]);
if (dist < bestDist)
{
ret = (*polygon)[n];
bestDist = dist;
}
}
return ret;
}
//Check if we are inside the polygon. We do this by tracing from the point towards the negative X direction,
// every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
bool inside(Point p)
{
if (polygon->size() < 1)
return false;
int crossings = 0;
Point p0 = (*polygon)[polygon->size()-1];
for(unsigned int n=0; n<polygon->size(); n++)
{
Point p1 = (*polygon)[n];
if ((p0.Y >= p.Y && p1.Y < p.Y) || (p1.Y > p.Y && p0.Y <= p.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (p.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= p.X)
crossings ++;
}
p0 = p1;
}
return (crossings % 2) == 1;
}
ClipperLib::Path::iterator begin()
{
return polygon->begin();
}
ClipperLib::Path::iterator end()
{
return polygon->end();
}
ClipperLib::Path::const_iterator begin() const
{
return polygon->begin();
}
ClipperLib::Path::const_iterator end() const
{
return polygon->end();
}
friend class Polygons;
friend class Polygon;
};
class Polygon : public PolygonRef
{
ClipperLib::Path poly;
public:
Polygon()
: PolygonRef(poly)
{
}
Polygon(const PolygonRef& other)
: PolygonRef(poly)
{
poly = *other.polygon;
}
};
class Polygons
{
private:
ClipperLib::Paths polygons;
public:
unsigned int size()
{
return polygons.size();
}
PolygonRef operator[] (unsigned int index)
{
POLY_ASSERT(index < size());
return PolygonRef(polygons[index]);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
polygons.erase(polygons.begin() + index);
}
void clear()
{
polygons.clear();
}
void add(const PolygonRef& poly)
{
polygons.push_back(*poly.polygon);
}
void add(const Polygons& other)
{
for(unsigned int n=0; n<other.polygons.size(); n++)
polygons.push_back(other.polygons[n]);
}
PolygonRef newPoly()
{
polygons.push_back(ClipperLib::Path());
return PolygonRef(polygons[polygons.size()-1]);
}
Polygons() {}
Polygons(const Polygons& other) { polygons = other.polygons; }
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
Polygons difference(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, ret.polygons);
return ret;
}
Polygons unionPolygons(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptSubject, true);
clipper.Execute(ClipperLib::ctUnion, ret.polygons, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
return ret;
}
Polygons intersection(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.AddPaths(other.polygons, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctIntersection, ret.polygons);
return ret;
}
Polygons offset(int distance) const
{
Polygons ret;
ClipperLib::ClipperOffset clipper;
clipper.AddPaths(polygons, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
clipper.MiterLimit = 2.0;
clipper.Execute(ret.polygons, distance);
return ret;
}
vector<Polygons> splitIntoParts(bool unionAll = false) const
{
vector<Polygons> ret;
ClipperLib::Clipper clipper(clipper_init);
ClipperLib::PolyTree resultPolyTree;
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
_processPolyTreeNode(&resultPolyTree, ret);
return ret;
}
private:
void _processPolyTreeNode(ClipperLib::PolyNode* node, vector<Polygons>& ret) const
{
for(int n=0; n<node->ChildCount(); n++)
{
ClipperLib::PolyNode* child = node->Childs[n];
Polygons polygons;
polygons.add(child->Contour);
for(int i=0; i<child->ChildCount(); i++)
{
polygons.add(child->Childs[i]->Contour);
_processPolyTreeNode(child->Childs[i], ret);
}
ret.push_back(polygons);
}
}
public:
Polygons processEvenOdd() const
{
Polygons ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
clipper.Execute(ClipperLib::ctUnion, ret.polygons);
return ret;
}
int64_t polygonLength() const
{
int64_t length = 0;
for(unsigned int i=0; i<polygons.size(); i++)
{
Point p0 = polygons[i][polygons[i].size()-1];
for(unsigned int n=0; n<polygons[i].size(); n++)
{
Point p1 = polygons[i][n];
length += vSize(p0 - p1);
p0 = p1;
}
}
return length;
}
bool inside(Point p)
{
if (size() < 1)
return false;
if (!(*this)[0].inside(p))
return false;
for(unsigned int n=1; n<polygons.size(); n++)
{
if ((*this)[n].inside(p))
return false;
}
return true;
}
void applyMatrix(const PointMatrix& matrix)
{
for(unsigned int i=0; i<polygons.size(); i++)
{
for(unsigned int j=0; j<polygons[i].size(); j++)
{
polygons[i][j] = matrix.apply(polygons[i][j]);
}
}
}
};
/* Axis aligned boundary box */
class AABB
{
public:
Point min, max;
AABB()
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
{
}
AABB(Polygons polys)
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
{
calculate(polys);
}
void calculate(Polygons polys)
{
min = Point(POINT_MAX, POINT_MAX);
max = Point(POINT_MIN, POINT_MIN);
for(unsigned int i=0; i<polys.size(); i++)
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
}
}
}
bool hit(const AABB& other) const
{
if (max.X < other.min.X) return false;
if (min.X > other.max.X) return false;
if (max.Y < other.min.Y) return false;
if (min.Y > other.max.Y) return false;
return true;
}
};
}//namespace cura
#endif//UTILS_POLYGON_H
+1
Ver Arquivo
@@ -0,0 +1 @@
+119 -117
Ver Arquivo
@@ -1,117 +1,119 @@
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "socket.h"
#include "logoutput.h"
#ifdef WIN32
bool wsaStartupDone = false;
#endif
ClientSocket::ClientSocket()
{
sockfd = -1;
#ifdef WIN32
if (!wsaStartupDone)
{
WSADATA wsaData = {0};
//WSAStartup needs to be called on windows before sockets can be used. Request version 1.1, which is supported on windows 98 and higher.
WSAStartup(MAKEWORD(1, 1), &wsaData);
wsaStartupDone = true;
}
#endif
}
void ClientSocket::connectTo(std::string host, int port)
{
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(host.c_str());
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("Connect to %s:%d failed\n", host.c_str(), port);
close();
return;
}
}
ClientSocket::~ClientSocket()
{
close();
}
void ClientSocket::sendNr(int nr)
{
sendAll(&nr, sizeof(int));
}
void ClientSocket::sendAll(const void* data, int length)
{
if (sockfd == -1)
return;
const char* ptr = (const char*)data;
while(length > 0)
{
int n = send(sockfd, ptr, length, 0);
if (n <= 0)
{
close();
return;
}
ptr += length;
length -= n;
}
}
int ClientSocket::recvNr()
{
int ret = 0;
recvAll(&ret, 4);
return ret;
}
void ClientSocket::recvAll(void* data, int length)
{
if (sockfd == -1)
return;
char* ptr = (char*)data;
while(length > 0)
{
int n = recv(sockfd, ptr, length, 0);
if (n <= 0)
{
log("ClientSocket::recvAll error...");
close();
return;
}
ptr += n;
length -= n;
}
}
void ClientSocket::close()
{
if (sockfd == -1)
return;
#ifdef WIN32
closesocket(sockfd);
#else
::close(sockfd);
#endif
sockfd = -1;
}
#include <stdio.h>
#include <string.h>
#ifdef __WIN32
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "socket.h"
#include "logoutput.h"
#ifdef __WIN32
bool wsaStartupDone = false;
#endif
ClientSocket::ClientSocket()
{
sockfd = -1;
#ifdef __WIN32
if (!wsaStartupDone)
{
WSADATA wsaData;
memset(&wsaData, 0, sizeof(WSADATA));
//WSAStartup needs to be called on windows before sockets can be used. Request version 1.1, which is supported on windows 98 and higher.
WSAStartup(MAKEWORD(1, 1), &wsaData);
wsaStartupDone = true;
}
#endif
}
void ClientSocket::connectTo(std::string host, int port)
{
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(host.c_str());
// TODO: Check this: C style cast replaced by reinterpret_cast!!
if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&serv_addr), sizeof(serv_addr)) < 0)
{
printf("Connect to %s:%d failed\n", host.c_str(), port);
close();
return;
}
}
ClientSocket::~ClientSocket()
{
close();
}
void ClientSocket::sendNr(int nr)
{
sendAll(&nr, sizeof(int));
}
void ClientSocket::sendAll(const void* data, int length)
{
if (sockfd == -1)
return;
const char* ptr = static_cast<const char*>(data);
while(length > 0)
{
int n = send(sockfd, ptr, length, 0);
if (n <= 0)
{
close();
return;
}
ptr += length;
length -= n;
}
}
int ClientSocket::recvNr()
{
int ret = 0;
recvAll(&ret, 4);
return ret;
}
void ClientSocket::recvAll(void* data, int length)
{
if (sockfd == -1)
return;
char* ptr = static_cast<char*>(data);
while(length > 0)
{
int n = recv(sockfd, ptr, length, 0);
if (n <= 0)
{
cura::log("ClientSocket::recvAll error...");
close();
return;
}
ptr += n;
length -= n;
}
}
void ClientSocket::close()
{
if (sockfd == -1)
return;
#ifdef __WIN32
closesocket(sockfd);
#else
::close(sockfd);
#endif
sockfd = -1;
}
+23 -23
Ver Arquivo
@@ -1,23 +1,23 @@
#ifndef SOCKET_H
#define SOCKET_H
#include <string>
class ClientSocket
{
int sockfd;
public:
ClientSocket();
~ClientSocket();
void connectTo(std::string host, int port);
void sendNr(int nr);
void sendAll(const void* data, int length);
int recvNr();
void recvAll(void* data, int length);
void close();
};
#endif//SOCKET_H
#ifndef SOCKET_H
#define SOCKET_H
#include <string>
class ClientSocket
{
int sockfd;
public:
ClientSocket();
~ClientSocket();
void connectTo(std::string host, int port);
void sendNr(int nr);
void sendAll(const void* data, int length);
int recvNr();
void recvAll(void* data, int length);
void close();
};
#endif//SOCKET_H
+17
Ver Arquivo
@@ -0,0 +1,17 @@
#ifndef STRING_H
#define STRING_H
//c++11 no longer supplies a strcasecmp, so define our own version.
static inline int stringcasecompare(const char* a, const char* b)
{
while(*a && *b)
{
if (tolower(*a) != tolower(*b))
return tolower(*a) - tolower(*b);
a++;
b++;
}
return *a - *b;
}
#endif//STRING_H
Ver Arquivo
-75
Ver Arquivo
@@ -1,75 +0,0 @@
#ifndef POLYGON_DEBUG_H
#define POLYGON_DEBUG_H
#include <stdio.h>
#include "polygon.h"
class PolygonDebug
{
private:
Polygons polys;
const char* filename;
public:
PolygonDebug(const char* filename)
: filename(filename)
{
}
PolygonDebug(const char* filename, Polygons polys)
: filename(filename)
{
add(polys);
}
PolygonDebug& add(Polygons polys)
{
this->polys.add(polys);
return *this;
}
~PolygonDebug()
{
Point polyMin(INT_MAX, INT_MAX), polyMax(INT_MIN, INT_MIN);
for(unsigned int j=0; j<polys.size(); j++)
{
for(unsigned int n=0; n<polys[j].size(); n++)
{
polyMin.X = std::min(polyMin.X, polys[j][n].X);
polyMin.Y = std::min(polyMin.Y, polys[j][n].Y);
polyMax.X = std::max(polyMax.X, polys[j][n].X);
polyMax.Y = std::max(polyMax.Y, polys[j][n].Y);
}
}
Point polySize = polyMax - polyMin;
FILE* f = fopen(filename, "a");
fprintf(f, "<!DOCTYPE html><html><body>\n");
//for(unsigned int i=0; i<layers.size(); i++)
//{
float scale = std::max(polySize.X, polySize.Y) / 1500;
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(polySize.X / scale), int(polySize.Y / scale));
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
fprintf(f, "<path d=\"");
for(unsigned int j=0; j<polys.size(); j++)
{
PolygonRef p = polys[j];
for(unsigned int n=0; n<p.size(); n++)
{
if (n == 0)
fprintf(f, "M");
else
fprintf(f, "L");
fprintf(f, "%f,%f ", float(p[n].X - polyMin.X)/scale, float(p[n].Y - polyMin.Y)/scale);
}
fprintf(f, "Z\n");
}
fprintf(f, "\"/>");
fprintf(f, "</g>\n");
fprintf(f, "</svg>\n");
//}
fprintf(f, "</body></html>");
fclose(f);
}
};
#endif//POLYGON_DEBUG_H