Comparar commits

..

102 Commits

Autor SHA1 Mensagem Data
daid f0eae492f7 Update to version 13.11.2 2013-11-20 13:30:38 +01:00
daid 1d171e71de Catch exceptions if they happen in Clipper and abort. 2013-11-19 12:30:05 +01:00
daid 16593d3a91 Ignore internal holes on bridge detection. 2013-11-18 11:02:17 +01:00
daid 24c87e639e Update main file to split up processing function into a class. Added wipe-tower support. 2013-11-12 15:29:26 +01:00
daid 5619087429 Add ooze-shield and fix the len bug in the ascii stl reader. 2013-11-11 09:06:28 +01:00
daid f333905fd0 Retract before moving up after finishing the print. 2013-11-07 11:51:25 +01:00
daid 339182611e Only retract always if there are more then 1 islands, to prevent retraction on layer swaps. 2013-11-06 10:21:32 +01:00
daid 73939c7567 Add multiple "total filament" stats on export. Split the support into different islands, making less moves between islands. 2013-11-05 14:07:31 +01:00
daid 8e77f212e5 Version 13.11 2013-11-04 10:52:10 +01:00
daid de7080619c Make sure the head is moved up after the last printed bit, so it does not hit other objects. 2013-11-04 10:51:44 +01:00
daid 5de1559110 Fix a bug where retraction on dual-extrusion does not happen at the proper spot. 2013-11-01 15:01:01 +01:00
daid 1f19060719 Add spialize mode which makes printing of cups and vases easy. 2013-10-30 14:53:56 +01:00
daid 3abf24a314 Fixed #36 2013-10-29 13:41:40 +01:00
daid 744f09eefb Change the support code a bit so it is easier to use, fix brim/skirt handling for support material. 2013-10-29 09:28:01 +01:00
daid 6a3963d903 Fix "everywhere" Z support distance. 2013-10-28 22:26:39 +01:00
daid d873602182 Change how the fan is handled on lower layers. 2013-10-28 14:16:33 +01:00
daid bca34c80d6 Change LONG_LONG_ into LLONG_ as LONG_LONG defines are depricated. 2013-10-28 10:13:07 +01:00
daid 685d11d5d4 Merge pull request #33 from nelsonsilva/limits
Renamed LONG_LONG_MAX to LLONG_MAX
2013-10-28 02:11:00 -07:00
Nelson Silva 9b8ddbe29c Renamed LONG_LONG_MAX to LLONG_MAX 2013-10-25 16:42:32 +01:00
daid 3ca3e1fcfe Fix 45min print time bug. 2013-10-22 11:54:06 +02:00
daid debfa35673 Merge branch 'master' into refactoring 2013-10-21 11:13:53 +02:00
daid 5e310d509c Refactored the headers files into cpp files. 2013-10-21 11:10:43 +02:00
daid 346815a3e0 Update version number. 2013-10-10 13:54:19 +02:00
daid c92139b8a2 Fix the skirt generation (refactor bug) 2013-10-07 18:31:51 +02:00
daid f777218a6d Update the bridge code to use the refactored clipper functions. 2013-10-06 20:31:37 +02:00
daid 4c3875e427 Merge branch 'master' into refactoring 2013-10-06 18:07:04 +02:00
daid 4f5b366af2 Add MakerBot flavored GCode. 2013-10-06 15:29:33 +02:00
daid c4eb84b6a3 Refactoring and cleanup of the code parts that are using Clipper, updated Clipper to version 5.1.6. 2013-10-06 12:15:57 +02:00
daid c338f4aca2 Remove "progress update" to stderr on slicing, because this update causes the stderr buffer to fill up, which is not read by the GUI till the process exits. 2013-10-01 08:56:07 +02:00
daid ee2700872f Remove "progress update" to stderr on slicing, because this update causes the stderr buffer to fill up, which is not read by the GUI till the process exits. 2013-10-01 08:51:28 +02:00
daid 3d3f093e3b Cosmetic codestyle changes. 2013-09-27 12:26:28 +02:00
daid 25cf9c0f9e Merge branch 'master' of github.com:Ultimaker/CuraEngine 2013-09-27 12:14:17 +02:00
daid de7620cea0 Fixing Cura#572 a different way. 2013-09-27 12:14:08 +02:00
Daid 74486d27b4 Fix compile for stupid MacOS compiler. 2013-09-27 11:21:16 +02:00
daid 3621aa88c8 Rename "move" to "travel" at some locations to make clear those are travel moves. Only apply the minimal layer time if we where not slowed down enough already. Fixes the first-layer-speedup issue. 2013-09-27 08:56:06 +02:00
daid ef5a252337 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2013-09-26 12:13:45 +02:00
daid 5640378faa Prevent support from bleeding out of the model. 2013-09-26 12:13:25 +02:00
daid 76689fa9b2 Merge pull request #26 from grimmy/master
Update Makefile to call run_tests.py $(EXECUTABLE)
2013-09-25 10:12:43 -07:00
Gary Kramlich 4094c42872 Ignore gcode files since tests use compressed versions 2013-09-16 08:24:29 -05:00
Gary Kramlich 04fde2a98a Test against compressed gcode 2013-09-16 08:10:38 -05:00
Gary Kramlich 47f04ffb4f Simple fix for the unittests for the moment 2013-09-16 07:57:15 -05:00
Gary Kramlich 7ddd198e03 use CXX instead of CC in Makefile 2013-09-16 07:48:40 -05:00
Gary Kramlich 11f489d06f add the gcode files that somehow got missed 2013-09-16 07:17:29 -05:00
Gary Kramlich 02036f34e6 Avoid a NULL pointer dereference (that clang sees) 2013-09-16 07:12:55 -05:00
Gary Kramlich 9ef3d792fe use the CC envvar if it is set 2013-09-16 07:05:27 -05:00
Gary Kramlich 04780c2115 Update Makefile to call run_tests.py $(EXECUTABLE) 2013-09-16 06:40:22 -05:00
daid 64e5ec3182 Merge pull request #25 from grimmy/master
Random cleanups
2013-09-16 02:54:05 -07:00
Gary Kramlich 6ccca8343f Updated the unittests to check the actual output
I updated the unittests so that the output gcode matches what's expected.  Of course this all matches right now, but this means the engine can be moddified and tested accordingly.
2013-09-16 02:52:32 -05:00
Gary Kramlich 538031171d Un-ingored gcode files
The unittests will be checking that gcode is output correctly, so we'll need references
2013-09-16 02:47:01 -05:00
Gary Kramlich 316861f05c made fprintf log to stderr
Made all fprintf's output to stderr so that you can easily slice a file to stdout and capture the output or push it to a pipe.  This is all for a revamp I'm doing of the unittests
2013-09-16 02:43:45 -05:00
Gary Kramlich 2ea0ab5c5f ignore CuraEngine binary 2013-09-16 02:02:38 -05:00
Gary Kramlich 20c9519427 Set default output for GCodeExport to be stdout 2013-09-16 02:02:02 -05:00
Gary Kramlich 85acee47b1 ran dos2unix on gcodeExport.h 2013-09-16 01:32:37 -05:00
Gary Kramlich 08ddccdf51 Removed some trailing whitespace in Makefile 2013-09-16 00:58:19 -05:00
Gary Kramlich 3e50a1c5fb added some wildchars to ignore 2013-09-16 00:57:38 -05:00
Gary Kramlich 7e8c7814ed Fixed a compiler warning in gcodeExport.h 2013-09-16 00:56:08 -05:00
Daid c47fb9f48f Fix MacOS compile bug. 2013-08-29 13:38:50 +02:00
daid ca8ab015db Bam! Round the boundary box in the infill generation down to a multiple of the lineSpacing to align the infill grid on eachother. 2013-08-28 20:09:10 +02:00
daid ff851e324c Change a bit in the support material, expand the support material and then substract an expanded model outline from it. 2013-08-26 16:05:00 +02:00
daid 54a79b1433 Fix typo 2013-08-26 13:56:20 +02:00
daid e2a2f69415 Another one for #18, even better. 2013-08-21 17:11:08 +02:00
daid 7a961d56cb Different, better, solution for #18. Added some more UltiGCode flavored things. 2013-08-21 17:10:00 +02:00
daid 009b23d1e3 Fix the minimal skirt distance hangup if you do not have a skirt. 2013-08-09 14:51:04 +02:00
daid 7c10a490e0 Update the version number to 13.07 2013-08-08 15:18:14 +02:00
daid 0d7b413d8e Fix for no support material and lots of infill in support material. 2013-08-08 12:39:53 +02:00
daid 31fb704c4c Update on Cura support material, use new area based support material. 2013-08-08 10:48:23 +02:00
daid bd1c7fd463 Add minimal skirt length to ensure extruder is primed. 2013-08-06 14:02:09 +02:00
daid 3681858d8d Fixed #14 2013-08-06 10:44:48 +02:00
daid b3a796ce26 Start in the retracted state, so the first thing added at the skirt is a G1 E0 F{retract_speed} 2013-08-06 10:36:20 +02:00
daid 7c775490d9 Add support for using different extruders for support material. 2013-08-06 09:47:44 +02:00
daid 38f244ba7d Add a minimal extrusion amount before allowing retraction to avoid flattening the filament. 2013-08-06 09:15:03 +02:00
daid cbe6bcf747 Add option to always retract instead of combing 2013-08-06 09:04:10 +02:00
daid 14f28328f9 Added minimal travel distance for retraction. 2013-08-05 20:49:16 +02:00
daid fda9dd9b4c Update speedFactor name to extrudeSpeedFactor to better reflect what it does after patch #16 2013-08-05 16:36:47 +02:00
daid 7f2fa9af9a Merge pull request #16 from CapnBry/master
Don't slow travel moves when enforcing minimal layer time
2013-08-05 07:34:14 -07:00
Bryan Mayland 50a01f1667 Don't slow travel moves when enforcing minimal layer time 2013-08-05 09:23:24 -04:00
daid e89106eacb Add UltiGCode flavor (reprap is still the default) 2013-07-26 20:53:55 +02:00
daid 74714ed2b5 Fix the problem where the head does not clear the final object on multi object print. Add gcode flavor for supporting multiple types of gcode in the future. Disable the fan between printing of multiple objects. 2013-07-26 15:01:41 +02:00
daid b9571c984f Fix https://github.com/daid/Cura/issues/496 2013-07-24 13:43:07 +02:00
daid 3d95856f6d Merge branch 'master' of github.com:Ultimaker/CuraEngine 2013-07-09 10:14:59 +02:00
daid 6f0848b538 Remove tiny extrusion lines on infill bits, so you do not get tiny blobs of extrusion due to rounding errors. Cura: #485 2013-07-09 10:10:35 +02:00
Daid 13316a61dc Compile static for linux for now. 2013-06-28 13:46:33 +02:00
daid 2eb079dfbf Change how the settings are read to be compatibile with the MAcOS shit compiler. 2013-06-27 14:12:29 +02:00
daid 21ba0c3da3 Fix 64bit numbers in the settings. 2013-06-26 15:53:31 +02:00
daid 68f5253bb9 13.06.4 2013-06-26 12:00:43 +02:00
daid d48cc09ab2 Add settings.cpp to Makefile. 2013-06-26 10:11:05 +02:00
daid cc44d86cb4 Put settings in their own file and make the setting loading code a bit cleaner. 2013-06-26 10:09:04 +02:00
daid 8125cd8558 Fix warnings. 2013-06-25 16:41:16 +02:00
daid 925f112863 Fix the python script that runs the tests. 2013-06-25 14:15:56 +02:00
daid 756e11445b Fix the python script that runs the tests. 2013-06-25 14:12:36 +02:00
daid 1b50b0e6d7 Add auto run test cases, just add STL files. 2013-06-25 14:00:49 +02:00
daid e343e3adae Add auto run test cases, just add STL files. 2013-06-25 13:55:09 +02:00
daid 338d049c5c Cleanup makefile, remove unused targets. 2013-06-25 13:28:44 +02:00
daid 64d7d5f82c Add some extra comments to explain how the combing code works. 2013-06-25 09:43:24 +02:00
daid 6f46d74797 Fix the comb "checkInside" function, this could fail if the Y position of a point had exactly the same coordinate as a comb Y position. 2013-06-25 09:35:07 +02:00
daid 7b0665cb2c Merge pull request #4 from hroncok/patch-2
Always use $(EXECUTABLE) instead of exact name of executable in Makefile
2013-06-24 06:09:11 -07:00
daid 717b5eb95d Merge pull request #3 from hroncok/patch-1
Update version so it corresponds with tag release version numbers
2013-06-24 06:08:31 -07:00
daid 236b9814f3 Do not crash if we do not have any layers to process. 2013-06-24 09:01:40 +02:00
daid df1d945339 Slightly change how a new path is started, this is less hacky and somehow solves a crash. 2013-06-24 08:50:34 +02:00
Miro Hrončok abbea47241 Always use $(EXECUTABLE) instead of exact name of executable 2013-06-23 15:50:29 +02:00
Miro Hrončok 43d545c528 Update version so it corresponds with tag release version numbers 2013-06-23 16:27:17 +03:00
daid 8a13a20cb1 Make sure we do not retract on a perimeter when there is no infill. 2013-06-21 15:04:59 +02:00
56 arquivos alterados com 4943 adições e 3690 exclusões
+4
Ver Arquivo
@@ -9,4 +9,8 @@
_bin
_obj
*.depend
*.o
.*.swp
*.gcode
CuraEngine
+17 -61
Ver Arquivo
@@ -1,95 +1,51 @@
#
# Makefile for Cura_SteamEngine
# Makefile for CuraEngine
#
# simplest working invocation to compile it
#g++ main.cpp modelFile/modelFile.cpp clipper/clipper.cpp -I. -o Cura_SteamEngine
#g++ main.cpp modelFile/modelFile.cpp clipper/clipper.cpp -I. -o CuraEngine
CC=g++
CFLAGS=-I. -c -Wall -O3 -fomit-frame-pointer
CXX ?= g++
CFLAGS += -I. -c -Wall -Wextra -O3 -fomit-frame-pointer
# also include debug symbols
#CFLAGS+=-ggdb
LDFLAGS=
SOURCES=main.cpp modelFile/modelFile.cpp clipper/clipper.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=CuraEngine
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
SOURCES += clipper/clipper.cpp modelFile/modelFile.cpp utils/gettime.cpp utils/logoutput.cpp
OBJECTS = $(SOURCES:.cpp=.o)
EXECUTABLE = ./CuraEngine
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
OPEN_HTML=firefox
LDFLAGS += --static
endif
ifeq ($(UNAME), Darwin)
OPEN_HTML=open
#For MacOS force to build
#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
ifeq ($(UNAME), MINGW32_NT-6.1)
#For windows make it large address aware, which allows the process to use more then 2GB of memory.
CFLAGS += -march=pentium4
LDFLAGS += -Wl,--large-address-aware
LDFLAGS += -Wl,--large-address-aware -lm
endif
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
$(CXX) $(LDFLAGS) $(OBJECTS) -o $@
.cpp.o:
$(CC) $(CFLAGS) $< -o $@
$(CXX) $(CFLAGS) $< -o $@
## gcode: will run several tests and output the gCode and loads the first 100,000 lines into an HTML5 preview tool
gcode: $(EXECUTABLE) httpServer
./$(EXECUTABLE) --html
rm -f html/gCodeViewer/output.gcode 2>/dev/null
ln -f output.gcode html/gCodeViewer/
$(OPEN_HTML) "http://localhost:8000/gCodeViewer/index.html?analyzeFile=output.gcode"
## layers: make 'layers' will output layer SVGs and loads it up in your browser.
layers: $(EXECUTABLE)
./$(EXECUTABLE) --html
$(OPEN_HTML) output.html
## tests: will run several tests and output the gCode
tests: $(EXECUTABLE) wolt testmodels klein
./Cura_SteamEngine --verbose -i ./testcase_models/woltBaseline.stl
./Cura_SteamEngine --verbose -i ./testcase_models/woltNotFlat.stl
./Cura_SteamEngine --verbose -i ./testcase_models/wolt_scaled200Perc.stl
./Cura_SteamEngine --verbose -i ./testcase_models/wolt_smoothingOn.stl
#find ./testcase_models/*.stl -exec ./$(EXECUTABLE) {} \|\| echo ====== FAILED EXIT CODE $? ====== \;
rm html/gCodeViewer/output.gcode 2>/dev/null || echo
head -n 100000 output.gcode > html/gCodeViewer/output.gcode
## robot: will slice the robot
robot: testmodels
./Cura_SteamEngine --verbose -i ./testcase_models/ultirobot_Martijn.stl
## robot: will slice the klein bottle
klein: testmodels
./Cura_SteamEngine --verbose -i ./testcase_models/kleinbottle_Dizingof.stl
wolt:
./Cura_SteamEngine --verbose -i ./testcase_models/wolt.stl
testmodels:
@ls testcase_models/ultirobot_Martijn.stl 2>/dev/null || \
( echo Fetching ultirobot_Martijn.stl... && \
wget -O testcase_models/ultirobot_Martijn.stl "http://www.thingiverse.com/download:36866" )
@ls testcase_models/kleinbottle_Dizingof.stl 2>/dev/null || \
( echo Fetching klein bottle stl file... && \
wget -O testcase_models/kleinbottle_Dizingof.stl http://www.thingiverse.com/download:69728 )
tests: $(EXECUTABLE)
python run_tests.py $(EXECUTABLE)
## clean stuff
clean:
rm -f $(EXECUTABLE) $(OBJECTS) output.html
clean_all: clean
rm -f ./testcase_models/ultirobot_Martijn.stl kleinbottle_Dizingof.stl
httpServer:
echo Starting simple webserver for online gCodeViewer...
ps aux |grep SimpleHTTP 1>/dev/null || python -m SimpleHTTPServer 2>&1 1>/dev/null
rm -f $(EXECUTABLE) $(OBJECTS)
help:
@cat Makefile |grep \#\#| grep \: |cut -d\# -f3
+56
Ver Arquivo
@@ -0,0 +1,56 @@
/** 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 (!ClipperLib::Orientation(islands[n]))
continue;
double area = fabs(ClipperLib::Area(islands[n]));
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 = centerOfMass(islands[idx1]);
Point center2 = centerOfMass(islands[idx2]);
double angle = atan2(center2.X - center1.X, center2.Y - center1.Y) / M_PI * 180;
if (angle < 0) angle += 360;
return angle;
}
+3 -53
Ver Arquivo
@@ -2,58 +2,8 @@
#ifndef BRIDGE_H
#define BRIDGE_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.
ClipperLib::Clipper bridgeClip;
bridgeClip.AddPolygon(part->outline[0], ClipperLib::ptSubject);
for(unsigned int n=0; n<prevLayer->parts.size(); n++)
{
if (!part->boundaryBox.hit(prevLayer->parts[n].boundaryBox)) continue;
bridgeClip.AddPolygon(prevLayer->parts[n].outline[0], ClipperLib::ptClip);
}
Polygons islands;
bridgeClip.Execute(ClipperLib::ctIntersection, islands);
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++)
{
double area = fabs(Area(islands[n]));
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 = centerOfMass(islands[idx1]);
Point center2 = centerOfMass(islands[idx2]);
double angle = atan2(center2.X - center1.X, center2.Y - center1.Y) / M_PI * 180;
if (angle < 0) angle += 360;
return angle;
}
#include "sliceDataStorage.h"
int bridgeAngle(SliceLayerPart* part, SliceLayer* prevLayer);
#endif//BRIDGE_H
+20 -23
Ver Arquivo
@@ -1,29 +1,26 @@
The Clipper code library, the "Software" (that includes Delphi, C++ & C#
source code, accompanying samples and documentation), has been released
under the following license, terms and conditions:
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 and accompanying documentation covered by
this license (the "Software") 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:
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 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.
+53
Ver Arquivo
@@ -2,6 +2,59 @@
Clipper Change Log
============================================================
v5.1.6 (23 May 2014)
* 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 2014)
* 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.
+1114 -944
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+80 -39
Ver Arquivo
@@ -1,10 +1,10 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 4.9.7 *
* Date : 29 November 2012 *
* Version : 5.1.6 *
* Date : 23 May 2013 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2012 *
* Copyright : Angus Johnson 2010-2013 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
@@ -26,7 +26,7 @@
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 2428, 2005 , Long Beach, California, USA *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
@@ -64,31 +64,71 @@ public:
typedef std::vector< IntPoint > Polygon;
typedef std::vector< Polygon > Polygons;
std::ostream& operator <<(std::ostream &s, Polygon &p);
std::ostream& operator <<(std::ostream &s, Polygons &p);
struct ExPolygon {
Polygon outer;
Polygons holes;
};
typedef std::vector< ExPolygon > ExPolygons;
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
class PolyNode
{
public:
PolyNode();
Polygon Contour;
PolyNodes Childs;
PolyNode* Parent;
PolyNode* GetNext() const;
bool IsHole() const;
int ChildCount() const;
private:
PolyNode* GetNextSiblingUp() const;
unsigned Index; //node index in Parent.Childs
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};
enum JoinType { jtSquare, jtRound, jtMiter };
enum EndType { etClosed, etButt, etSquare, etRound};
bool Orientation(const Polygon &poly);
double Area(const Polygon &poly);
void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
double delta, JoinType jointype = jtSquare, double MiterLimit = 2, bool CheckInputs = true);
double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true);
void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines,
double delta, JoinType jointype = jtSquare, EndType endtype = etSquare, double limit = 0, bool autoFix = true);
void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance = 1.415);
void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance = 1.415);
void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons);
void ReversePolygon(Polygon& p);
void ReversePolygons(Polygons& p);
//used internally ...
enum EdgeSide { esNeither = 0, esLeft = 1, esRight = 2, esBoth = 3 };
enum EdgeSide { esLeft = 1, esRight = 2};
enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
//inline IntersectProtects operator|(IntersectProtects a, IntersectProtects b)
//{return static_cast<IntersectProtects>(static_cast<int>(a) | static_cast<int>(b));}
struct TEdge {
long64 xbot;
@@ -100,7 +140,6 @@ struct TEdge {
double dx;
long64 deltaX;
long64 deltaY;
long64 tmpX;
PolyType polyType;
EdgeSide side;
int windDelta; //1 or -1 depending on winding direction
@@ -140,12 +179,10 @@ struct OutPt; //forward declaration
struct OutRec {
int idx;
bool isHole;
OutRec *FirstLeft;
OutRec *AppendLink;
OutRec *FirstLeft; //see comments in clipper.pas
PolyNode *polyNode;
OutPt *pts;
OutPt *bottomPt;
OutPt *bottomFlag;
EdgeSide sides;
};
struct OutPt {
@@ -206,19 +243,21 @@ public:
Clipper();
~Clipper();
bool Execute(ClipType clipType,
Polygons &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
Polygons &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType,
ExPolygons &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
PolyTree &polytree,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
void Clear();
bool ReverseSolution() {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool ForceSimple() {return m_ForceSimple;};
void ForceSimple(bool value) {m_ForceSimple = value;};
protected:
void Reset();
virtual bool ExecuteInternal(bool fixHoleLinkages);
virtual bool ExecuteInternal();
private:
PolyOutList m_PolyOuts;
JoinList m_Joins;
@@ -227,11 +266,13 @@ private:
Scanbeam *m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
IntersectNode *m_IntersectNodes;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
IntersectNode *m_IntersectNodes;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
bool m_UsingPolyTree;
bool m_ForceSimple;
void DisposeScanbeamList();
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
@@ -254,37 +295,37 @@ private:
void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, const IntersectProtects protects);
OutRec* CreateOutRec();
void AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeBottomPt(OutRec &outRec);
void DisposeAllPolyPts();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const long64 botY, const long64 topY);
void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
void InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
void BuildIntersectList(const long64 botY, const long64 topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const long64 topY);
void BuildResult(Polygons& polys);
void BuildResultEx(ExPolygons& polys);
void SetHoleState(TEdge *e, OutRec *OutRec);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
void DisposeIntersectNodes();
bool FixupIntersections();
void FixupOutPolygon(OutRec &outRec);
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
bool IsHole(TEdge *e);
void FixHoleLinkage(OutRec *outRec);
void FixHoleLinkage(OutRec &outrec);
void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
void ClearJoins();
void AddHorzJoin(TEdge *e, int idx);
void ClearHorzJoins();
bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
void JoinCommonEdges(bool fixHoleLinkages);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
};
//------------------------------------------------------------------------------
+256
Ver Arquivo
@@ -0,0 +1,256 @@
/** 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, 1000));
Point off1 = crossZ(normal(p2 - p1, 1000));
Point n = normal(off0 + off1, 200);
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 = 10000LL * 10000LL;
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 < 10000LL * 10000LL)
{
*p = ret;
return true;
}
return false;
}
bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, 1500))
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] - 200, 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] + 200, 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;
}
+13 -242
Ver Arquivo
@@ -2,6 +2,8 @@
#ifndef COMB_H
#define COMB_H
#include "utils/polygon.h"
class Comb
{
private:
@@ -16,255 +18,24 @@ private:
Point sp;
Point ep;
bool preTest(Point startPoint, Point endPoint)
{
return collisionTest(startPoint, endPoint);
}
bool collisionTest(Point startPoint, Point endPoint)
{
Point diff = endPoint - startPoint;
bool preTest(Point startPoint, Point endPoint);
bool collisionTest(Point startPoint, Point endPoint);
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 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;
}
}
}
void calcMinMax();
unsigned int 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;
}
unsigned int getPolygonAbove(int64_t x);
Point 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, 1000));
Point off1 = crossZ(normal(p2 - p1, 1000));
Point n = normal(off0 + off1, 200);
return p1 + n;
}
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx);
bool checkInside(Point p)
{
//Check if we are inside the comb 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 moveInside(Point& p)
{
Point ret = p;
int64_t bestDist = 10000LL * 10000LL;
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, 100));
}
p0 = p1;
}
}
if (bestDist < 10000LL * 10000LL)
{
p = ret;
return true;
}
return false;
}
public:
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(Polygons& _boundery);
~Comb();
~Comb()
{
delete[] minX;
delete[] maxX;
delete[] minIdx;
delete[] maxIdx;
}
bool checkInside(Point p);
bool moveInside(Point* p, int distance = 100);
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, 1500))
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;
while(true)
{
unsigned int n = getPolygonAbove(x);
if (n == UINT_MAX) break;
pointList.push_back(matrix.unapply(Point(minX[n] - 200, 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] + 200, sp.Y)));
x = maxX[n];
}
pointList.push_back(endPoint);
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;
}
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints);
};
#endif//COMB_H
+524
Ver Arquivo
@@ -0,0 +1,524 @@
#ifndef FFF_PROCESSOR_H
#define FFF_PROCESSOR_H
//FusedFilamentFabrication processor.
class fffProcessor
{
private:
int maxObjectHeight;
int fileNr;
GCodeExport gcode;
ConfigSettings& config;
TimeKeeper timeKeeper;
GCodePathConfig skirtConfig;
GCodePathConfig inset0Config;
GCodePathConfig inset1Config;
GCodePathConfig fillConfig;
GCodePathConfig supportConfig;
public:
fffProcessor(ConfigSettings& config)
: config(config)
{
fileNr = 1;
maxObjectHeight = 0;
}
bool setTargetFile(const char* filename)
{
gcode.setFilename(filename);
if (gcode.isValid())
gcode.addComment("Generated with Cura_SteamEngine %s", VERSION);
return gcode.isValid();
}
bool processFile(const char* input_filename)
{
if (!gcode.isValid())
return false;
TimeKeeper timeKeeperTotal;
SliceDataStorage storage;
preSetup();
if (!prepareModel(storage, input_filename))
return false;
processSliceData(storage);
writeGCode(storage);
logProgress("process", 1, 1);
log("Total time elapsed %5.2fs.\n", timeKeeperTotal.restart());
return true;
}
void finalize()
{
if (!gcode.isValid())
return;
gcode.addFanCommand(0);
gcode.addRetraction();
gcode.setZ(maxObjectHeight + 5000);
gcode.addMove(gcode.getPositionXY(), config.moveSpeed, 0);
gcode.addCode(config.endCode);
log("Print time: %d\n", int(gcode.getTotalPrintTime()));
log("Filament: %d\n", int(gcode.getTotalFilamentUsed(0)));
log("Filament2: %d\n", int(gcode.getTotalFilamentUsed(1)));
if (gcode.getFlavor() == GCODE_FLAVOR_ULTIGCODE)
{
char numberString[16];
sprintf(numberString, "%d", int(gcode.getTotalPrintTime()));
gcode.replaceTagInStart("<__TIME__>", numberString);
sprintf(numberString, "%d", int(gcode.getTotalFilamentUsed(0)));
gcode.replaceTagInStart("<FILAMENT>", numberString);
sprintf(numberString, "%d", int(gcode.getTotalFilamentUsed(1)));
gcode.replaceTagInStart("<FILAMEN2>", numberString);
}
}
private:
void preSetup()
{
skirtConfig.setData(config.printSpeed, config.extrusionWidth, "SKIRT");
inset0Config.setData(config.printSpeed, config.extrusionWidth, "WALL-OUTER");
inset1Config.setData(config.printSpeed, config.extrusionWidth, "WALL-INNER");
fillConfig.setData(config.infillSpeed, config.extrusionWidth, "FILL");
supportConfig.setData(config.printSpeed, config.extrusionWidth, "SUPPORT");
for(unsigned int n=1; n<MAX_EXTRUDERS;n++)
gcode.setExtruderOffset(n, config.extruderOffset[n].p());
gcode.setFlavor(config.gcodeFlavor);
gcode.setRetractionSettings(config.retractionAmount, config.retractionSpeed, config.retractionAmountExtruderSwitch, config.minimalExtrusionBeforeRetraction);
}
bool prepareModel(SliceDataStorage& storage, const char* input_filename)
{
timeKeeper.restart();
log("Loading %s from disk...\n", input_filename);
SimpleModel* m = loadModel(input_filename, config.matrix);
if (!m)
{
log("Failed to load model: %s\n", input_filename);
return false;
}
log("Loaded from disk in %5.3fs\n", timeKeeper.restart());
log("Analyzing and optimizing model...\n");
OptimizedModel* om = new OptimizedModel(m, Point3(config.objectPosition.X, config.objectPosition.Y, -config.objectSink));
for(unsigned int v = 0; v < m->volumes.size(); v++)
{
log(" Face counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size(), (int)om->volumes[v].faces.size(), float(om->volumes[v].faces.size()) / float(m->volumes[v].faces.size()) * 100);
log(" Vertex counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size() * 3, (int)om->volumes[v].points.size(), float(om->volumes[v].points.size()) / float(m->volumes[v].faces.size() * 3) * 100);
}
delete m;
log("Optimize model %5.3fs \n", timeKeeper.restart());
//om->saveDebugSTL("c:\\models\\output.stl");
log("Slicing model...\n");
vector<Slicer*> slicerList;
for(unsigned int volumeIdx=0; volumeIdx < om->volumes.size(); volumeIdx++)
{
slicerList.push_back(new Slicer(&om->volumes[volumeIdx], config.initialLayerThickness / 2, config.layerThickness, config.fixHorrible & FIX_HORRIBLE_KEEP_NONE_CLOSED, config.fixHorrible & FIX_HORRIBLE_EXTENSIVE_STITCHING));
//slicerList[volumeIdx]->dumpSegmentsToHTML("C:\\models\\output.html");
}
log("Sliced model in %5.3fs\n", timeKeeper.restart());
fprintf(stdout,"Generating support map...\n");
generateSupportGrid(storage.support, om, config.supportAngle, config.supportEverywhere > 0, config.supportXYDistance, config.supportZDistance);
storage.modelSize = om->modelSize;
storage.modelMin = om->vMin;
storage.modelMax = om->vMax;
delete om;
log("Generating layer parts...\n");
for(unsigned int volumeIdx=0; volumeIdx < slicerList.size(); volumeIdx++)
{
storage.volumes.push_back(SliceVolumeStorage());
createLayerParts(storage.volumes[volumeIdx], slicerList[volumeIdx], config.fixHorrible & (FIX_HORRIBLE_UNION_ALL_TYPE_A | FIX_HORRIBLE_UNION_ALL_TYPE_B | FIX_HORRIBLE_UNION_ALL_TYPE_C));
delete slicerList[volumeIdx];
}
log("Generated layer parts in %5.3fs\n", timeKeeper.restart());
return true;
}
void processSliceData(SliceDataStorage& storage)
{
//carveMultipleVolumes(storage.volumes);
generateMultipleVolumesOverlap(storage.volumes, config.multiVolumeOverlap);
//dumpLayerparts(storage, "c:/models/output.html");
const unsigned int totalLayers = storage.volumes[0].layers.size();
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
int insetCount = config.insetCount;
if (config.spiralizeMode && int(layerNr) < config.downSkinCount && layerNr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
insetCount += 5;
generateInsets(&storage.volumes[volumeIdx].layers[layerNr], config.extrusionWidth, insetCount);
}
logProgress("inset",layerNr+1,totalLayers);
}
if (config.enableOozeShield && storage.volumes.size() > 1)
{
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
Polygons oozeShield;
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++)
{
oozeShield = oozeShield.unionPolygons(storage.volumes[volumeIdx].layers[layerNr].parts[partNr].outline.offset(2000));
}
}
storage.oozeShield.push_back(oozeShield);
}
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
storage.oozeShield[layerNr] = storage.oozeShield[layerNr].offset(-1000).offset(1000);
int offsetAngle = tan(60.0*M_PI/180) * config.layerThickness;//Allow for a 60deg angle in the oozeShield.
for(unsigned int layerNr=1; layerNr<totalLayers; layerNr++)
storage.oozeShield[layerNr] = storage.oozeShield[layerNr].unionPolygons(storage.oozeShield[layerNr-1].offset(-offsetAngle));
for(unsigned int layerNr=totalLayers-1; layerNr>0; layerNr--)
storage.oozeShield[layerNr-1] = storage.oozeShield[layerNr-1].unionPolygons(storage.oozeShield[layerNr].offset(-offsetAngle));
}
log("Generated inset in %5.3fs\n", timeKeeper.restart());
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
if (!config.spiralizeMode || int(layerNr) < config.downSkinCount) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
generateSkins(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount, config.infillOverlap);
generateSparse(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount);
}
}
logProgress("skin",layerNr+1,totalLayers);
}
log("Generated up/down skin in %5.3fs\n", timeKeeper.restart());
generateSkirt(storage, config.skirtDistance, config.extrusionWidth, config.skirtLineCount, config.skirtMinLength);
generateRaft(storage, config.raftMargin);
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++)
{
if (layerNr > 0)
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = bridgeAngle(&storage.volumes[volumeIdx].layers[layerNr].parts[partNr], &storage.volumes[volumeIdx].layers[layerNr-1]);
else
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = -1;
}
}
}
}
void writeGCode(SliceDataStorage& storage)
{
if (fileNr == 1)
{
if (gcode.getFlavor() == GCODE_FLAVOR_ULTIGCODE)
{
gcode.addCode(";FLAVOR:UltiGCode");
gcode.addCode(";TIME:<__TIME__>");
gcode.addCode(";MATERIAL:<FILAMENT>");
gcode.addCode(";MATERIAL2:<FILAMEN2>");
}
gcode.addCode(config.startCode);
}else{
gcode.addFanCommand(0);
gcode.resetExtrusionValue();
gcode.addRetraction();
gcode.setZ(maxObjectHeight + 5000);
gcode.addMove(Point(storage.modelMin.x, storage.modelMin.y), config.moveSpeed, 0);
}
fileNr++;
unsigned int totalLayers = storage.volumes[0].layers.size();
gcode.addComment("Layer count: %d", totalLayers);
if (config.raftBaseThickness > 0 && config.raftInterfaceThickness > 0)
{
GCodePathConfig raftBaseConfig(config.initialLayerSpeed, config.raftBaseLinewidth, "SUPPORT");
GCodePathConfig raftInterfaceConfig(config.initialLayerSpeed, config.raftInterfaceLinewidth, "SUPPORT");
{
gcode.addComment("LAYER:-2");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance);
gcode.setZ(config.raftBaseThickness);
gcode.setExtrusion(config.raftBaseThickness, config.filamentDiameter, config.filamentFlow);
gcodeLayer.addPolygonsByOptimizer(storage.raftOutline, &raftBaseConfig);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftBaseLinewidth, config.raftLineSpacing, config.infillOverlap, 0);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftBaseConfig);
gcodeLayer.writeGCode(false, config.raftBaseThickness);
}
{
gcode.addComment("LAYER:-1");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance);
gcode.setZ(config.raftBaseThickness + config.raftInterfaceThickness);
gcode.setExtrusion(config.raftInterfaceThickness, config.filamentDiameter, config.filamentFlow);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftInterfaceLinewidth, config.raftLineSpacing, config.infillOverlap, 90);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftInterfaceConfig);
gcodeLayer.writeGCode(false, config.raftInterfaceThickness);
}
}
if (config.enableWipeTower)
{
int wipeTowerSize = 10000;
ClipperLib::Polygon p;
p.push_back(Point(storage.modelMin.x - 3000, storage.modelMax.y + 3000));
p.push_back(Point(storage.modelMin.x - 3000, storage.modelMax.y + 3000 + wipeTowerSize));
p.push_back(Point(storage.modelMin.x - 3000 - wipeTowerSize, storage.modelMax.y + 3000 + wipeTowerSize));
p.push_back(Point(storage.modelMin.x - 3000 - wipeTowerSize, storage.modelMax.y + 3000));
storage.wipeTower.add(p);
storage.wipePoint = Point(storage.modelMin.x - 3000 - wipeTowerSize / 2, storage.modelMax.y + 3000 + wipeTowerSize / 2);
}
int volumeIdx = 0;
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
logProgress("export", layerNr+1, totalLayers);
gcode.addComment("LAYER:%d", layerNr);
if (layerNr == 0)
gcode.setExtrusion(config.initialLayerThickness, config.filamentDiameter, config.filamentFlow);
else
gcode.setExtrusion(config.layerThickness, config.filamentDiameter, config.filamentFlow);
GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance);
int32_t z = config.initialLayerThickness + layerNr * config.layerThickness;
z += config.raftBaseThickness + config.raftInterfaceThickness;
gcode.setZ(z);
bool printSupportFirst = (storage.support.generated && config.supportExtruder > 0 && config.supportExtruder == gcodeLayer.getExtruder());
if (printSupportFirst)
addSupportToGCode(storage, gcodeLayer, layerNr);
for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++)
{
if (volumeCnt > 0)
volumeIdx = (volumeIdx + 1) % storage.volumes.size();
addVolumeLayerToGCode(storage, gcodeLayer, volumeIdx, layerNr);
}
if (!printSupportFirst)
addSupportToGCode(storage, gcodeLayer, layerNr);
//Finish the layer by applying speed corrections for minimal layer times and slowdown for the initial layer.
if (int(layerNr) < config.initialSpeedupLayers)
{
int n = config.initialSpeedupLayers;
int layer0Factor = config.initialLayerSpeed * 100 / config.printSpeed;
gcodeLayer.setExtrudeSpeedFactor((layer0Factor * (n - layerNr) + 100 * (layerNr)) / n);
if (layerNr == 0)//On the first layer, also slow down the travel
gcodeLayer.setTravelSpeedFactor(layer0Factor);
}
gcodeLayer.forceMinimalLayerTime(config.minimalLayerTime, config.minimalFeedrate);
int fanSpeed = config.fanSpeedMin;
if (gcodeLayer.getExtrudeSpeedFactor() <= 50)
{
fanSpeed = config.fanSpeedMax;
}else{
int n = gcodeLayer.getExtrudeSpeedFactor() - 50;
fanSpeed = config.fanSpeedMin * n / 50 + config.fanSpeedMax * (50 - n) / 50;
}
if (int(layerNr) < config.fanFullOnLayerNr)
{
//Slow down the fan on the layers below the [fanFullOnLayerNr], where layer 0 is speed 0.
fanSpeed = fanSpeed * layerNr / config.fanFullOnLayerNr;
}
gcode.addFanCommand(fanSpeed);
gcodeLayer.writeGCode(config.coolHeadLift > 0, int(layerNr) > 0 ? config.layerThickness : config.initialLayerThickness);
}
/* support debug
for(int32_t y=0; y<storage.support.gridHeight; y++)
{
for(int32_t x=0; x<storage.support.gridWidth; x++)
{
unsigned int n = x+y*storage.support.gridWidth;
if (storage.support.grid[n].size() < 1) continue;
int32_t z = storage.support.grid[n][0].z;
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, z), z);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
}
}
//*/
log("Wrote layers in %5.2fs.\n", timeKeeper.restart());
gcode.tellFileSize();
gcode.addFanCommand(0);
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
maxObjectHeight = std::max(maxObjectHeight, storage.modelSize.z);
}
//Add a single layer from a single mesh-volume to the GCode
void addVolumeLayerToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int volumeIdx, int layerNr)
{
int prevExtruder = gcodeLayer.getExtruder();
bool extruderChanged = gcodeLayer.setExtruder(volumeIdx);
if (layerNr == 0)
gcodeLayer.addPolygonsByOptimizer(storage.skirt, &skirtConfig);
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
if (extruderChanged)
addWipeTower(storage, gcodeLayer, layerNr, prevExtruder);
if (storage.oozeShield.size() > 0)
{
gcodeLayer.setAlwaysRetract(true);
gcodeLayer.addPolygonsByOptimizer(storage.oozeShield[layerNr], &skirtConfig);
gcodeLayer.setAlwaysRetract(!config.enableCombing);
}
PathOrderOptimizer partOrderOptimizer(gcode.getPositionXY());
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
partOrderOptimizer.addPolygon(layer->parts[partNr].insets[0][0]);
}
partOrderOptimizer.optimize();
for(unsigned int partCounter=0; partCounter<partOrderOptimizer.polyOrder.size(); partCounter++)
{
SliceLayerPart* part = &layer->parts[partOrderOptimizer.polyOrder[partCounter]];
if (config.enableCombing)
gcodeLayer.setCombBoundary(&part->combBoundery);
else
gcodeLayer.setAlwaysRetract(true);
if (config.insetCount > 0)
{
if (config.spiralizeMode)
{
if (int(layerNr) >= config.downSkinCount)
inset0Config.spiralize = true;
if (int(layerNr) == config.downSkinCount && part->insets.size() > 0)
gcodeLayer.addPolygonsByOptimizer(part->insets[0], &inset1Config);
}
for(int insetNr=part->insets.size()-1; insetNr>-1; insetNr--)
{
if (insetNr == 0)
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset0Config);
else
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset1Config);
}
}
Polygons fillPolygons;
int fillAngle = 45;
if (layerNr & 1)
fillAngle += 90;
//int sparseSteps[1] = {config.extrusionWidth};
//generateConcentricInfill(part->skinOutline, fillPolygons, sparseSteps, 1);
generateLineInfill(part->skinOutline, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, (part->bridgeAngle > -1) ? part->bridgeAngle : fillAngle);
//int sparseSteps[2] = {config.extrusionWidth*5, config.extrusionWidth * 0.8};
//generateConcentricInfill(part->sparseOutline, fillPolygons, sparseSteps, 2);
if (config.sparseInfillLineDistance > 0)
{
if (config.sparseInfillLineDistance > config.extrusionWidth * 4)
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45);
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45 + 90);
}
else
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance, config.infillOverlap, fillAngle);
}
}
gcodeLayer.addPolygonsByOptimizer(fillPolygons, &fillConfig);
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!config.spiralizeMode || int(layerNr) < config.downSkinCount)
gcodeLayer.moveInsideCombBoundary(config.extrusionWidth * 2);
}
gcodeLayer.setCombBoundary(NULL);
}
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layerNr)
{
if (!storage.support.generated)
return;
if (config.supportExtruder > -1)
{
int prevExtruder = gcodeLayer.getExtruder();
if (gcodeLayer.setExtruder(config.supportExtruder))
addWipeTower(storage, gcodeLayer, layerNr, prevExtruder);
if (storage.oozeShield.size() > 0)
{
gcodeLayer.setAlwaysRetract(true);
gcodeLayer.addPolygonsByOptimizer(storage.oozeShield[layerNr], &skirtConfig);
gcodeLayer.setAlwaysRetract(!config.enableCombing);
}
}
int32_t z = config.initialLayerThickness + layerNr * config.layerThickness;
z += config.raftBaseThickness + config.raftInterfaceThickness;
SupportPolyGenerator supportGenerator(storage.support, z);
for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++)
{
SliceLayer* layer = &storage.volumes[volumeCnt].layers[layerNr];
Polygons polys;
for(unsigned int n=0; n<layer->parts.size(); n++)
supportGenerator.polygons = supportGenerator.polygons.difference(layer->parts[n].outline.offset(config.supportXYDistance));
}
//Contract and expand the suppory polygons so small sections are removed and the final polygon is smoothed a bit.
supportGenerator.polygons = supportGenerator.polygons.offset(-config.extrusionWidth * 3);
supportGenerator.polygons = supportGenerator.polygons.offset(config.extrusionWidth * 3);
vector<Polygons> supportIslands = supportGenerator.polygons.splitIntoParts();
for(unsigned int n=0; n<supportIslands.size(); n++)
{
Polygons supportLines;
if (config.supportLineDistance > 0)
{
if (config.supportLineDistance > config.extrusionWidth * 4)
{
generateLineInfill(supportIslands[n], supportLines, config.extrusionWidth, config.supportLineDistance*2, config.infillOverlap, 0);
generateLineInfill(supportIslands[n], supportLines, config.extrusionWidth, config.supportLineDistance*2, config.infillOverlap, 90);
}else{
generateLineInfill(supportIslands[n], supportLines, config.extrusionWidth, config.supportLineDistance, config.infillOverlap, (layerNr & 1) ? 0 : 90);
}
}
gcodeLayer.addPolygonsByOptimizer(supportIslands[n], &supportConfig);
gcodeLayer.addPolygonsByOptimizer(supportLines, &supportConfig);
}
}
void addWipeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layerNr, int prevExtruder)
{
if (!config.enableWipeTower)
return;
//If we changed extruder, print the wipe/prime tower for this nozzle;
gcodeLayer.addPolygonsByOptimizer(storage.wipeTower, &supportConfig);
Polygons fillPolygons;
generateLineInfill(storage.wipeTower, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, 45 + 90 * (layerNr % 2));
gcodeLayer.addPolygonsByOptimizer(fillPolygons, &supportConfig);
//Make sure we wipe the old extruder on the wipe tower.
gcodeLayer.addTravel(storage.wipePoint - config.extruderOffset[prevExtruder].p() + config.extruderOffset[gcodeLayer.getExtruder()].p());
}
};
#endif//FFF_PROCESSOR_H
+557
Ver Arquivo
@@ -0,0 +1,557 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdarg.h>
#include "gcodeExport.h"
#include "pathOrderOptimizer.h"
#include "settings.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
GCodeExport::GCodeExport()
: currentPosition(0,0,0)
{
extrusionAmount = 0;
extrusionPerMM = 0;
retractionAmount = 4.5;
minimalExtrusionBeforeRetraction = 0.0;
extrusionAmountAtPreviousRetraction = -10000;
extruderSwitchRetraction = 14.5;
extruderNr = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
totalFilament[e] = 0.0;
currentSpeed = 0;
retractionSpeed = 45;
isRetracted = true;
memset(extruderOffset, 0, sizeof(extruderOffset));
f = stdout;
}
GCodeExport::~GCodeExport()
{
if (f)
fclose(f);
}
void GCodeExport::replaceTagInStart(const char* tag, const char* replaceValue)
{
off64_t oldPos = ftello64(f);
char buffer[1024];
fseeko64(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);
fwrite(buffer, 1024, 1, f);
fseeko64(f, oldPos, SEEK_SET);
}
void GCodeExport::setExtruderOffset(int id, Point p)
{
extruderOffset[id] = p;
}
void GCodeExport::setFlavor(int flavor)
{
this->flavor = flavor;
}
int GCodeExport::getFlavor()
{
return this->flavor;
}
void GCodeExport::setFilename(const char* filename)
{
f = fopen(filename, "w+");
}
bool GCodeExport::isValid()
{
return f != NULL;
}
void GCodeExport::setExtrusion(int layerThickness, int filamentDiameter, int flow)
{
double filamentArea = M_PI * (double(filamentDiameter) / 1000.0 / 2.0) * (double(filamentDiameter) / 1000.0 / 2.0);
if (flavor == GCODE_FLAVOR_ULTIGCODE)//UltiGCode uses volume extrusion as E value, and thus does not need the filamentArea in the mix.
extrusionPerMM = double(layerThickness) / 1000.0;
else
extrusionPerMM = double(layerThickness) / 1000.0 / filamentArea * double(flow) / 100.0;
}
void GCodeExport::setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction)
{
this->retractionAmount = double(retractionAmount) / 1000.0;
this->retractionSpeed = retractionSpeed;
this->extruderSwitchRetraction = double(extruderSwitchRetraction) / 1000.0;
this->minimalExtrusionBeforeRetraction = double(minimalExtrusionBeforeRetraction) / 1000.0;
}
void GCodeExport::setZ(int z)
{
this->zPos = z;
}
Point GCodeExport::getPositionXY()
{
return Point(currentPosition.x, currentPosition.y);
}
int GCodeExport::getPositionZ()
{
return currentPosition.z;
}
int GCodeExport::getExtruderNr()
{
return extruderNr;
}
double GCodeExport::getTotalFilamentUsed(int e)
{
if (e == extruderNr)
return totalFilament[e] + extrusionAmount;
return totalFilament[e];
}
double GCodeExport::getTotalPrintTime()
{
return totalPrintTime;
}
void GCodeExport::addComment(const char* comment, ...)
{
va_list args;
va_start(args, comment);
fprintf(f, ";");
vfprintf(f, comment, args);
fprintf(f, "\n");
va_end(args);
}
void GCodeExport::addLine(const char* line, ...)
{
va_list args;
va_start(args, line);
vfprintf(f, line, args);
fprintf(f, "\n");
va_end(args);
}
void GCodeExport::resetExtrusionValue()
{
if (extrusionAmount != 0.0)
{
fprintf(f, "G92 E0\n");
totalFilament[extruderNr] += extrusionAmount;
extrusionAmountAtPreviousRetraction -= extrusionAmount;
extrusionAmount = 0.0;
}
}
void GCodeExport::addDelay(double timeAmount)
{
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
}
void GCodeExport::addMove(Point p, int speed, int lineWidth)
{
if (lineWidth != 0)
{
Point diff = p - getPositionXY();
if (isRetracted)
{
if (flavor == GCODE_FLAVOR_ULTIGCODE)
{
fprintf(f, "G11\n");
}else{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount);
currentSpeed = retractionSpeed;
}
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 * double(lineWidth) / 1000.0 * vSizeMM(diff);
fprintf(f, "G1");
}else{
fprintf(f, "G0");
}
if (currentSpeed != speed)
{
fprintf(f, " F%i", speed * 60);
currentSpeed = speed;
}
fprintf(f, " X%0.2f Y%0.2f", float(p.X - extruderOffset[extruderNr].X)/1000, float(p.Y - extruderOffset[extruderNr].Y)/1000);
if (zPos != currentPosition.z)
fprintf(f, " Z%0.2f", float(zPos)/1000);
if (lineWidth != 0)
fprintf(f, " E%0.5lf", extrusionAmount);
fprintf(f, "\n");
currentPosition = Point3(p.X, p.Y, zPos);
}
void GCodeExport::addRetraction()
{
if (retractionAmount > 0 && !isRetracted && extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount)
{
if (flavor == GCODE_FLAVOR_ULTIGCODE)
{
fprintf(f, "G10\n");
}else{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount - retractionAmount);
currentSpeed = retractionSpeed;
}
extrusionAmountAtPreviousRetraction = extrusionAmount;
isRetracted = true;
}
}
void GCodeExport::switchExtruder(int newExtruder)
{
if (extruderNr == newExtruder)
return;
resetExtrusionValue();
extruderNr = newExtruder;
if (flavor == GCODE_FLAVOR_ULTIGCODE)
{
fprintf(f, "G10 S1\n");
}else{
fprintf(f, "G1 F%i E%0.4lf\n", retractionSpeed * 60, extrusionAmount - extruderSwitchRetraction);
currentSpeed = retractionSpeed;
}
isRetracted = true;
fprintf(f, "T%i\n", extruderNr);
}
void GCodeExport::addCode(const char* str)
{
fprintf(f, "%s\n", str);
}
void GCodeExport::addFanCommand(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 (flavor == GCODE_FLAVOR_MAKERBOT)
fprintf(f, "M127 T0\n");
else
fprintf(f, "M107\n");
}
currentFanSpeed = speed;
}
int GCodeExport::getFileSize(){
return ftell(f);
}
void GCodeExport::tellFileSize() {
float fsize = (float) ftell(f);
if(fsize > 1024*1024) {
fsize /= 1024.0*1024.0;
fprintf(stderr, "Wrote %5.1f MB.\n",fsize);
}
if(fsize > 1024) {
fsize /= 1024.0;
fprintf(stderr, "Wrote %5.1f kilobytes.\n",fsize);
}
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config)
{
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done)
return &paths[paths.size()-1];
paths.push_back(GCodePath());
GCodePath* ret = &paths[paths.size()-1];
ret->retract = false;
ret->config = config;
ret->extruder = currentExtruder;
ret->done = false;
return ret;
}
void GCodePlanner::forceNewPathStart()
{
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;
travelSpeedFactor = 100;
extraTime = 0.0;
totalPrintTime = 0.0;
forceRetraction = false;
alwaysRetract = false;
currentExtruder = gcode.getExtruderNr();
this->retractionMinimalDistance = retractionMinimalDistance;
}
GCodePlanner::~GCodePlanner()
{
if (comb)
delete comb;
}
void GCodePlanner::addTravel(Point p)
{
GCodePath* path = getLatestPathWithConfig(&travelConfig);
if (forceRetraction)
{
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
{
path->retract = true;
}
forceRetraction = false;
}else if (comb != NULL)
{
vector<Point> pointList;
if (comb->calc(lastPosition, p, pointList))
{
for(unsigned int n=0; n<pointList.size(); n++)
{
path->points.push_back(pointList[n]);
}
}else{
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
path->retract = true;
}
}else if (alwaysRetract)
{
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
path->retract = true;
}
path->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config)
{
getLatestPathWithConfig(config)->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::moveInsideCombBoundary(int distance)
{
if (!comb || comb->checkInside(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);
addTravel(p);
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
forceNewPathStart();
}
}
void GCodePlanner::addPolygon(ClipperLib::Polygon& polygon, int startIdx, GCodePathConfig* config)
{
Point p0 = polygon[startIdx];
addTravel(p0);
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config);
p0 = p1;
}
if (polygon.size() > 2)
addExtrusionMove(polygon[startIdx], config);
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config)
{
PathOrderOptimizer orderOptimizer(lastPosition);
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
{
int nr = orderOptimizer.polyOrder[i];
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
}
}
void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
{
Point p0 = gcode.getPositionXY();
double travelTime = 0.0;
double extrudeTime = 0.0;
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
for(unsigned int i=0; i<path->points.size(); i++)
{
double thisTime = vSizeMM(p0 - path->points[i]) / double(path->config->speed);
if (path->config->lineWidth != 0)
extrudeTime += thisTime;
else
travelTime += thisTime;
p0 = path->points[i];
}
}
double totalTime = extrudeTime + travelTime;
if (totalTime < minTime && extrudeTime > 0.0)
{
double minExtrudeTime = minTime - travelTime;
if (minExtrudeTime < 1)
minExtrudeTime = 1;
double factor = extrudeTime / minExtrudeTime;
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
if (path->config->lineWidth == 0)
continue;
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.
if (factor * 100 < getExtrudeSpeedFactor())
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;
}
}
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
{
GCodePathConfig* lastConfig = NULL;
int extruder = gcode.getExtruderNr();
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
if (extruder != path->extruder)
{
extruder = path->extruder;
gcode.switchExtruder(extruder);
}else if (path->retract)
{
gcode.addRetraction();
}
if (path->config != &travelConfig && lastConfig != path->config)
{
gcode.addComment("TYPE:%s", path->config->name);
lastConfig = path->config;
}
int speed = path->config->speed;
if (path->config->lineWidth != 0)// Only apply the extrudeSpeedFactor to extrusion moves
speed = speed * extrudeSpeedFactor / 100;
else
speed = speed * travelSpeedFactor / 100;
if (path->points.size() == 1 && path->config != &travelConfig && shorterThen(gcode.getPositionXY() - path->points[0], path->config->lineWidth * 2))
{
//Check for lots of small moves and combine them into one large line
Point p0 = path->points[0];
unsigned int i = n + 1;
while(i < paths.size() && paths[i].points.size() == 1 && shorterThen(p0 - paths[i].points[0], path->config->lineWidth * 2))
{
p0 = paths[i].points[0];
i ++;
}
if (paths[i-1].config == &travelConfig)
i --;
if (i > n + 2)
{
p0 = gcode.getPositionXY();
for(unsigned int x=n; x<i-1; x+=2)
{
int64_t oldLen = vSize(p0 - paths[x].points[0]);
Point newPoint = (paths[x].points[0] + paths[x+1].points[0]) / 2;
int64_t newLen = vSize(gcode.getPositionXY() - newPoint);
if (newLen > 0)
gcode.addMove(newPoint, speed, path->config->lineWidth * oldLen / newLen);
p0 = paths[x+1].points[0];
}
gcode.addMove(paths[i-1].points[0], speed, path->config->lineWidth);
n = i - 1;
continue;
}
}
if (path->config->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();
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.addMove(path->points[i], speed, path->config->lineWidth);
}
}else{
for(unsigned int i=0; i<path->points.size(); i++)
{
gcode.addMove(path->points[i], speed, path->config->lineWidth);
}
}
}
gcode.totalPrintTime += this->totalPrintTime;
if (liftHeadIfNeeded && extraTime > 0.0)
{
gcode.totalPrintTime += extraTime;
gcode.addComment("Small layer, adding delay of %f", extraTime);
gcode.addRetraction();
gcode.setZ(gcode.getPositionZ() + 3000);
gcode.addMove(gcode.getPositionXY(), travelConfig.speed, 0);
gcode.addMove(gcode.getPositionXY() - Point(-20000, 0), travelConfig.speed, 0);
gcode.addDelay(extraTime);
}
}
+198 -486
Ver Arquivo
@@ -1,497 +1,209 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef GCODEEXPORT_H
#define GCODEEXPORT_H
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef GCODEEXPORT_H
#define GCODEEXPORT_H
class GCodeExport
{
private:
FILE* f;
double extrusionAmount;
double extrusionPerMM;
double retractionAmount;
double extruderSwitchRetraction;
Point3 currentPosition;
Point extruderOffset[16];
int currentSpeed, retractionSpeed;
int zPos;
bool isRetracted;
int extruderNr;
int currentFanSpeed;
double totalFilament;
public:
double totalPrintTime;
GCodeExport()
: currentPosition(0,0,0)
{
extrusionAmount = 0;
extrusionPerMM = 0;
retractionAmount = 4.5;
extruderSwitchRetraction = 14.5;
extruderNr = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
totalFilament = 0.0;
currentSpeed = 0;
retractionSpeed = 45;
isRetracted = false;
memset(extruderOffset, 0, sizeof(extruderOffset));
f = NULL;
}
~GCodeExport()
{
if (f)
fclose(f);
}
void setExtruderOffset(int id, Point p)
{
extruderOffset[id] = p;
}
void setFilename(const char* filename)
{
f = fopen(filename, "w");
}
bool isValid()
{
return f != NULL;
}
void setExtrusion(int layerThickness, int filamentDiameter, int flow)
{
double filamentArea = M_PI * (double(filamentDiameter) / 1000.0 / 2.0) * (double(filamentDiameter) / 1000.0 / 2.0);
extrusionPerMM = double(layerThickness) / 1000.0 / filamentArea * double(flow) / 100.0;
}
void setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction)
{
this->retractionAmount = double(retractionAmount) / 1000.0;
this->retractionSpeed = retractionSpeed;
this->extruderSwitchRetraction = double(extruderSwitchRetraction) / 1000.0;
}
void setZ(int z)
{
this->zPos = z;
}
Point getPositionXY()
{
return Point(currentPosition.x, currentPosition.y);
}
int getPositionZ()
{
return currentPosition.z;
}
#include <stdio.h>
int getExtruderNr()
{
return extruderNr;
}
double getTotalFilamentUsed()
{
return totalFilament + extrusionAmount;
}
double getTotalPrintTime()
{
return totalPrintTime;
}
void addComment(const char* comment, ...)
{
va_list args;
va_start(args, comment);
fprintf(f, ";");
vfprintf(f, comment, args);
fprintf(f, "\n");
va_end(args);
}
void addLine(const char* line, ...)
{
va_list args;
va_start(args, line);
vfprintf(f, line, args);
fprintf(f, "\n");
va_end(args);
}
void resetExtrusionValue()
{
if (extrusionAmount != 0.0)
{
fprintf(f, "G92 E0\n");
totalFilament += extrusionAmount;
extrusionAmount = 0.0;
}
}
void addDelay(double timeAmount)
{
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
}
void addMove(Point p, int speed, int lineWidth)
{
if (lineWidth != 0)
{
Point diff = p - getPositionXY();
if (isRetracted)
{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount);
currentSpeed = retractionSpeed;
isRetracted = false;
//}else if (shorterThen(diff, 50)){
// return;
}
extrusionAmount += extrusionPerMM * double(lineWidth) / 1000.0 * vSizeMM(diff);
fprintf(f, "G1");
}else{
fprintf(f, "G0");
}
if (currentSpeed != speed)
{
fprintf(f, " F%i", speed * 60);
currentSpeed = speed;
}
fprintf(f, " X%0.2f Y%0.2f", float(p.X - extruderOffset[extruderNr].X)/1000, float(p.Y - extruderOffset[extruderNr].Y)/1000);
if (zPos != currentPosition.z)
fprintf(f, " Z%0.2f", float(zPos)/1000);
if (lineWidth != 0)
fprintf(f, " E%0.5lf", extrusionAmount);
fprintf(f, "\n");
currentPosition = Point3(p.X, p.Y, zPos);
}
void addRetraction()
{
if (retractionAmount > 0 && !isRetracted)
{
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount - retractionAmount);
currentSpeed = retractionSpeed;
isRetracted = true;
}
}
void switchExtruder(int newExtruder)
{
if (extruderNr == newExtruder)
return;
extruderNr = newExtruder;
fprintf(f, "G1 F%i E%0.4lf\n", retractionSpeed * 60, extrusionAmount - extruderSwitchRetraction);
currentSpeed = retractionSpeed;
isRetracted = true;
fprintf(f, "T%i\n", extruderNr);
}
void addCode(const char* str)
{
fprintf(f, "%s\n", str);
}
void addFanCommand(int speed)
{
if (currentFanSpeed == speed)
return;
if (speed > 0)
fprintf(f, "M106 S%d\n", speed * 255 / 100);
else
fprintf(f, "M107\n");
currentFanSpeed = speed;
}
int getFileSize(){
return ftell(f);
}
void tellFileSize() {
float fsize = (float) ftell(f);
if(fsize > 1024*1024) {
fsize /= 1024.0*1024.0;
fprintf(stdout, "Wrote %5.1f MB.\n",fsize);
}
if(fsize > 1024) {
fsize /= 1024.0;
fprintf(stdout, "Wrote %5.1f kilobytes.\n",fsize);
}
}
};
class GCodePathConfig
{
public:
int speed;
int lineWidth;
#include "settings.h"
#include "comb.h"
#include "utils/intpoint.h"
#include "utils/polygon.h"
class GCodeExport
{
private:
FILE* f;
double extrusionAmount;
double extrusionPerMM;
double retractionAmount;
double extruderSwitchRetraction;
double minimalExtrusionBeforeRetraction;
double extrusionAmountAtPreviousRetraction;
Point3 currentPosition;
Point extruderOffset[MAX_EXTRUDERS];
int currentSpeed, retractionSpeed;
int zPos;
bool isRetracted;
int extruderNr;
int currentFanSpeed;
int flavor;
double totalFilament[MAX_EXTRUDERS];
public:
double totalPrintTime;
GCodeExport();
~GCodeExport();
void replaceTagInStart(const char* tag, const char* replaceValue);
void setExtruderOffset(int id, Point p);
void setFlavor(int flavor);
int getFlavor();
void setFilename(const char* filename);
bool isValid();
void setExtrusion(int layerThickness, int filamentDiameter, int flow);
void setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction);
void setZ(int z);
Point getPositionXY();
int getPositionZ();
int getExtruderNr();
double getTotalFilamentUsed(int e);
double getTotalPrintTime();
void addComment(const char* comment, ...);
void addLine(const char* line, ...);
void resetExtrusionValue();
void addDelay(double timeAmount);
void addMove(Point p, int speed, int lineWidth);
void addRetraction();
void switchExtruder(int newExtruder);
void addCode(const char* str);
void addFanCommand(int speed);
int getFileSize();
void tellFileSize();
};
class GCodePathConfig
{
public:
int speed;
int lineWidth;
const char* name;
bool spiralize;
GCodePathConfig(int speed, int lineWidth, const char* name)
: speed(speed), lineWidth(lineWidth), name(name)
GCodePathConfig() : speed(0), lineWidth(0), name(NULL), 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)
{
}
};
class GCodePath
{
public:
GCodePathConfig* config;
bool retract;
int extruder;
this->speed = speed;
this->lineWidth = lineWidth;
this->name = name;
}
};
class GCodePath
{
public:
GCodePathConfig* config;
bool retract;
int extruder;
vector<Point> points;
};
class GCodePlanner
{
private:
GCodeExport& gcode;
Point lastPosition;
vector<GCodePath> paths;
Comb* comb;
GCodePathConfig moveConfig;
int speedFactor;
int currentExtruder;
bool forceRetraction;
double extraTime;
double totalPrintTime;
private:
GCodePath* getLatestPathWithConfig(GCodePathConfig* config)
{
if (paths.size() > 0 && paths[paths.size()-1].config == config)
{
return &paths[paths.size()-1];
}
paths.push_back(GCodePath());
GCodePath* ret = &paths[paths.size()-1];
ret->retract = false;
ret->config = config;
ret->extruder = currentExtruder;
return ret;
}
public:
GCodePlanner(GCodeExport& gcode, int moveSpeed)
: gcode(gcode), moveConfig(moveSpeed, 0, "move")
{
lastPosition = gcode.getPositionXY();
comb = NULL;
speedFactor = 100;
extraTime = 0.0;
totalPrintTime = 0.0;
forceRetraction = false;
currentExtruder = gcode.getExtruderNr();
}
~GCodePlanner()
{
if (comb)
delete comb;
}
void setExtruder(int extruder)
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.
};
class GCodePlanner
{
private:
GCodeExport& gcode;
Point lastPosition;
vector<GCodePath> paths;
Comb* comb;
GCodePathConfig travelConfig;
int extrudeSpeedFactor;
int travelSpeedFactor;
int currentExtruder;
int retractionMinimalDistance;
bool forceRetraction;
bool alwaysRetract;
double extraTime;
double totalPrintTime;
private:
GCodePath* getLatestPathWithConfig(GCodePathConfig* config);
void forceNewPathStart();
public:
GCodePlanner(GCodeExport& gcode, int travelSpeed, int retractionMinimalDistance);
~GCodePlanner();
bool setExtruder(int extruder)
{
if (extruder == currentExtruder)
return false;
currentExtruder = extruder;
}
void setCombBoundary(Polygons* polygons)
{
if (comb)
delete comb;
if (polygons)
comb = new Comb(*polygons);
else
comb = NULL;
return true;
}
void forceRetract()
int getExtruder()
{
forceRetraction = true;
}
void setSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->speedFactor = speedFactor;
}
int getSpeedFactor()
{
return this->speedFactor;
}
void addMove(Point p)
{
GCodePath* path = getLatestPathWithConfig(&moveConfig);
if (forceRetraction)
{
if (!shorterThen(lastPosition - p, 1500))
path->retract = true;
forceRetraction = false;
}else if (comb != NULL)
{
vector<Point> pointList;
if (comb->calc(lastPosition, p, pointList))
{
for(unsigned int n=0; n<pointList.size(); n++)
{
path->points.push_back(pointList[n]);
}
}else{
path->retract = true;
}
}
path->points.push_back(p);
lastPosition = p;
}
void addExtrusionMove(Point p, GCodePathConfig* config)
{
getLatestPathWithConfig(config)->points.push_back(p);
lastPosition = p;
}
void addPolygon(ClipperLib::Polygon& polygon, int startIdx, GCodePathConfig* config)
{
Point p0 = polygon[startIdx];
addMove(p0);
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config);
p0 = p1;
}
if (polygon.size() > 2)
addExtrusionMove(polygon[startIdx], config);
}
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config)
{
PathOptimizer orderOptimizer(lastPosition);
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
{
int nr = orderOptimizer.polyOrder[i];
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
}
}
void forceMinimalLayerTime(double minTime, int minimalSpeed)
{
Point p0 = gcode.getPositionXY();
double totalTime = 0.0;
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
for(unsigned int i=0; i<path->points.size(); i++)
{
totalTime += vSizeMM(p0 - path->points[i]) / double(path->config->speed);
p0 = path->points[i];
}
}
if (totalTime < minTime)
{
double factor = totalTime / minTime;
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
int speed = path->config->speed * factor;
if (speed < minimalSpeed)
factor = double(minimalSpeed) / double(path->config->speed);
}
setSpeedFactor(factor * 100);
if (minTime - (totalTime / factor) > 0.1)
{
//TODO: Use up this extra time (circle around the print?)
this->extraTime = minTime - (totalTime / factor);
}
this->totalPrintTime = totalTime * factor;
}else{
this->totalPrintTime = totalTime;
}
}
void writeGCode(bool liftHeadIfNeeded)
{
GCodePathConfig* lastConfig = NULL;
int extruder = gcode.getExtruderNr();
for(unsigned int n=0; n<paths.size(); n++)
{
GCodePath* path = &paths[n];
if (extruder != path->extruder)
{
extruder = path->extruder;
gcode.switchExtruder(extruder);
}else if (path->retract)
{
gcode.addRetraction();
}
if (path->config != &moveConfig && lastConfig != path->config)
{
gcode.addComment("TYPE:%s", path->config->name);
lastConfig = path->config;
}
int speed = path->config->speed * speedFactor / 100;
if (path->points.size() == 1 && path->config != &moveConfig && shorterThen(gcode.getPositionXY() - path->points[0], path->config->lineWidth * 2))
{
//Check for lots of small moves and combine them into one large line
Point p0 = path->points[0];
unsigned int i = n + 1;
while(i < paths.size() && paths[i].points.size() == 1 && shorterThen(p0 - paths[i].points[0], path->config->lineWidth * 2))
{
p0 = paths[i].points[0];
i ++;
}
if (paths[i-1].config == &moveConfig)
i --;
if (i > n + 2)
{
p0 = gcode.getPositionXY();
for(unsigned int x=n; x<i-1; x+=2)
{
int64_t oldLen = vSize(p0 - paths[x].points[0]);
Point newPoint = (paths[x].points[0] + paths[x+1].points[0]) / 2;
int64_t newLen = vSize(gcode.getPositionXY() - newPoint);
if (newLen > 0)
gcode.addMove(newPoint, speed, path->config->lineWidth * oldLen / newLen);
p0 = paths[x+1].points[0];
}
gcode.addMove(paths[i-1].points[0], speed, path->config->lineWidth);
n = i - 1;
continue;
}
}
for(unsigned int i=0; i<path->points.size(); i++)
{
gcode.addMove(path->points[i], speed, path->config->lineWidth);
}
}
gcode.totalPrintTime += this->totalPrintTime;
if (liftHeadIfNeeded && extraTime > 0.0)
{
gcode.totalPrintTime += extraTime;
gcode.addComment("Small layer, adding delay of %f", extraTime);
gcode.addRetraction();
gcode.setZ(gcode.getPositionZ() + 3000);
gcode.addMove(gcode.getPositionXY(), moveConfig.speed, 0);
gcode.addMove(gcode.getPositionXY() - Point(-20000, 0), moveConfig.speed, 0);
gcode.addDelay(extraTime);
}
}
};
#endif//GCODEEXPORT_H
return currentExtruder;
}
void setCombBoundary(Polygons* polygons)
{
if (comb)
delete comb;
if (polygons)
comb = new Comb(*polygons);
else
comb = NULL;
}
void setAlwaysRetract(bool alwaysRetract)
{
this->alwaysRetract = alwaysRetract;
}
void forceRetract()
{
forceRetraction = true;
}
void setExtrudeSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->extrudeSpeedFactor = speedFactor;
}
int getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void setTravelSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->travelSpeedFactor = speedFactor;
}
int getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void addTravel(Point p);
void addExtrusionMove(Point p, GCodePathConfig* config);
void moveInsideCombBoundary(int distance);
void addPolygon(ClipperLib::Polygon& polygon, int startIdx, GCodePathConfig* config);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config);
void forceMinimalLayerTime(double minTime, int minimalSpeed);
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
};
#endif//GCODEEXPORT_H
+80
Ver Arquivo
@@ -0,0 +1,80 @@
/** 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;
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;
ClipperLib::Polygon p;
p.push_back(matrix.unapply(Point(x, cutList[idx][i])));
p.push_back(matrix.unapply(Point(x, cutList[idx][i+1])));
result.add(p);
}
idx += 1;
}
}
+3 -70
Ver Arquivo
@@ -2,76 +2,9 @@
#ifndef INFILL_H
#define 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.push_back(outline[polygonNr]);
ClipperLib::OffsetPolygons(outline, outline, -offsets[step], ClipperLib::jtSquare, 2, false);
if (outline.size() < 1)
break;
step = (step + 1) % offsetsSize;
}
}
#include "utils/polygon.h"
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(Polygons outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
ClipperLib::OffsetPolygons(outline, outline, extrusionWidth * infillOverlap / 100, ClipperLib::jtSquare, 2, false);
PointMatrix matrix(rotation);
matrix.apply(outline);
AABB boundary(outline);
int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
vector<int64_t> cutList[lineCount];
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 / 2) continue;
ClipperLib::Polygon p;
p.push_back(matrix.unapply(Point(x, cutList[idx][i])));
p.push_back(matrix.unapply(Point(x, cutList[idx][i+1])));
result.push_back(p);
}
idx += 1;
}
}
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
+45
Ver Arquivo
@@ -0,0 +1,45 @@
/** 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;
}
}
}
+4 -39
Ver Arquivo
@@ -2,45 +2,10 @@
#ifndef INSET_H
#define INSET_H
void generateInsets(SliceLayerPart* part, int offset, int insetCount)
{
ClipperLib::OffsetPolygons(part->outline, part->combBoundery, -offset, ClipperLib::jtSquare, 2, false);
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
ClipperLib::OffsetPolygons(part->outline, part->insets[i], -offset * i - offset/2, ClipperLib::jtSquare, 2, false);
optimizePolygons(part->insets[i]);
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
#include "sliceDataStorage.h"
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;
}
}
}
void generateInsets(SliceLayerPart* part, int offset, int insetCount);
void generateInsets(SliceLayer* layer, int offset, int insetCount);
#endif//INSET_H
+90
Ver Arquivo
@@ -0,0 +1,90 @@
/** 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 (ClipperLib::Orientation(layer->polygonList[i]))
ClipperLib::ReversePolygon(layer->polygonList[i]);
}
}
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());
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);
}
+6 -78
Ver Arquivo
@@ -2,6 +2,9 @@
#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,
@@ -15,85 +18,10 @@ It's also the first step that stores the result in the "data storage" so all oth
*/
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType)
{
ClipperLib::Polygons polyList;
for(unsigned int i=0; i<layer->polygonList.size(); i++)
{
ClipperLib::Polygon p;
p.push_back(layer->polygonList[i][0]);
for(unsigned int j=1; j<layer->polygonList[i].size(); j++)
{
p.push_back(layer->polygonList[i][j]);
}
if ((unionAllType & 0x02) && ClipperLib::Orientation(p))
ClipperLib::ReversePolygon(p);
polyList.push_back(p);
}
ClipperLib::ExPolygons resultPolys;
ClipperLib::Clipper clipper;
clipper.AddPolygons(polyList, ClipperLib::ptSubject);
if (unionAllType)
clipper.Execute(ClipperLib::ctUnion, resultPolys, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolys);
for(unsigned int i=0; i<resultPolys.size(); i++)
{
storageLayer.parts.push_back(SliceLayerPart());
storageLayer.parts[i].outline.push_back(resultPolys[i].outer);
for(unsigned int j=0; j<resultPolys[i].holes.size(); j++)
{
storageLayer.parts[i].outline.push_back(resultPolys[i].holes[j]);
}
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType);
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType)
{
for(unsigned int layerNr = 0; layerNr < slicer->layers.size(); layerNr++)
{
storage.layers.push_back(SliceLayer());
createLayerWithParts(storage.layers[layerNr], &slicer->layers[layerNr], unionAllType);
//LayerPartsLayer(&slicer->layers[layerNr])
}
}
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int 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);
}
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
#endif//LAYERPART_H
+45 -446
Ver Arquivo
@@ -2,19 +2,20 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.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"
@@ -25,428 +26,12 @@
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOptimizer.h"
#include "pathOrderOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#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_KEEP_NONE_CLOSED 0x10
#define VERSION "1.0"
class Config
{
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 retractionAmount;
int retractionAmountExtruderSwitch;
int retractionSpeed;
int multiVolumeOverlap;
int initialSpeedupLayers;
int initialLayerSpeed;
int printSpeed;
int infillSpeed;
int moveSpeed;
int fanOnLayerNr;
//Support material
int supportAngle;
int supportEverywhere;
int supportLineWidth;
//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;
Point objectPosition;
int objectSink;
int fixHorrible;
Point extruderOffset[16];
const char* startCode;
const char* endCode;
};
int verbose_level;
int maxObjectHeight;
void processFile(const char* input_filename, Config& config, GCodeExport& gcode, bool firstFile)
{
for(unsigned int n=1; n<16;n++)
gcode.setExtruderOffset(n, config.extruderOffset[n]);
double t = getTime();
log("Loading %s from disk...\n", input_filename);
SimpleModel* m = loadModel(input_filename, config.matrix);
if (!m)
{
log("Failed to load model: %s\n", input_filename);
return;
}
log("Loaded from disk in %5.3fs\n", timeElapsed(t));
log("Analyzing and optimizing model...\n");
OptimizedModel* om = new OptimizedModel(m, Point3(config.objectPosition.X, config.objectPosition.Y, -config.objectSink));
for(unsigned int v = 0; v < m->volumes.size(); v++)
{
log(" Face counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size(), (int)om->volumes[v].faces.size(), float(om->volumes[v].faces.size()) / float(m->volumes[v].faces.size()) * 100);
log(" Vertex counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size() * 3, (int)om->volumes[v].points.size(), float(om->volumes[v].points.size()) / float(m->volumes[v].faces.size() * 3) * 100);
}
delete m;
log("Optimize model %5.3fs \n", timeElapsed(t));
//om->saveDebugSTL("c:\\models\\output.stl");
log("Slicing model...\n");
vector<Slicer*> slicerList;
for(unsigned int volumeIdx=0; volumeIdx < om->volumes.size(); volumeIdx++)
{
slicerList.push_back(new Slicer(&om->volumes[volumeIdx], config.initialLayerThickness / 2, config.layerThickness, config.fixHorrible & FIX_HORRIBLE_KEEP_NONE_CLOSED, config.fixHorrible & FIX_HORRIBLE_EXTENSIVE_STITCHING));
//slicerList[volumeIdx]->dumpSegments("C:\\models\\output.html");
}
log("Sliced model in %5.3fs\n", timeElapsed(t));
SliceDataStorage storage;
if (config.supportAngle > -1)
{
fprintf(stdout,"Generating support map...\n");
generateSupportGrid(storage.support, om, config.initialLayerThickness / 2, config.layerThickness);
}
storage.modelSize = om->modelSize;
storage.modelMin = om->vMin;
storage.modelMax = om->vMax;
delete om;
log("Generating layer parts...\n");
for(unsigned int volumeIdx=0; volumeIdx < slicerList.size(); volumeIdx++)
{
storage.volumes.push_back(SliceVolumeStorage());
createLayerParts(storage.volumes[volumeIdx], slicerList[volumeIdx], config.fixHorrible & (FIX_HORRIBLE_UNION_ALL_TYPE_A | FIX_HORRIBLE_UNION_ALL_TYPE_B));
delete slicerList[volumeIdx];
}
//carveMultipleVolumes(storage.volumes);
generateMultipleVolumesOverlap(storage.volumes, config.multiVolumeOverlap);
log("Generated layer parts in %5.3fs\n", timeElapsed(t));
//dumpLayerparts(storage, "c:/models/output.html");
const unsigned int totalLayers = storage.volumes[0].layers.size();
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
generateInsets(&storage.volumes[volumeIdx].layers[layerNr], config.extrusionWidth, config.insetCount);
}
logProgress("inset",layerNr+1,totalLayers);
}
log("Generated inset in %5.3fs\n", timeElapsed(t));
//dumpLayerparts(storage, "c:/models/output.html");
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
generateSkins(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount, config.infillOverlap);
generateSparse(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount);
}
logProgress("skin",layerNr+1,totalLayers);
}
log("Generated up/down skin in %5.3fs\n", timeElapsed(t));
generateSkirt(storage, config.skirtDistance, config.extrusionWidth, config.skirtLineCount);
generateRaft(storage, config.raftMargin);
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++)
{
if (layerNr > 0)
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = bridgeAngle(&storage.volumes[volumeIdx].layers[layerNr].parts[partNr], &storage.volumes[volumeIdx].layers[layerNr-1]);
else
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = -1;
}
}
}
gcode.setRetractionSettings(config.retractionAmount, config.retractionSpeed, config.retractionAmountExtruderSwitch);
if (firstFile)
{
gcode.addCode(config.startCode);
}else{
gcode.resetExtrusionValue();
gcode.addRetraction();
gcode.setZ(maxObjectHeight + 5000);
gcode.addMove(config.objectPosition, config.moveSpeed, 0);
}
gcode.addComment("total_layers=%d",totalLayers);
GCodePathConfig skirtConfig(config.printSpeed, config.extrusionWidth, "SKIRT");
GCodePathConfig inset0Config(config.printSpeed, config.extrusionWidth, "WALL-OUTER");
GCodePathConfig inset1Config(config.printSpeed, config.extrusionWidth, "WALL-INNER");
GCodePathConfig fillConfig(config.infillSpeed, config.extrusionWidth, "FILL");
GCodePathConfig supportConfig(config.printSpeed, config.supportLineWidth, "SUPPORT");
if (config.raftBaseThickness > 0 && config.raftInterfaceThickness > 0)
{
GCodePathConfig raftBaseConfig(config.initialLayerSpeed, config.raftBaseLinewidth, "SUPPORT");
GCodePathConfig raftInterfaceConfig(config.initialLayerSpeed, config.raftInterfaceLinewidth, "SUPPORT");
{
gcode.addComment("LAYER:-2");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.setZ(config.raftBaseThickness);
gcode.setExtrusion(config.raftBaseThickness, config.filamentDiameter, config.filamentFlow);
gcodeLayer.addPolygonsByOptimizer(storage.raftOutline, &raftBaseConfig);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftBaseLinewidth, config.raftLineSpacing, config.infillOverlap, 0);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftBaseConfig);
gcodeLayer.writeGCode(false);
}
{
gcode.addComment("LAYER:-1");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.setZ(config.raftBaseThickness + config.raftInterfaceThickness);
gcode.setExtrusion(config.raftInterfaceThickness, config.filamentDiameter, config.filamentFlow);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftInterfaceLinewidth, config.raftLineSpacing, config.infillOverlap, 90);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftInterfaceConfig);
gcodeLayer.writeGCode(false);
}
}
int volumeIdx = 0;
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
logProgress("export", layerNr+1, totalLayers);
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.addComment("LAYER:%d", layerNr);
int32_t z = config.initialLayerThickness + layerNr * config.layerThickness;
z += config.raftBaseThickness + config.raftInterfaceThickness;
gcode.setZ(z);
if (layerNr == 0)
gcodeLayer.addPolygonsByOptimizer(storage.skirt, &skirtConfig);
for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++)
{
if (volumeCnt > 0)
volumeIdx = (volumeIdx + 1) % storage.volumes.size();
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
gcodeLayer.setExtruder(volumeIdx);
PathOptimizer partOrderOptimizer(gcode.getPositionXY());
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
partOrderOptimizer.addPolygon(layer->parts[partNr].insets[0][0]);
}
partOrderOptimizer.optimize();
for(unsigned int partCounter=0; partCounter<partOrderOptimizer.polyOrder.size(); partCounter++)
{
SliceLayerPart* part = &layer->parts[partOrderOptimizer.polyOrder[partCounter]];
gcodeLayer.setCombBoundary(&part->combBoundery);
gcodeLayer.forceRetract();
if (config.insetCount > 0)
{
for(int insetNr=part->insets.size()-1; insetNr>-1; insetNr--)
{
if (insetNr == 0)
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset0Config);
else
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset1Config);
}
}
Polygons fillPolygons;
int fillAngle = 45;
if (layerNr & 1) fillAngle += 90;
//int sparseSteps[1] = {config.extrusionWidth};
//generateConcentricInfill(part->skinOutline, fillPolygons, sparseSteps, 1);
generateLineInfill(part->skinOutline, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, (part->bridgeAngle > -1) ? part->bridgeAngle : fillAngle);
//int sparseSteps[2] = {config.extrusionWidth*5, config.extrusionWidth * 0.8};
//generateConcentricInfill(part->sparseOutline, fillPolygons, sparseSteps, 2);
if (config.sparseInfillLineDistance > 0)
{
if (config.sparseInfillLineDistance > config.extrusionWidth * 4)
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45);
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45 + 90);
}
else
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance, config.infillOverlap, fillAngle);
}
}
gcodeLayer.addPolygonsByOptimizer(fillPolygons, &fillConfig);
}
gcodeLayer.setCombBoundary(NULL);
}
if (config.supportAngle > -1)
{
SupportPolyGenerator supportGenerator(storage.support, z, config.supportAngle, config.supportEverywhere > 0, true);
gcodeLayer.addPolygonsByOptimizer(supportGenerator.polygons, &supportConfig);
if (layerNr == 0)
{
SupportPolyGenerator supportGenerator2(storage.support, z, config.supportAngle, config.supportEverywhere > 0, false);
gcodeLayer.addPolygonsByOptimizer(supportGenerator2.polygons, &supportConfig);
}
}
//Finish the layer by applying speed corrections for minimal layer times and slowdown for the initial layer.
if (int(layerNr) < config.initialSpeedupLayers)
{
int n = config.initialSpeedupLayers;
int layer0Factor = config.initialLayerSpeed * 100 / config.printSpeed;
gcodeLayer.setSpeedFactor((layer0Factor * (n - layerNr) + 100 * (layerNr)) / n);
}
gcodeLayer.forceMinimalLayerTime(config.minimalLayerTime, config.minimalFeedrate);
if (layerNr == 0)
gcode.setExtrusion(config.initialLayerThickness, config.filamentDiameter, config.filamentFlow);
else
gcode.setExtrusion(config.layerThickness, config.filamentDiameter, config.filamentFlow);
if (int(layerNr) >= config.fanOnLayerNr)
{
int speed = config.fanSpeedMin;
if (gcodeLayer.getSpeedFactor() <= 50)
{
speed = config.fanSpeedMax;
}else{
int n = gcodeLayer.getSpeedFactor() - 50;
speed = config.fanSpeedMin * n / 50 + config.fanSpeedMax * (50 - n) / 50;
}
gcode.addFanCommand(speed);
}else{
gcode.addFanCommand(0);
}
gcodeLayer.writeGCode(config.coolHeadLift > 0);
}
/* support debug
for(int32_t y=0; y<storage.support.gridHeight; y++)
{
for(int32_t x=0; x<storage.support.gridWidth; x++)
{
unsigned int n = x+y*storage.support.gridWidth;
if (storage.support.grid[n].size() < 1) continue;
int32_t z = storage.support.grid[n][0].z;
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, z), z);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
}
}
//*/
log("Wrote layers in %5.2fs.\n", timeElapsed(t));
gcode.tellFileSize();
gcode.addFanCommand(0);
logProgress("process", 1, 1);
log("Total time elapsed %5.2fs.\n", timeElapsed(t,true));
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
maxObjectHeight = std::max(maxObjectHeight, storage.modelSize.z);
}
void setConfig(Config& config, char* str)
{
char* valuePtr = strchr(str, '=');
if (!valuePtr) return;
*valuePtr++ = '\0';
#define STRINGIFY(_s) #_s
#define SETTING(longName, shortName) if (strcasecmp(str, STRINGIFY(longName)) == 0 || strcasecmp(str, STRINGIFY(shortName)) == 0) { config.longName = atoi(valuePtr); }
SETTING(layerThickness, lt);
SETTING(initialLayerThickness, ilt);
SETTING(filamentDiameter, fd);
SETTING(filamentFlow, ff);
SETTING(extrusionWidth, ew);
SETTING(insetCount, ic);
SETTING(downSkinCount, dsc);
SETTING(upSkinCount, usc);
SETTING(sparseInfillLineDistance, sild);
SETTING(infillOverlap, iover);
SETTING(skirtDistance, sd);
SETTING(skirtLineCount, slc);
SETTING(initialSpeedupLayers, isl);
SETTING(initialLayerSpeed, ils);
SETTING(printSpeed, ps);
SETTING(infillSpeed, is);
SETTING(moveSpeed, ms);
SETTING(fanOnLayerNr, fl);
SETTING(supportAngle, supa);
SETTING(supportEverywhere, supe);
SETTING(supportLineWidth, sulw);
SETTING(retractionAmount, reta);
SETTING(retractionSpeed, rets);
SETTING(retractionAmountExtruderSwitch, retes);
SETTING(multiVolumeOverlap, multiOverlap);
SETTING(objectPosition.X, posx);
SETTING(objectPosition.Y, posy);
SETTING(objectSink, objsink);
SETTING(raftMargin, raftMar);
SETTING(raftLineSpacing, raftLS);
SETTING(raftBaseThickness, raftBaseT);
SETTING(raftBaseLinewidth, raftBaseL);
SETTING(raftInterfaceThickness, raftInterfaceT);
SETTING(raftInterfaceLinewidth, raftInterfaceL);
SETTING(minimalLayerTime, minLayTime);
SETTING(minimalFeedrate, minFeed);
SETTING(coolHeadLift, coolLift);
SETTING(fanSpeedMin, fanMin);
SETTING(fanSpeedMax, fanMax);
SETTING(fixHorrible, fixHorrible);
SETTING(extruderOffset[1].X, eOff1X);
SETTING(extruderOffset[1].Y, eOff1Y);
SETTING(extruderOffset[2].X, eOff2X);
SETTING(extruderOffset[2].Y, eOff2Y);
SETTING(extruderOffset[3].X, eOff3X);
SETTING(extruderOffset[3].Y, eOff3Y);
#undef SETTING
if (strcasecmp(str, "startCode") == 0)
config.startCode = valuePtr;
if (strcasecmp(str, "endCode") == 0)
config.endCode = valuePtr;
}
#include "fffProcessor.h"
void print_usage()
{
@@ -455,6 +40,7 @@ void print_usage()
void signal_FPE(int n)
{
(void)n;
printf("Arithmetic exception.\n");
exit(1);
}
@@ -467,9 +53,8 @@ int main(int argc, char **argv)
#endif
signal(SIGFPE, signal_FPE);
GCodeExport gcode;
Config config;
int fileNr = 0;
ConfigSettings config;
fffProcessor processor(config);
config.filamentDiameter = 2890;
config.filamentFlow = 100;
@@ -484,19 +69,29 @@ int main(int argc, char **argv)
config.printSpeed = 50;
config.infillSpeed = 50;
config.moveSpeed = 200;
config.fanOnLayerNr = 2;
config.fanFullOnLayerNr = 2;
config.skirtDistance = 6000;
config.skirtLineCount = 1;
config.skirtMinLength = 0;
config.sparseInfillLineDistance = 100 * config.extrusionWidth / 20;
config.infillOverlap = 15;
config.objectPosition = Point(102500, 102500);
config.objectPosition.X = 102500;
config.objectPosition.Y = 102500;
config.objectSink = 0;
config.supportAngle = -1;
config.supportEverywhere = 0;
config.supportLineWidth = config.extrusionWidth;
config.retractionAmount = 4.5;
config.supportLineDistance = config.sparseInfillLineDistance;
config.supportExtruder = -1;
config.supportXYDistance = 700;
config.supportZDistance = 150;
config.retractionAmount = 4500;
config.retractionSpeed = 45;
config.retractionAmountExtruderSwitch = 14.5;
config.retractionAmountExtruderSwitch = 14500;
config.retractionMinimalDistance = 1500;
config.minimalExtrusionBeforeRetraction = 100;
config.enableOozeShield = 0;
config.enableCombing = 1;
config.enableWipeTower = 0;
config.multiVolumeOverlap = 0;
config.minimalLayerTime = 5;
@@ -512,7 +107,10 @@ int main(int argc, char **argv)
config.raftInterfaceThickness = 0;
config.raftInterfaceLinewidth = 0;
config.spiralizeMode = 0;
config.fixHorrible = 0;
config.gcodeFlavor = GCODE_FLAVOR_REPRAP;
memset(config.extruderOffset, 0, sizeof(config.extruderOffset));
config.startCode =
"M109 S210 ;Heatup to 210C\n"
@@ -533,7 +131,7 @@ int main(int argc, char **argv)
"M84 ;steppers off\n"
"G90 ;absolute positioning\n";
fprintf(stdout,"Cura_SteamEngine version %s\n", VERSION);
fprintf(stderr,"Cura_SteamEngine version %s\n", VERSION);
for(int argn = 1; argn < argc; argn++)
{
@@ -556,17 +154,24 @@ int main(int argc, char **argv)
break;
case 'o':
argn++;
gcode.setFilename(argv[argn]);
if (!gcode.isValid())
if (!processor.setTargetFile(argv[argn]))
{
logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
gcode.addComment("Generated with Cura_SteamEngine %s", VERSION);
break;
case 's':
argn++;
setConfig(config, argv[argn]);
{
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
if (!config.setSetting(argv[argn], valuePtr))
printf("Setting found: %s %s\n", argv[argn], valuePtr);
}
}
break;
case 'm':
argn++;
@@ -581,20 +186,14 @@ int main(int argc, char **argv)
}
}
}else{
if (!gcode.isValid())
{
logError("No output file specified\n");
return 1;
try {
processor.processFile(argv[argn]);
}catch(...){
printf("Unknown exception\n");
exit(1);
}
processFile(argv[argn], config, gcode, fileNr == 0);
fileNr ++;
}
}
if (gcode.isValid())
{
gcode.addFanCommand(0);
gcode.addCode(config.endCode);
log("Print time: %d\n", int(gcode.getTotalPrintTime()));
log("Filament: %d\n", int(gcode.getTotalFilamentUsed()));
}
processor.finalize();
}
+7 -1
Ver Arquivo
@@ -17,7 +17,7 @@ void* fgets_(char* ptr, size_t len, FILE* f)
return ptr;
}
ptr++;
len++;
len--;
}
return NULL;
}
@@ -79,6 +79,12 @@ SimpleModel* loadModelSTL_binary(const char* filename, FMatrix3x3& matrix)
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)
+3 -3
Ver Arquivo
@@ -8,7 +8,7 @@ The format returned is a Model class with an array of faces, which have integer
**/
#include <vector>
using std::vector;
#include "utils/intpoint.h"
#include "utils/floatpoint.h"
@@ -30,7 +30,7 @@ public:
class SimpleVolume
{
public:
std::vector<SimpleFace> faces;
vector<SimpleFace> faces;
void addFace(Point3& v0, Point3& v1, Point3& v2)
{
@@ -77,7 +77,7 @@ public:
class SimpleModel
{
public:
std::vector<SimpleVolume> volumes;
vector<SimpleVolume> volumes;
Point3 min()
{
+4 -18
Ver Arquivo
@@ -16,13 +16,10 @@ void carveMultipleVolumes(vector<SliceVolumeStorage> &volumes)
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
ClipperLib::Clipper clipper;
clipper.AddPolygons(layer1->parts[p1].outline, ClipperLib::ptSubject);
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
clipper.AddPolygons(layer2->parts[p2].outline, ClipperLib::ptClip);
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
}
clipper.Execute(ClipperLib::ctDifference, layer1->parts[p1].outline);
}
}
}
@@ -40,31 +37,20 @@ void generateMultipleVolumesOverlap(vector<SliceVolumeStorage> &volumes, int ove
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
ClipperLib::Clipper fullLayerClipper;
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
fullLayerClipper.AddPolygons(fullLayer, ClipperLib::ptSubject);
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
Polygons tmp;
ClipperLib::OffsetPolygons(layer1->parts[p1].outline, tmp, 20, ClipperLib::jtSquare, 2, false);
fullLayerClipper.AddPolygons(tmp, ClipperLib::ptClip);
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20));
}
fullLayerClipper.Execute(ClipperLib::ctUnion, fullLayer);
}
ClipperLib::OffsetPolygons(fullLayer, fullLayer, -20, ClipperLib::jtSquare, 2, false);
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++)
{
Polygons tmp;
ClipperLib::OffsetPolygons(layer1->parts[p1].outline, tmp, overlap / 2, ClipperLib::jtSquare, 2, false);
ClipperLib::Clipper clipper;
clipper.AddPolygons(tmp, ClipperLib::ptClip);
clipper.AddPolygons(fullLayer, ClipperLib::ptSubject);
clipper.Execute(ClipperLib::ctIntersection, layer1->parts[p1].outline);
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
}
}
}
+141
Ver Arquivo
@@ -0,0 +1,141 @@
/** 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 30
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 = vol->points[vol->faces[i].index[0]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[0]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[0]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
fwrite(&s, sizeof(s), 1, f);
}
fclose(f);
//Export the open faces so you can view the with Cura (hacky)
/*
char gcodeFilename[1024];
strcpy(gcodeFilename, filename);
strcpy(strchr(gcodeFilename, '.'), ".gcode");
f = fopen(gcodeFilename, "w");
for(unsigned int i=0;i<faces.size();i++)
{
for(int j=0;j<3;j++)
{
if (faces[i].touching[j] == -1)
{
Point3 p0 = points[faces[i].index[j]].p;
Point3 p1 = points[faces[i].index[(j+1)%3]].p;
fprintf(f, ";Model error(open face): (%f, %f, %f) (%f, %f, %f)\n", p0.x / 1000.0, p0.y / 1000.0, p0.z / 1000.0, p1.x / 1000.0, p1.y / 1000.0, p1.z / 1000.0);
}
}
}
fclose(f);
*/
}
+6 -138
Ver Arquivo
@@ -2,10 +2,8 @@
#ifndef OPTIMIZED_MODEL_H
#define OPTIMIZED_MODEL_H
#include "utils/intpoint.h"
#include <map>
#include <vector>
#include "modelFile/modelFile.h"
class OptimizedFace
{
@@ -17,98 +15,20 @@ class OptimizedPoint3
{
public:
Point3 p;
std::vector<uint32_t> faceIndexList;
vector<uint32_t> faceIndexList;
OptimizedPoint3(Point3 p): p(p) {}
};
#define MELD_DIST 30
class OptimizedModel;
class OptimizedVolume
{
public:
OptimizedModel* model;
std::vector<OptimizedPoint3> points;
std::vector<OptimizedFace> faces;
vector<OptimizedPoint3> points;
vector<OptimizedFace> faces;
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);
}
OptimizedVolume(SimpleVolume* volume, OptimizedModel* model);
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
{
@@ -151,59 +71,7 @@ public:
vMax -= vOffset;
}
void 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 = vol->points[vol->faces[i].index[0]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[0]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[0]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[1]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
flt = vol->points[vol->faces[i].index[2]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
fwrite(&s, sizeof(s), 1, f);
}
fclose(f);
//Export the open faces so you can view the with Cura (hacky)
/*
char gcodeFilename[1024];
strcpy(gcodeFilename, filename);
strcpy(strchr(gcodeFilename, '.'), ".gcode");
f = fopen(gcodeFilename, "w");
for(unsigned int i=0;i<faces.size();i++)
{
for(int j=0;j<3;j++)
{
if (faces[i].touching[j] == -1)
{
Point3 p0 = points[faces[i].index[j]].p;
Point3 p1 = points[faces[i].index[(j+1)%3]].p;
fprintf(f, ";Model error(open face): (%f, %f, %f) (%f, %f, %f)\n", p0.x / 1000.0, p0.y / 1000.0, p0.z / 1000.0, p1.x / 1000.0, p1.y / 1000.0, p1.z / 1000.0);
}
}
}
fclose(f);
*/
}
void saveDebugSTL(const char* filename);
};
#endif//OPTIMIZED_MODEL_H
-132
Ver Arquivo
@@ -1,132 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATHOPTIMIZER_H
#define PATHOPTIMIZER_H
#include <stdint.h>
float distanceSquared(const Point& p0, const Point& p1)
{
float dx = p0.X - p1.X;
float dy = p0.Y - p1.Y;
return dx * dx + dy * dy;
}
class PathOptimizer
{
public:
Point startPoint;
std::vector<ClipperLib::Polygon*> polygons;
std::vector<int> polyStart;
std::vector<int> polyOrder;
PathOptimizer(ClipperLib::IntPoint startPoint)
{
this->startPoint = startPoint;
}
void addPolygon(ClipperLib::Polygon& polygon)
{
this->polygons.push_back(&polygon);
}
void addPolygons(ClipperLib::Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(&polygons[i]);
}
void optimize()
{
std::vector<bool> picked;
for(unsigned int i=0;i<polygons.size(); i++)
{
int best = -1;
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
ClipperLib::Polygon* poly = polygons[i];
for(unsigned int j=0; j<poly->size(); j++)
{
float dist = distanceSquared((*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 = distanceSquared((*polygons[i])[0], p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 0;
}
dist = distanceSquared((*polygons[i])[1], p0);
if (dist < bestDist)
{
best = i;
bestDist = dist;
polyStart[i] = 1;
}
}else{
float dist = distanceSquared((*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 = distanceSquared((*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];
}
}
}
};
#endif//PATHOPTIMIZER_H
+95
Ver Arquivo
@@ -0,0 +1,95 @@
/** 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;
ClipperLib::Polygon* 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];
}
}
}
+35
Ver Arquivo
@@ -0,0 +1,35 @@
/** 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<ClipperLib::Polygon*> polygons;
vector<int> polyStart;
vector<int> polyOrder;
PathOrderOptimizer(ClipperLib::IntPoint startPoint)
{
this->startPoint = startPoint;
}
void addPolygon(ClipperLib::Polygon& polygon)
{
this->polygons.push_back(&polygon);
}
void addPolygons(ClipperLib::Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(&polygons[i]);
}
void optimize();
};
#endif//PATHOPTIMIZER_H
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "polygonOptimizer.h"
void optimizePolygon(ClipperLib::Polygon& poly)
{
Point p0 = poly[poly.size()-1];
for(unsigned int i=0;i<poly.size();i++)
{
Point p1 = poly[i];
if (shorterThen(p0 - p1, 10))
{
poly.erase(poly.begin() + i);
i --;
}else{
Point p2;
if (i < poly.size() - 1)
p2 = poly[i+1];
else
p2 = poly[0];
Point diff0 = normal(p1 - p0, 1000000);
Point diff2 = normal(p1 - p2, 1000000);
int64_t d = dot(diff0, diff2);
if (d < -999999000000LL)
{
poly.erase(poly.begin() + 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--;
}
}
}
+4 -43
Ver Arquivo
@@ -2,49 +2,10 @@
#ifndef POLYGON_OPTIMIZER_H
#define POLYGON_OPTIMIZER_H
void optimizePolygon(ClipperLib::Polygon& poly)
{
Point p0 = poly[poly.size()-1];
for(unsigned int i=0;i<poly.size();i++)
{
Point p1 = poly[i];
if (shorterThen(p0 - p1, 10))
{
poly.erase(poly.begin() + i);
i --;
}else{
Point p2;
if (i < poly.size() - 1)
p2 = poly[i+1];
else
p2 = poly[0];
Point diff0 = normal(p1 - p0, 1000000);
Point diff2 = normal(p1 - p2, 1000000);
int64_t d = dot(diff0, diff2);
if (d < -999999000000LL)
{
poly.erase(poly.begin() + i);
i --;
}else{
p0 = p1;
}
}
}
}
#include "utils/polygon.h"
void optimizePolygons(Polygons& polys)
{
for(unsigned int n=0;n<polys.size();n++)
{
optimizePolygon(polys[n]);
if (polys[n].size() < 3)
{
polys.erase(polys.begin() + n);
n--;
}
}
}
void optimizePolygon(ClipperLib::Polygon& poly);
void optimizePolygons(Polygons& polys);
#endif//POLYGON_OPTIMIZER_H
+19
Ver Arquivo
@@ -0,0 +1,19 @@
/** 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);
}
+4 -18
Ver Arquivo
@@ -1,23 +1,9 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef RAFT_H
#define RAFT_H
void generateRaft(SliceDataStorage& storage, int distance)
{
ClipperLib::Clipper raftUnion;
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
Polygons raft;
ClipperLib::OffsetPolygons(layer->parts[i].outline, raft, distance, ClipperLib::jtSquare, 2, false);
raftUnion.AddPolygon(raft[0], ClipperLib::ptSubject);
}
}
Polygons raftResult;
raftUnion.Execute(ClipperLib::ctUnion, raftResult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
for(unsigned int n=0; n<raftResult.size(); n++)
storage.raftOutline.push_back(raftResult[n]);
}
#include "sliceDataStorage.h"
void generateRaft(SliceDataStorage& storage, int distance);
#endif//RAFT_H
+58
Ver Arquivo
@@ -0,0 +1,58 @@
# vi:et:ts=4 sw=4 sts=4
import gzip
import os
import string
import subprocess
import sys
import unittest
EXECUTABLE = './CuraEngine'
TEST_DIR = 'testcase_models'
class TestGCode(unittest.TestCase):
def _tuplify(self, first, second):
return (first, second)
def _main(self, base):
stl_file = '{}.stl'.format(base)
gcode_file = '{}.gcode.gz'.format(base)
with gzip.GzipFile(os.path.join(TEST_DIR, gcode_file)) as ifp:
gcode_output = map(string.strip, ifp.readlines())
cmd = [EXECUTABLE, os.path.join(TEST_DIR, stl_file)]
stl_output = subprocess.check_output(cmd, stderr=subprocess.PIPE)
stl_output = stl_output.splitlines()
results = map(self._tuplify, gcode_output, stl_output)
for gcode_line, stl_line in results:
self.assertEqual(gcode_line, stl_line)
def test_wolt(self):
self._main('wolt')
def test_woltBaseline(self):
self._main('woltBaseline')
def test_woltNotFlat(self):
self._main('woltNotFlat')
def test_wolt_scaled200Perc(self):
self._main('wolt_scaled200Perc')
def test_wolt_smoothingOn(self):
self._main('wolt_smoothingOn')
if __name__ == '__main__':
if len(sys.argv) == 2:
EXECUTABLE = sys.argv[1]
sys.argv.remove(EXECUTABLE)
unittest.main()
+101
Ver Arquivo
@@ -0,0 +1,101 @@
#include <stdio.h>
#include "settings.h"
#define STRINGIFY(_s) #_s
#define SETTING(name) _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name))
#define SETTING2(name, altName) _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); _index.push_back(_ConfigSettingIndex(STRINGIFY(altName), &name))
ConfigSettings::ConfigSettings()
{
SETTING(layerThickness);
SETTING(initialLayerThickness);
SETTING(filamentDiameter);
SETTING(filamentFlow);
SETTING(extrusionWidth);
SETTING(insetCount);
SETTING(downSkinCount);
SETTING(upSkinCount);
SETTING(sparseInfillLineDistance);
SETTING(infillOverlap);
SETTING(skirtDistance);
SETTING(skirtLineCount);
SETTING(skirtMinLength);
SETTING(initialSpeedupLayers);
SETTING(initialLayerSpeed);
SETTING(printSpeed);
SETTING(infillSpeed);
SETTING(moveSpeed);
SETTING(fanFullOnLayerNr);
SETTING(supportAngle);
SETTING(supportEverywhere);
SETTING(supportLineDistance);
SETTING(supportXYDistance);
SETTING(supportZDistance);
SETTING(supportExtruder);
SETTING(retractionAmount);
SETTING(retractionSpeed);
SETTING(retractionAmountExtruderSwitch);
SETTING(retractionMinimalDistance);
SETTING(minimalExtrusionBeforeRetraction);
SETTING(enableCombing);
SETTING(enableOozeShield);
SETTING(enableWipeTower);
SETTING(multiVolumeOverlap);
SETTING2(objectPosition.X, posx);
SETTING2(objectPosition.Y, posy);
SETTING(objectSink);
SETTING(raftMargin);
SETTING(raftLineSpacing);
SETTING(raftBaseThickness);
SETTING(raftBaseLinewidth);
SETTING(raftInterfaceThickness);
SETTING(raftInterfaceLinewidth);
SETTING(minimalLayerTime);
SETTING(minimalFeedrate);
SETTING(coolHeadLift);
SETTING(fanSpeedMin);
SETTING(fanSpeedMax);
SETTING(fixHorrible);
SETTING(spiralizeMode);
SETTING(gcodeFlavor);
SETTING(extruderOffset[1].X);
SETTING(extruderOffset[1].Y);
SETTING(extruderOffset[2].X);
SETTING(extruderOffset[2].Y);
SETTING(extruderOffset[3].X);
SETTING(extruderOffset[3].Y);
}
#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;
}
+130
Ver Arquivo
@@ -0,0 +1,130 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <utils/floatpoint.h>
#include <vector>
#define VERSION "13.11.2"
#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
/**
* 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;
int retractionAmount;
int retractionAmountExtruderSwitch;
int retractionSpeed;
int retractionMinimalDistance;
int minimalExtrusionBeforeRetraction;
int enableCombing;
int enableOozeShield;
int enableWipeTower;
int multiVolumeOverlap;
int initialSpeedupLayers;
int initialLayerSpeed;
int printSpeed;
int infillSpeed;
int moveSpeed;
int fanFullOnLayerNr;
//Support material
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];
const char* startCode;
const char* endCode;
ConfigSettings();
bool setSetting(const char* key, const char* value);
};
#endif//SETTINGS_H
+116
Ver Arquivo
@@ -0,0 +1,116 @@
/** 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 * (double(extrusionWidth) / 1000.0) * (double(extrusionWidth) / 1000.0)) * 0.3;
for(unsigned int i=0; i<part->skinOutline.size(); i++)
{
double area = fabs(ClipperLib::Area(part->skinOutline[i])) / 1000.0 / 1000.0;
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 * (double(config.extrusionWidth) / 1000.0) * (double(config.extrusionWidth) / 1000.0)) * 3;
for(unsigned int i=0; i<result.size(); i++)
{
double area = fabs(ClipperLib::Area(result[i])) / 1000.0 / 1000.0;
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);
}
}
+3 -152
Ver Arquivo
@@ -2,158 +2,9 @@
#ifndef SKIN_H
#define SKIN_H
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap)
{
SliceLayer* layer = &storage.layers[layerNr];
#include "sliceDataStorage.h"
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons temp;
ClipperLib::OffsetPolygons(part->insets[part->insets.size() - 1], temp, -extrusionWidth/2, ClipperLib::jtSquare, 2, false);
ClipperLib::Clipper downskinClipper;
ClipperLib::Clipper upskinClipper;
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
if (part->insets.size() > 1)
{
ClipperLib::Clipper thinWallClipper;
ClipperLib::OffsetPolygons(part->insets[0], temp, -extrusionWidth / 2 - extrusionWidth * infillOverlap / 100, ClipperLib::jtSquare, 2, false);
thinWallClipper.AddPolygons(temp, ClipperLib::ptSubject);
ClipperLib::OffsetPolygons(part->insets[1], temp, extrusionWidth * 6 / 10, ClipperLib::jtSquare, 2, false);
thinWallClipper.AddPolygons(temp, ClipperLib::ptClip);
thinWallClipper.Execute(ClipperLib::ctDifference, temp);
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
}
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))
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
}
}
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))
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
}
}
ClipperLib::Polygons downSkin;
ClipperLib::Polygons upSkin;
downskinClipper.Execute(ClipperLib::ctDifference, downSkin);
upskinClipper.Execute(ClipperLib::ctDifference, upSkin);
{
ClipperLib::Clipper skinCombineClipper;
skinCombineClipper.AddPolygons(downSkin, ClipperLib::ptSubject);
skinCombineClipper.AddPolygons(upSkin, ClipperLib::ptClip);
skinCombineClipper.Execute(ClipperLib::ctUnion, part->skinOutline);
}
double minAreaSize = (2 * M_PI * (double(extrusionWidth) / 1000.0) * (double(extrusionWidth) / 1000.0)) * 0.3;
for(unsigned int i=0; i<part->skinOutline.size(); i++)
{
double area = fabs(ClipperLib::Area(part->skinOutline[i])) / 1000.0 / 1000.0;
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.erase(part->skinOutline.begin() + 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 temp;
ClipperLib::OffsetPolygons(part->insets[part->insets.size() - 1], temp, -extrusionWidth/2, ClipperLib::jtSquare, 2, false);
ClipperLib::Clipper downskinClipper;
ClipperLib::Clipper upskinClipper;
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
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)
{
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2], ClipperLib::ptClip);
}else{
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
}
}
}
}
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)
{
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2], ClipperLib::ptClip);
}else{
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
}
}
}
}
ClipperLib::Polygons downSkin;
ClipperLib::Polygons upSkin;
ClipperLib::Polygons result;
downskinClipper.Execute(ClipperLib::ctDifference, downSkin);
upskinClipper.Execute(ClipperLib::ctDifference, upSkin);
{
ClipperLib::Clipper skinClipper;
skinClipper.AddPolygons(downSkin, ClipperLib::ptSubject);
skinClipper.AddPolygons(upSkin, ClipperLib::ptClip);
skinClipper.Execute(ClipperLib::ctUnion, result);
}
double minAreaSize = 3.0;//(2 * M_PI * (double(config.extrusionWidth) / 1000.0) * (double(config.extrusionWidth) / 1000.0)) * 3;
for(unsigned int i=0; i<result.size(); i++)
{
double area = fabs(ClipperLib::Area(result[i])) / 1000.0 / 1000.0;
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.erase(result.begin() + i);
i -= 1;
}
}
ClipperLib::Clipper sparseClipper;
sparseClipper.AddPolygons(temp, ClipperLib::ptSubject);
sparseClipper.AddPolygons(result, ClipperLib::ptClip);
sparseClipper.Execute(ClipperLib::ctDifference, part->sparseOutline);
}
}
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
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/** 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)
{
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
Polygons skirtPolygons;
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++)
{
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(distance + extrusionWidth * skirtNr + extrusionWidth / 2));
}
}
SupportPolyGenerator supportGenerator(storage.support, 0);
skirtPolygons = skirtPolygons.unionPolygons(supportGenerator.polygons.offset(distance + extrusionWidth * skirtNr + extrusionWidth / 2));
storage.skirt.add(skirtPolygons);
int lenght = storage.skirt.polygonLength();
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength)
count++;
}
}
+3 -21
Ver Arquivo
@@ -2,26 +2,8 @@
#ifndef SKIRT_H
#define SKIRT_H
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count)
{
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
ClipperLib::Clipper skirtUnion;
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
{
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
Polygons skirt;
ClipperLib::OffsetPolygons(layer->parts[i].outline, skirt, distance + extrusionWidth * skirtNr + extrusionWidth / 2, ClipperLib::jtSquare, 2, false);
skirtUnion.AddPolygon(skirt[0], ClipperLib::ptSubject);
}
}
Polygons skirtResult;
skirtUnion.Execute(ClipperLib::ctUnion, skirtResult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
for(unsigned int n=0; n<skirtResult.size(); n++)
storage.skirt.push_back(skirtResult[n]);
}
}
#include "sliceDataStorage.h"
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength);
#endif//SKIRT_H
+10 -3
Ver Arquivo
@@ -2,10 +2,8 @@
#ifndef SLICE_DATA_STORAGE_H
#define SLICE_DATA_STORAGE_H
#include <vector>
using std::vector;
#include "utils/intpoint.h"
using ClipperLib::Polygons;
#include "utils/polygon.h"
/*
SliceData
@@ -47,6 +45,12 @@ public:
class SupportStorage
{
public:
bool generated;
int angle;
bool everywhere;
int XYDistance;
int ZDistance;
Point gridOffset;
int32_t gridScale;
int32_t gridWidth, gridHeight;
@@ -66,9 +70,12 @@ public:
Point3 modelSize, modelMin, modelMax;
Polygons skirt;
Polygons raftOutline;
vector<Polygons> oozeShield;
vector<SliceVolumeStorage> volumes;
SupportStorage support;
Polygons wipeTower;
Point wipePoint;
};
#endif//SLICE_DATA_STORAGE_H
+404
Ver Arquivo
@@ -0,0 +1,404 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "utils/gettime.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;
ClipperLib::Polygon poly;
poly.push_back(segmentList[startSegment].start);
unsigned int segmentIndex = startSegment;
bool canClose;
while(true)
{
canClose = false;
segmentList[segmentIndex].addedToPolygon = true;
Point p0 = segmentList[segmentIndex].end;
poly.push_back(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, 10))
{
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 < 2 * 2)
{
if (i == j)
{
polygonList.add(openPolygonList[i]);
openPolygonList[i].clear();
break;
}else{
for(unsigned int n=0; n<openPolygonList[j].size(); n++)
openPolygonList[i].push_back(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 = 10000 * 10000;
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 >= 10000 * 10000)
break;
if (bestA == bestB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}else{
if (reversed)
{
for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
openPolygonList[bestA].push_back(openPolygonList[bestB][n]);
}else{
for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
openPolygonList[bestA].push_back(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)
{
unsigned int n = polygonList.size();
polygonList.add(ClipperLib::Polygon());
for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].push_back(polygonList[bestResult.polygonIdx][j]);
for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
polygonList[n].push_back(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].push_back(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].push_back(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
ClipperLib::Polygon poly;
for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
poly.push_back(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
openPolygonList[bestB].push_back(poly[n]);
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].push_back(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].push_back(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
openPolygonList[bestB].push_back(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) printf("***\n");
printf("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y));
printf("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 = 1000;
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;
fprintf(stderr, "Layer count: %i\n", layerCount);
layers.resize(layerCount);
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, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
fprintf(f, "<path d=\"");
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
{
ClipperLib::Polygon& 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++)
{
ClipperLib::Polygon& p = layers[i].openPolygonList[j];
if (p.size() < 1) continue;
fprintf(f, "<polyline 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);
}
+7 -386
Ver Arquivo
@@ -2,6 +2,8 @@
#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.
@@ -42,285 +44,7 @@ public:
Polygons polygonList;
Polygons openPolygonList;
void makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching)
{
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
{
if (segmentList[startSegment].addedToPolygon)
continue;
ClipperLib::Polygon poly;
poly.push_back(segmentList[startSegment].start);
unsigned int segmentIndex = startSegment;
bool canClose;
while(true)
{
canClose = false;
segmentList[segmentIndex].addedToPolygon = true;
Point p0 = segmentList[segmentIndex].end;
poly.push_back(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, 10))
{
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.push_back(poly);
else
openPolygonList.push_back(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 < 2 * 2)
{
if (i == j)
{
polygonList.push_back(openPolygonList[i]);
openPolygonList.erase(openPolygonList.begin() + i);
}else{
for(unsigned int n=0; n<openPolygonList[j].size(); n++)
openPolygonList[i].push_back(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 = 10000 * 10000;
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 >= 10000 * 10000)
break;
if (bestA == bestB)
{
polygonList.push_back(openPolygonList[bestA]);
openPolygonList.erase(openPolygonList.begin() + bestA);
}else{
if (reversed)
{
for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
openPolygonList[bestA].push_back(openPolygonList[bestB][n]);
}else{
for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
openPolygonList[bestA].push_back(openPolygonList[bestB][n]);
}
openPolygonList[bestB].clear();
}
}
if (extensiveStitching)
{
//For extensive stitching find 2 open polygons that are touching the same closed polygon.
// 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 = LONG_LONG_MAX;
bestResult.polygonIdx = -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 < LONG_LONG_MAX)
{
if (bestA == bestB)
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
polygonList.push_back(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}else if (bestResult.AtoB)
{
unsigned int n = polygonList.size();
polygonList.push_back(ClipperLib::Polygon());
for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].push_back(polygonList[bestResult.polygonIdx][j]);
for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
polygonList[n].push_back(openPolygonList[bestA][j]);
openPolygonList[bestA].clear();
}else{
unsigned int n = polygonList.size();
polygonList.push_back(openPolygonList[bestA]);
for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].push_back(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].push_back(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}else if (bestResult.AtoB)
{
ClipperLib::Polygon poly;
for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
poly.push_back(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
openPolygonList[bestB].push_back(poly[n]);
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].push_back(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].push_back(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
openPolygonList[bestB].push_back(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) printf("***\n");
printf("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y));
printf("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)
{
while(openPolygonList.size() > 0)
{
if (openPolygonList[0].size() > 0)
polygonList.push_back(openPolygonList[0]);
openPolygonList.erase(openPolygonList.begin());
}
}
//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 = 1000;
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.erase(polygonList.begin() + i);
i--;
}
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
optimizePolygons(polygonList);
}
void makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching);
private:
gapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
@@ -418,73 +142,9 @@ public:
std::vector<SlicerLayer> layers;
Point3 modelSize, modelMin;
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;
fprintf(stdout, "Layer count: %i\n", layerCount);
layers.resize(layerCount);
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);
}
}
double t = getTime();
int percDone;
for(unsigned int layerNr=0; layerNr<layers.size(); layerNr++)
{
percDone = 100*layerNr/layers.size();
if((getTime()-t)>2.0) fprintf(stdout, "\rProcessing layers... (%d percent)",percDone);
layers[layerNr].makePolygons(ov, keepNoneClosed, extensiveStitching);
}
fprintf(stdout, "\rProcessed all layers in %5.1fs \n",timeElapsed(t));
}
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z)
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);
@@ -494,46 +154,7 @@ public:
return seg;
}
void dumpSegments(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, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
fprintf(f, "<path d=\"");
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
{
ClipperLib::Polygon& 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++)
{
ClipperLib::Polygon& p = layers[i].openPolygonList[j];
fprintf(f, "<polyline 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);
}
void dumpSegmentsToHTML(const char* filename);
};
#endif//SLICER_H
+187
Ver Arquivo
@@ -0,0 +1,187 @@
/** 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++;
ClipperLib::Polygon poly;
ClipperLib::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.push_back(startPoint * storage.gridScale + storage.gridOffset - Point(storage.gridScale/2, 0));
poly.push_back(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.push_back(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);
}
+9 -178
Ver Arquivo
@@ -2,94 +2,10 @@
#ifndef SUPPORT_H
#define SUPPORT_H
inline void swap(Point3& p0, Point3& p1)
{
Point3 tmp = p0;
p0 = p1;
p1 = tmp;
}
inline void swap(int64_t& n, int64_t& m)
{
int64_t tmp = n;
n = m;
m = tmp;
}
#include "sliceDataStorage.h"
#include "optimizedModel.h"
int cmp_SupportPoint(const void* a, const void* b)
{
return ((SupportPoint*)a)->z - ((SupportPoint*)b)->z;
}
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int32_t initial, int32_t thickness)
{
storage.gridOffset.X = om->vMin.x;
storage.gridOffset.Y = om->vMin.y;
storage.gridScale = 400;
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];
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;
}
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance);
class SupportPolyGenerator
{
@@ -100,100 +16,15 @@ private:
SupportStorage& storage;
double cosAngle;
int32_t z;
int supportZDistance;
bool everywhere;
int* done;
inline bool needSupportAt(int32_t x, int32_t y)
{
if (x < 1) return false;
if (y < 1) return false;
if (x >= storage.gridWidth - 1) return false;
if (y >= storage.gridHeight - 1) return false;
unsigned int n = x+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 >= z && (i == 0 || storage.grid[n][i-1].z < 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 < z) return false;
if (storage.grid[n-storage.gridWidth].size() < 1) return false;
if (storage.grid[n-storage.gridWidth][0].cosAngle < cosAngle) return false;
if (storage.grid[n-storage.gridWidth][0].z < z) return false;
if (storage.grid[n+storage.gridWidth].size() < 1) return false;
if (storage.grid[n+storage.gridWidth][0].cosAngle < cosAngle) return false;
if (storage.grid[n+storage.gridWidth][0].z < z) return false;
}
return true;
}
public:
SupportPolyGenerator(SupportStorage& storage, int32_t z, int angle, bool everywhere, bool xAxis)
: storage(storage), z(z), everywhere(everywhere)
{
cosAngle = cos(double(90 - angle) / 180.0 * M_PI) - 0.01;
bool needSupportAt(Point p);
void lazyFill(Point startPoint);
if (xAxis)
{
for(int32_t y=0; y<storage.gridHeight; y+=2)
{
for(int32_t x=0; x<storage.gridWidth; x++)
{
if (!needSupportAt(x, y)) continue;
int32_t startX = x;
while(x < storage.gridWidth && needSupportAt(x, y))
{
x ++;
}
x --;
if (x > startX)
{
Point p0(startX * storage.gridScale + storage.gridOffset.X, y * storage.gridScale + storage.gridOffset.Y);
Point p1(x * storage.gridScale + storage.gridOffset.X, y * storage.gridScale + storage.gridOffset.Y);
ClipperLib::Polygon p;
p.push_back(p0);
p.push_back(p1);
polygons.push_back(p);
}
}
}
}else{
for(int32_t x=0; x<storage.gridWidth; x+=2)
{
for(int32_t y=0; y<storage.gridHeight; y++)
{
if (!needSupportAt(x, y)) continue;
int32_t startY = y;
while(y < storage.gridHeight && needSupportAt(x, y))
{
y ++;
}
y --;
if (y > startY)
{
Point p0(x * storage.gridScale + storage.gridOffset.X, startY * storage.gridScale + storage.gridOffset.Y);
Point p1(x * storage.gridScale + storage.gridOffset.X, y * storage.gridScale + storage.gridOffset.Y);
ClipperLib::Polygon p;
p.push_back(p0);
p.push_back(p1);
polygons.push_back(p);
}
}
}
}
}
public:
SupportPolyGenerator(SupportStorage& storage, int32_t z);
};
#endif//SUPPORT_H
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
+14
Ver Arquivo
@@ -0,0 +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;
}
+11 -8
Ver Arquivo
@@ -6,6 +6,7 @@
#include <windows.h>
#else
#include <sys/time.h>
#include <stddef.h>
#endif
static inline double getTime()
@@ -18,13 +19,15 @@ static inline double getTime()
return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0;
#endif
}
double t_start = getTime();
static inline double timeElapsed(double &t,bool all_time = false) {
double t_passed = getTime() - t;
t = getTime();
if(all_time)
return getTime() - t_start;
return t_passed;
}
class TimeKeeper
{
private:
double startTime;
public:
TimeKeeper();
double restart();
};
#endif//GETTIME_H
+10 -80
Ver Arquivo
@@ -8,8 +8,8 @@ Integer points are used to avoid floating point rounding errors, and because Cli
*/
#define INLINE static inline
//Include Clipper to get the ClipperLib::IntPoint definition, which we reuse as Point definition.
#include "clipper/clipper.hpp"
using ClipperLib::Polygons;
#include <limits.h>
#include <stdint.h>
@@ -71,6 +71,11 @@ public:
/* 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); }
@@ -88,6 +93,10 @@ 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)
{
@@ -169,85 +178,6 @@ public:
{
return Point(p.X * matrix[0] + p.Y * matrix[2], p.X * matrix[1] + p.Y * matrix[3]);
}
void apply(Polygons& polys) const
{
for(unsigned int i=0; i<polys.size(); i++)
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
polys[i][j] = apply(polys[i][j]);
}
}
}
};
INLINE Point centerOfMass(const ClipperLib::Polygon& poly)
{
double x = 0, y = 0;
Point p0 = poly[poly.size()-1];
for(unsigned int n=0; n<poly.size(); n++)
{
Point p1 = poly[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(poly);
x = x / 6 / area;
y = y / 6 / area;
if (x < 0)
{
x = -x;
y = -y;
}
return Point(x, y);
}
/* 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//INT_POINT_H
+36
Ver Arquivo
@@ -0,0 +1,36 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include <stdarg.h>
#include "utils/logoutput.h"
int verbose_level;
void logError(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(stdout);
}
void _log(const char* fmt, ...)
{
if (verbose_level < 1)
return;
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(stdout);
}
void logProgress(const char* type, int value, int maxValue)
{
if (verbose_level < 2)
return;
fprintf(stdout, "Progress:%s:%i:%i\n", type, value, maxValue);
fflush(stdout);
}
+3 -28
Ver Arquivo
@@ -4,34 +4,9 @@
extern int verbose_level;
void logError(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(stdout);
}
void _log(const char* fmt, ...)
{
if (verbose_level < 1)
return;
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(stdout);
}
void logProgress(const char* type, int value, int maxValue)
{
if (verbose_level < 2)
return;
fprintf(stdout, "Progress:%s:%i:%i\n", type, value, maxValue);
fflush(stdout);
}
void logError(const char* fmt, ...);
void _log(const char* fmt, ...);
#define log _log
void logProgress(const char* type, int value, int maxValue);
#endif//LOGOUTPUT_H
+204
Ver Arquivo
@@ -0,0 +1,204 @@
#ifndef UTILS_POLYGON_H
#define UTILS_POLYGON_H
#include <vector>
using std::vector;
#include "clipper/clipper.hpp"
#include "utils/intpoint.h"
class Polygons
{
private:
ClipperLib::Polygons polygons;
public:
unsigned int size()
{
return polygons.size();
}
ClipperLib::Polygon& operator[] (int index) //__attribute__((__deprecated__))
{
return polygons[index];
}
void remove(int index)
{
polygons.erase(polygons.begin() + index);
}
void clear()
{
polygons.clear();
}
void add(const ClipperLib::Polygon& poly)
{
polygons.push_back(poly);
}
void add(const Polygons& other)
{
for(unsigned int n=0; n<other.polygons.size(); n++)
polygons.push_back(other.polygons[n]);
}
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.AddPolygons(polygons, ClipperLib::ptSubject);
clipper.AddPolygons(other.polygons, ClipperLib::ptClip);
clipper.Execute(ClipperLib::ctDifference, ret.polygons);
return ret;
}
Polygons unionPolygons(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper;
clipper.AddPolygons(polygons, ClipperLib::ptSubject);
clipper.AddPolygons(other.polygons, ClipperLib::ptSubject);
clipper.Execute(ClipperLib::ctUnion, ret.polygons, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
return ret;
}
Polygons intersection(const Polygons& other) const
{
Polygons ret;
ClipperLib::Clipper clipper;
clipper.AddPolygons(polygons, ClipperLib::ptSubject);
clipper.AddPolygons(other.polygons, ClipperLib::ptClip);
clipper.Execute(ClipperLib::ctIntersection, ret.polygons);
return ret;
}
Polygons offset(int distance) const
{
Polygons ret;
ClipperLib::OffsetPolygons(polygons, ret.polygons, distance, ClipperLib::jtSquare, 2, false);
return ret;
}
vector<Polygons> splitIntoParts(bool unionAll = false) const
{
vector<Polygons> ret;
ClipperLib::Clipper clipper;
ClipperLib::PolyTree resultPolyTree;
clipper.AddPolygons(polygons, ClipperLib::ptSubject);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
_processPolyTreeNode(&resultPolyTree, ret);
return ret;
}
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);
}
}
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]);
}
}
}
};
INLINE Point centerOfMass(const ClipperLib::Polygon& poly)
{
double x = 0, y = 0;
Point p0 = poly[poly.size()-1];
for(unsigned int n=0; n<poly.size(); n++)
{
Point p1 = poly[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(poly);
x = x / 6 / area;
y = y / 6 / area;
if (x < 0)
{
x = -x;
y = -y;
}
return Point(x, y);
}
/* 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
+75
Ver Arquivo
@@ -0,0 +1,75 @@
#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++)
{
ClipperLib::Polygon& 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