Comparar commits
102 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| f0eae492f7 | |||
| 1d171e71de | |||
| 16593d3a91 | |||
| 24c87e639e | |||
| 5619087429 | |||
| f333905fd0 | |||
| 339182611e | |||
| 73939c7567 | |||
| 8e77f212e5 | |||
| de7080619c | |||
| 5de1559110 | |||
| 1f19060719 | |||
| 3abf24a314 | |||
| 744f09eefb | |||
| 6a3963d903 | |||
| d873602182 | |||
| bca34c80d6 | |||
| 685d11d5d4 | |||
| 9b8ddbe29c | |||
| 3ca3e1fcfe | |||
| debfa35673 | |||
| 5e310d509c | |||
| 346815a3e0 | |||
| c92139b8a2 | |||
| f777218a6d | |||
| 4c3875e427 | |||
| 4f5b366af2 | |||
| c4eb84b6a3 | |||
| c338f4aca2 | |||
| ee2700872f | |||
| 3d3f093e3b | |||
| 25cf9c0f9e | |||
| de7620cea0 | |||
| 74486d27b4 | |||
| 3621aa88c8 | |||
| ef5a252337 | |||
| 5640378faa | |||
| 76689fa9b2 | |||
| 4094c42872 | |||
| 04fde2a98a | |||
| 47f04ffb4f | |||
| 7ddd198e03 | |||
| 11f489d06f | |||
| 02036f34e6 | |||
| 9ef3d792fe | |||
| 04780c2115 | |||
| 64e5ec3182 | |||
| 6ccca8343f | |||
| 538031171d | |||
| 316861f05c | |||
| 2ea0ab5c5f | |||
| 20c9519427 | |||
| 85acee47b1 | |||
| 08ddccdf51 | |||
| 3e50a1c5fb | |||
| 7e8c7814ed | |||
| c47fb9f48f | |||
| ca8ab015db | |||
| ff851e324c | |||
| 54a79b1433 | |||
| e2a2f69415 | |||
| 7a961d56cb | |||
| 009b23d1e3 | |||
| 7c10a490e0 | |||
| 0d7b413d8e | |||
| 31fb704c4c | |||
| bd1c7fd463 | |||
| 3681858d8d | |||
| b3a796ce26 | |||
| 7c775490d9 | |||
| 38f244ba7d | |||
| cbe6bcf747 | |||
| 14f28328f9 | |||
| fda9dd9b4c | |||
| 7f2fa9af9a | |||
| 50a01f1667 | |||
| e89106eacb | |||
| 74714ed2b5 | |||
| b9571c984f | |||
| 3d95856f6d | |||
| 6f0848b538 | |||
| 13316a61dc | |||
| 2eb079dfbf | |||
| 21ba0c3da3 | |||
| 68f5253bb9 | |||
| d48cc09ab2 | |||
| cc44d86cb4 | |||
| 8125cd8558 | |||
| 925f112863 | |||
| 756e11445b | |||
| 1b50b0e6d7 | |||
| e343e3adae | |||
| 338d049c5c | |||
| 64d7d5f82c | |||
| 6f46d74797 | |||
| 7b0665cb2c | |||
| 717b5eb95d | |||
| 236b9814f3 | |||
| df1d945339 | |||
| abbea47241 | |||
| 43d545c528 | |||
| 8a13a20cb1 |
@@ -9,4 +9,8 @@
|
||||
_bin
|
||||
_obj
|
||||
*.depend
|
||||
*.o
|
||||
.*.swp
|
||||
*.gcode
|
||||
CuraEngine
|
||||
|
||||
|
||||
+17
-61
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+80
-39
@@ -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 24–28, 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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Referência em uma Nova Issue
Bloquear um usuário