Comparar commits

..

19 Commits

Autor SHA1 Mensagem Data
Tim Kuipers 8b7bbbad02 cleanup: removed double setisinside (CURA-1841) 2016-07-13 17:41:34 +02:00
Tim Kuipers 0322ef69e9 lil assert CURA-833 2016-07-11 16:01:08 +02:00
Tim Kuipers aaca121916 Merge branch 'master' into feature_infill_surface_mesh 2016-07-11 14:47:11 +02:00
Tim Kuipers b6042c2895 mesh surface mode bug debugging code 2016-05-20 15:30:11 +02:00
Tim Kuipers 0750c075aa fix: mesh surface mode should still connect line segments in polylines (CURA-833) 2016-05-20 15:28:18 +02:00
Tim Kuipers 8f85540093 fix: svg opacity for lines 2016-05-20 15:13:46 +02:00
Tim Kuipers d35ef076ca fix: SVG lines color was forgotten 2016-05-20 15:13:08 +02:00
Tim Kuipers 756e0a2c94 fix: clear open polylines when applying surface thickness and dont make polygons in surface only mode (CURA-833) 2016-05-20 15:12:43 +02:00
Tim Kuipers 72683ac03e fix: don't retract when doing an infill mesh surface mode (CURA-833) 2016-05-13 18:29:53 +02:00
Tim Kuipers 3302c24dc7 fix: let combing not see infill meshes (CURA-833) 2016-05-13 18:29:21 +02:00
Tim Kuipers 9f21dc9312 fix: updated clipper (CURA-833) 2016-05-13 17:37:49 +02:00
Tim Kuipers d0e7ab58b0 fix: compute infill area even is infill is off (CURA-833)
infill meshes use this infill area
2016-05-13 12:16:39 +02:00
Tim Kuipers e2a4142b43 fix: polygons::intersectPolylines did it the wrong way around (CURA-833) 2016-05-13 12:14:11 +02:00
Tim Kuipers f7c5ff3ba4 feat: extra svg debug stuff (CURA-833) 2016-05-13 12:13:37 +02:00
Tim Kuipers bded9d54e3 feat: support magic surface mode for infill meshes (CURA-833) 2016-05-13 12:12:39 +02:00
Tim Kuipers e809005ea7 fix: handled open polys as if they were closed and vice versa (CURA-833) 2016-05-12 23:37:28 +02:00
Tim Kuipers 841a9ecd05 lil: supress mesh errors (CURA-833) 2016-05-12 23:36:43 +02:00
Tim Kuipers 57ada0edc8 feat: let infill meshes work with open polylines (CURA-833) 2016-05-12 23:07:12 +02:00
Tim Kuipers 84fb65aa4b CHANGES CLIPPER feat: intersect with open polylines (CURA-833) 2016-05-12 23:06:41 +02:00
50 arquivos alterados com 1091 adições e 1904 exclusões
-1
Ver Arquivo
@@ -47,4 +47,3 @@ documentation/latex/*
## Test results.
tests/output.xml
callgrind.out.*
-3
Ver Arquivo
@@ -31,7 +31,6 @@ option(BUILD_TESTS OFF)
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
add_definitions(-DUSE_CPU_TIME)
endif()
# Add warnings
@@ -48,7 +47,6 @@ add_library(clipper STATIC libs/clipper/clipper.cpp)
set(engine_SRCS # Except main.cpp.
src/bridge.cpp
src/commandSocket.cpp
src/ConicalOverhang.cpp
src/ExtruderTrain.cpp
src/FffGcodeWriter.cpp
src/FffPolygonGenerator.cpp
@@ -115,7 +113,6 @@ set(engine_TEST_UTILS
BucketGrid2DTest
LinearAlg2DTest
PolygonUtilsTest
PolygonTest
)
# Generating ProtoBuf protocol
+34 -3
Ver Arquivo
@@ -1,8 +1,39 @@
=====================================================================
Clipper Change Log
=====================================================================
v6.2.1 (31 October 2014) Rev 482
* Bugfix in ClipperOffset.Execute where the Polytree.IsHole property
was returning incorrect values with negative offsets
* Very minor improvement to join rounding in ClipperOffset
* Fixed CPP OpenGL demo.
v6.1.3 (19 January 2014)
v6.2.0 (17 October 2014) Rev 477
* Numerous minor bugfixes, too many to list.
(See revisions 454-475 in Sourceforge Repository)
* The ZFillFunction (custom callback function) has had its parameters
changed.
* Curves demo removed (temporarily).
* Deprecated functions have been removed.
v6.1.5 (26 February 2014) Rev 460
* Improved the joining of output polygons sharing a common edge
when those common edges are horizontal.
* Fixed a bug in ClipperOffset.AddPath() which would produce
incorrect solutions when open paths were added before closed paths.
* Minor code tidy and performance improvement
v6.1.4 (6 February 2014)
* Fixed bugs in MinkowskiSum
* Fixed minor bug when using Clipper.ForceSimplify.
* Modified use_xyz callback so that all 4 vertices around an
intersection point are now passed to the callback function.
v6.1.3a (22 January 2014) Rev 453
* Fixed buggy PointInPolygon function (C++ and C# only).
Note this bug only affected the newly exported function, the
internal PointInPolygon function used by Clipper was OK.
v6.1.3 (19 January 2014) Rev 452
* Fixed potential endless loop condition when adding open
paths to Clipper.
* Fixed missing implementation of SimplifyPolygon function
@@ -13,11 +44,11 @@ v6.1.3 (19 January 2014)
* Overloaded MinkowskiSum function to accommodate multi-contour
paths.
v6.1.2 (15 December 2013)
v6.1.2 (15 December 2013) Rev 444
* Fixed broken C++ header file.
* Minor improvement to joining polygons.
v6.1.1 (13 December 2013)
v6.1.1 (13 December 2013) Rev 441
* Fixed a couple of bugs affecting open paths that could
raise unhandled exceptions.
+388 -534
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+33 -36
Ver Arquivo
@@ -1,8 +1,8 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Version : 6.2.1 *
* Date : 31 October 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
@@ -34,7 +34,7 @@
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.1.3"
#define CLIPPER_VERSION "6.2.0"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
@@ -44,11 +44,10 @@
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
#define use_lines
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <vector>
#include <set>
@@ -57,6 +56,7 @@
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
namespace ClipperLib {
@@ -69,11 +69,16 @@ enum PolyType { ptSubject, ptClip };
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
typedef unsigned int cUInt;
typedef int cInt;
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
#else
typedef signed long long cInt;
typedef unsigned long long cUInt;
typedef signed long long cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class
typedef unsigned long long ulong64;
#endif
struct IntPoint {
@@ -117,15 +122,12 @@ struct DoublePoint
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
@@ -134,6 +136,7 @@ class PolyNode
{
public:
PolyNode();
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
@@ -168,11 +171,6 @@ bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
@@ -183,8 +181,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
@@ -202,7 +199,7 @@ enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinima;
struct LocalMinimum;
struct Scanbeam;
struct OutPt;
struct OutRec;
@@ -213,7 +210,6 @@ typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
@@ -236,12 +232,14 @@ protected:
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
@@ -268,7 +266,7 @@ public:
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(TZFillCallback zFillFunc);
void ZFillFunction(ZFillCallback zFillFunc);
#endif
protected:
void Reset();
@@ -279,7 +277,8 @@ private:
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
@@ -289,7 +288,7 @@ private:
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
TZFillCallback m_ZFill; //custom callback
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
@@ -308,21 +307,19 @@ private:
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
@@ -344,7 +341,7 @@ private:
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e);
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
};
//------------------------------------------------------------------------------
@@ -1,50 +1,40 @@
{
"version": 2,
"version": 1,
"name": "Command line setting defaults CuraEngine",
"metadata":
{
"author": "Ultimaker B.V."
},
"settings": {
"author": "Ultimaker B.V.",
"categories": {
"command_line_settings": {
"label": "Command Line Settings",
"type": "category",
"children": {
"settings": {
"center_object": {
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
"type": "bool",
"label": "Center object",
"default_value": true
"type": "boolean",
"default": false
},
"machine_print_temp_wait": {
"description": "Whether to wait for the nozzle temperature to be reached when preheating the nozzles at the start of the gcode.",
"type": "bool",
"label": "Machine print temp wait",
"default_value": true
"type": "boolean",
"default": true
},
"mesh_position_x": {
"description": "Offset applied to the object in the x direction.",
"type": "float",
"label": "Mesh position x",
"default_value": 0
"default": 0
},
"mesh_position_y": {
"description": "Offset applied to the object in the y direction.",
"type": "float",
"label": "Mesh position y",
"default_value": 0
"default": 0
},
"mesh_position_z": {
"description": "Offset applied to the object in the z direction. With this you can perform what was used to call 'Object Sink'.",
"type": "float",
"label": "Mesh position z",
"default_value": 0
"default": 0
},
"prime_tower_dir_outward": {
"description": "Whether to start printing in the middle of the prime tower and end up at the perimeter, or the other way around. This is only used for certain types of prime tower.",
"type": "bool",
"label": "Prime tower direction outward",
"default_value": false
"type": "boolean",
"default": false
}
}
}
-35
Ver Arquivo
@@ -1,35 +0,0 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#include "ConicalOverhang.h"
namespace cura {
void ConicalOverhang::apply(Slicer* slicer, double angle, int layer_thickness)
{
double tanAngle = tan(angle); // the XY-component of the angle
int max_dist_from_lower_layer = tanAngle * layer_thickness; // max dist which can be bridged
for (unsigned int layer_nr = slicer->layers.size() - 2; static_cast<int>(layer_nr) >= 0; layer_nr--)
{
SlicerLayer& layer = slicer->layers[layer_nr];
SlicerLayer& layer_above = slicer->layers[layer_nr + 1];
if (std::abs(max_dist_from_lower_layer) < 5)
{ // magically nothing happens when max_dist_from_lower_layer == 0
// below magic code solves that
int safe_dist = 20;
Polygons diff = layer_above.polygons.difference(layer.polygons.offset(-safe_dist));
layer.polygons = layer.polygons.unionPolygons(diff);
layer.polygons = layer.polygons.smooth(safe_dist, 100*100);
layer.polygons.simplify(safe_dist, safe_dist * safe_dist / 4);
// somehow layer.polygons get really jagged lines with a lot of vertices
// without the above steps slicing goes really slow
}
else
{
layer.polygons = layer.polygons.unionPolygons(layer_above.polygons.offset(-max_dist_from_lower_layer));
}
}
}
}//namespace cura
-30
Ver Arquivo
@@ -1,30 +0,0 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef CONICAL_OVERHANG_H
#define CONICAL_OVERHANG_H
#include "slicer.h"
namespace cura {
/*!
* A class for changing the geometry of a model such that it is printable without support -
* Or at least with at least support as possible
*/
class ConicalOverhang
{
public:
/*!
* Change the slice data such that the model becomes more printable
*
* \param[in,out] slicer The slice data
* \param angle The maximum angle which can be printed without generating support (or at least generating least support)
* \param layer_thickness The general layer thickness
*/
static void apply(Slicer* slicer, double angle, int layer_thickness);
};
}//namespace cura
#endif // CONICAL_OVERHANG_H
+48 -120
Ver Arquivo
@@ -35,7 +35,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
CommandSocket::getInstance()->beginGCode();
}
setConfigFanSpeedLayerTime(storage);
setConfigFanSpeedLayerTime();
setConfigCoasting(storage);
@@ -90,20 +90,14 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
layer_plan_buffer.flush();
}
void FffGcodeWriter::setConfigFanSpeedLayerTime(SliceDataStorage& storage)
void FffGcodeWriter::setConfigFanSpeedLayerTime()
{
for (int extr = 0; extr < storage.meshgroup->getExtruderCount(); extr++)
{
fan_speed_layer_time_settings_per_extruder.emplace_back();
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings = fan_speed_layer_time_settings_per_extruder.back();
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
fan_speed_layer_time_settings.cool_min_layer_time = train->getSettingInSeconds("cool_min_layer_time");
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = train->getSettingInSeconds("cool_min_layer_time_fan_speed_max");
fan_speed_layer_time_settings.cool_fan_speed_min = train->getSettingInPercentage("cool_fan_speed_min");
fan_speed_layer_time_settings.cool_fan_speed_max = train->getSettingInPercentage("cool_fan_speed_max");
fan_speed_layer_time_settings.cool_min_speed = train->getSettingInMillimetersPerSecond("cool_min_speed");
fan_speed_layer_time_settings.cool_fan_full_layer = train->getSettingAsCount("cool_fan_full_layer");
}
fan_speed_layer_time_settings.cool_min_layer_time = getSettingInSeconds("cool_min_layer_time");
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = getSettingInSeconds("cool_min_layer_time_fan_speed_max");
fan_speed_layer_time_settings.cool_fan_speed_min = getSettingInPercentage("cool_fan_speed_min");
fan_speed_layer_time_settings.cool_fan_speed_max = getSettingInPercentage("cool_fan_speed_max");
fan_speed_layer_time_settings.cool_min_speed = getSettingInMillimetersPerSecond("cool_min_speed");
fan_speed_layer_time_settings.cool_fan_full_layer = getSettingAsCount("cool_fan_full_layer");
}
void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
@@ -170,7 +164,7 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_x"), mesh.getSettingInMillimetersPerSecond("jerk_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMillimetersPerSecond("acceleration_infill"), mesh.getSettingInMillimetersPerSecond("jerk_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
}
@@ -236,8 +230,6 @@ void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
constexpr bool wait = true;
gcode.writeTemperatureCommand(start_extruder_nr, train.getSettingInDegreeCelsius("material_print_temperature"), wait);
gcode.writePrimeTrain(train.getSettingInMillimetersPerSecond("speed_travel"));
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[start_extruder_nr];
gcode.writeRetraction(&retraction_config);
}
}
@@ -276,7 +268,6 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
// some infill config for all lines infill generation below
int offset_from_poly_outline = 0;
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
int extra_infill_shift = 0;
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
{ // raft base layer
@@ -285,9 +276,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_height = train->getSettingInMicrons("raft_base_thickness");
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(true);
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
{
gcode_layer.setExtruder(extruder_nr);
@@ -300,16 +290,16 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
Polygons raftLines;
double fill_angle = 0;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_base_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_base_fan_speed"));
}
{ // raft interface layer
@@ -317,9 +307,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(true);
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
@@ -328,7 +317,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
Polygons raftLines;
int offset_from_poly_outline = 0;
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
Infill infill_comp(EFillMethod::ZIG_ZAG, storage.raftOutline, offset_from_poly_outline, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
@@ -336,8 +325,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
current_extruder_planned = gcode_layer.getExtruder();
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_interface_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_interface_fan_speed"));
}
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
@@ -347,9 +336,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(true);
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
@@ -358,7 +346,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
Polygons raft_lines;
int offset_from_poly_outline = 0;
double fill_angle = 90 * raftSurfaceLayer;
Infill infill_comp(EFillMethod::ZIG_ZAG, storage.raftOutline, offset_from_poly_outline, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raft_lines);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
@@ -366,8 +354,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
current_extruder_planned = gcode_layer.getExtruder();
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_surface_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_surface_fan_speed"));
}
}
@@ -406,7 +394,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, getSettingAsCombingMode("retraction_combing"), comb_offset_from_outlines, avoid_other_parts, avoid_distance);
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings, getSettingAsCombingMode("retraction_combing"), comb_offset_from_outlines, avoid_other_parts, avoid_distance);
if (layer_nr == 0)
{ // process the skirt of the starting extruder
@@ -545,7 +533,12 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
{
return;
}
if (mesh->getSettingBoolean("infill_mesh"))
{
gcode_layer.setIsInside(true);
}
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
SliceLayer* layer = &mesh->layers[layer_nr];
@@ -677,8 +670,8 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
{
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
gcode_layer.setIsInside(false);
gcode_layer.setIsInside(false); // we are going out of this part to a different part which might cross air
}
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -692,29 +685,18 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle, int extrusion_width)
{
int64_t z = layer_nr * getSettingInMicrons("layer_height");
if (infill_line_distance > 0)
{
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
for(unsigned int combine_idx = 1; combine_idx < part.infill_area_per_combine_per_density[0].size(); combine_idx++)
for(unsigned int n=1; n<part.infill_area_per_combine.size(); n++)
{
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
Infill infill_comp(infill_pattern, part.infill_area_per_combine[n], 0, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
Polygons infill_polygons;
Polygons infill_lines;
for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++)
{ // combine different density infill areas (for gradual infill)
unsigned int density_factor = 2 << density_idx; // == pow(2, density_idx + 1)
int infill_line_distance_here = infill_line_distance * density_factor; // the highest density infill combines with the next to create a grid with density_factor 1
int infill_shift = infill_line_distance_here / 2;
if (density_idx == part.infill_area_per_combine_per_density.size() - 1)
{
infill_line_distance_here /= 2;
}
Infill infill_comp(infill_pattern, part.infill_area_per_combine_per_density[density_idx][combine_idx], 0, extrusion_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift, false, false);
infill_comp.generate(infill_polygons, infill_lines);
}
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[combine_idx]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[combine_idx], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
infill_comp.generate(infill_polygons, infill_lines);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
}
}
@@ -722,7 +704,7 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle, int extrusion_width)
{
if (infill_line_distance == 0 || part.infill_area_per_combine_per_density[0].size() == 0)
if (infill_line_distance == 0 || part.infill_area_per_combine.size() == 0)
{
return;
}
@@ -730,45 +712,10 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
//Combine the 1 layer thick infill with the top/bottom skin and print that as one thing.
Polygons infill_polygons;
Polygons infill_lines;
int64_t z = layer_nr * getSettingInMicrons("layer_height");
EFillMethod pattern = mesh->getSettingAsFillMethod("infill_pattern");
for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++)
{
unsigned int density_factor = 2 << density_idx; // == pow(2, density_idx + 1)
int infill_line_distance_here = infill_line_distance * density_factor; // the highest density infill combines with the next to create a grid with density_factor 1
int infill_shift = infill_line_distance_here / 2;
// infill shift explanation: [>]=shift ["]=line_dist
// : | : | : | : | > furthest from top
// : | | | : | | | : | | | : | | | > further from top
// : | | | | | | | : | | | | | | | : | | | | | | | : | | | | | | | > near top
// >>"""""
// : | : | : | : | > furthest from top
// : | | | : | | | : | | | : | | | > further from top
// : | | | | | | | : | | | | | | | : | | | | | | | : | | | | | | | > near top
// >>>>"""""""""
// : | : | : | : | > furthest from top
// : | | | : | | | : | | | : | | | > further from top
// : | | | | | | | : | | | | | | | : | | | | | | | : | | | | | | | > near top
// >>>>>>>>"""""""""""""""""
if (density_idx == part.infill_area_per_combine_per_density.size() - 1)
{ // the least dense infill should fill up all remaining gaps
// : | : | : | : | : > furthest from top
// : | | | : | | | : | | | : | | | : > further from top
// : | | | | | | | : | | | | | | | : | | | | | | | : | | | | | | | : > near top
// . . . . . . . .
// : : : : : : : :
// `"""' `"""""""' `"""""""""""""""' `"""""""'
// ^ new line distance for lowest density infill
// ^ infill_line_distance_here for lowest density infill up till here
// ^ middle density line dist
// ^ highest density line dist
infill_line_distance_here /= 2;
}
Infill infill_comp(pattern, part.infill_area_per_combine_per_density[density_idx][0], 0, extrusion_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift, false, false);
infill_comp.generate(infill_polygons, infill_lines);
}
Infill infill_comp(pattern, part.infill_area_per_combine[0], 0, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
infill_comp.generate(infill_polygons, infill_lines);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
{
@@ -833,8 +780,6 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int skin_angle, int extrusion_width)
{
int64_t z = layer_nr * getSettingInMicrons("layer_height");
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
{
Polygons skin_polygons;
@@ -868,9 +813,8 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
{
inner_skin_outline = &skin_part.outline;
}
int extra_infill_shift = 0;
Infill infill_comp(pattern, *inner_skin_outline, offset_from_inner_skin_outline, extrusion_width, extrusion_width, skin_overlap, skin_angle, z, extra_infill_shift, false, false);
Infill infill_comp(pattern, *inner_skin_outline, offset_from_inner_skin_outline, extrusion_width, extrusion_width, skin_overlap, skin_angle, false, false);
infill_comp.generate(skin_polygons, skin_lines);
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
@@ -937,8 +881,6 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
return;
}
int64_t z = layer_nr * getSettingInMicrons("layer_height");
int support_line_distance = getSettingInMicrons("support_line_distance");
int extrusion_width = storage.support_config.getLineWidth();
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
@@ -975,9 +917,7 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
offset_from_outline = -extrusion_width;
support_infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInMicrons("infill_overlap_mm"); // support lines area should be expanded outward to overlap with the boundary polygon
}
int extra_infill_shift = 0;
Infill infill_comp(support_pattern, island, offset_from_outline, extrusion_width, support_line_distance, support_infill_overlap, 0, z, extra_infill_shift, getSettingBoolean("support_connect_zigzags"), true);
Infill infill_comp(support_pattern, island, offset_from_outline, extrusion_width, support_line_distance, support_infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
Polygons support_polygons;
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines);
@@ -998,9 +938,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
{
return;
}
int64_t z = layer_nr * getSettingInMicrons("layer_height");
EFillMethod pattern = getSettingAsFillMethod("support_roof_pattern");
int support_line_distance = getSettingInMicrons("support_roof_line_distance");
@@ -1030,10 +968,9 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
}
int support_skin_overlap = 0; // the roofs should never be expanded outwards
int outline_offset = 0;
int extra_infill_shift = 0;
int outline_offset = 0;
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, storage.support_roof_config.getLineWidth(), support_line_distance, support_skin_overlap, fillAngle, z, extra_infill_shift, false, true);
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, storage.support_roof_config.getLineWidth(), support_line_distance, support_skin_overlap, fillAngle, false, true);
Polygons support_polygons;
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines);
@@ -1085,23 +1022,14 @@ void FffGcodeWriter::finalize()
{
double print_time = gcode.getTotalPrintTime();
std::vector<double> filament_used;
std::vector<std::string> material_ids;
for (int extr_nr = 0; extr_nr < getSettingAsCount("machine_extruder_count"); extr_nr++)
{
filament_used.emplace_back(gcode.getTotalFilamentUsed(extr_nr));
material_ids.emplace_back(gcode.getMaterialGUID(extr_nr));
}
std::string prefix = gcode.getFileHeader(&print_time, filament_used, material_ids);
std::string prefix = gcode.getFileHeader(&print_time, filament_used);
CommandSocket::getInstance()->sendGCodePrefix(prefix);
}
if (getSettingBoolean("acceleration_enabled"))
{
gcode.writeAcceleration(getSettingInMillimetersPerSecond("machine_acceleration"));
}
if (getSettingBoolean("jerk_enabled"))
{
gcode.writeJerk(getSettingInMillimetersPerSecond("machine_max_jerk_xy"));
}
gcode.finalize(getSettingString("machine_end_gcode").c_str());
for (int e = 0; e < getSettingAsCount("machine_extruder_count"); e++)
{
+2 -4
Ver Arquivo
@@ -68,7 +68,7 @@ private:
bool skirt_is_processed[MAX_EXTRUDERS]; //!< Whether the skirt polygons have been processed into planned paths for each extruder train
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder; //!< The settings used relating to minimal layer time and fan speeds. Configured for each extruder.
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //!< The settings used relating to minimal layer time and fan speeds.
Point last_position_planned; //!< The position of the head before planning the next layer
int current_extruder_planned; //!< The extruder train in use before planning the next layer
@@ -153,10 +153,8 @@ public:
private:
/*!
* Set the FffGcodeWriter::fan_speed_layer_time_settings by retrieving all settings from the global/per-meshgroup settings.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigFanSpeedLayerTime(SliceDataStorage& storage);
void setConfigFanSpeedLayerTime();
/*!
* Create and set the SliceDataStorage::coasting_config for each extruder.
+129 -113
Ver Arquivo
@@ -18,11 +18,11 @@
#include "debug.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "ConicalOverhang.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
#include "progress/ProgressEstimatorLinear.h"
#include "utils/SVG.h" // debug
namespace cura
{
@@ -62,8 +62,8 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
return false;
}
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
int slice_layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if (slice_layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
return true; //This is NOT an error state!
}
@@ -72,7 +72,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup->meshes[mesh_idx];
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, slice_layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
slicerList.push_back(slicer);
/*
for(SlicerLayer& layer : slicer->layers)
@@ -84,32 +84,25 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("conical_overhang_enabled"))
{
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
}
}
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList);
// TODO!!! dont generate multi volume overlap with infill meshes!
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
Slicer* slicer = slicerList[meshIdx];
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
createLayerParts(meshStorage, slicer, mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
@@ -134,13 +127,19 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
layer.printZ += train->getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
}
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
return true;
@@ -150,12 +149,12 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
// compute layer count and remove first empty layers
// there is no separate progress stage for removeEmptyFisrtLayer (TODO)
unsigned int slice_layer_count = 0;
unsigned int total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
{
slice_layer_count = std::max<unsigned int>(slice_layer_count, mesh.layers.size());
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
}
}
@@ -180,44 +179,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
mesh_order.push_back(order_and_mesh_idx.second);
}
}
for (unsigned int mesh_order_idx(0); mesh_order_idx < mesh_order.size(); ++mesh_order_idx)
for (unsigned int mesh_idx : mesh_order)
{
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, slice_layer_count, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_order_idx + 1, storage.meshes.size());
processBasicWallsSkinInfill(storage, mesh_idx, mesh_order, total_layers, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_idx + 1, storage.meshes.size());
}
unsigned int print_layer_count = 0;
if (CommandSocket::isInstantiated())
{ // send layer info
for (unsigned int layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
{
SliceLayer* layer = nullptr;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{ // find first mesh which has this layer
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (int(layer_nr) <= mesh.layer_nr_max_filled_layer)
{
layer = &mesh.layers[layer_nr];
print_layer_count = layer_nr + 1;
break;
}
}
if (layer != nullptr)
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer->printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
}
}
}
log("Layer count: %i\n", print_layer_count);
//layerparts2HTML(storage, "output/output.html");
// we need to remove empty layers after we have procesed the insets
// processInsets might throw away parts if they have no wall at all (cause it doesn't fit)
// brim depends on the first layer not being empty
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), print_layer_count); // changes total_layers!
if (print_layer_count == 0)
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers); // changes total_layers!
if (total_layers == 0)
{
log("Stopping process because there are no non-empty layers.\n");
return;
@@ -225,7 +198,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, print_layer_count);
AreaSupport::generateSupportAreas(storage, total_layers);
/*
if (storage.support.generated)
@@ -243,28 +216,28 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
// handle helpers
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, print_layer_count);
storage.primeTower.generatePaths(storage, total_layers);
processOozeShield(storage, print_layer_count);
processOozeShield(storage, total_layers);
processDraftShield(storage, print_layer_count);
processDraftShield(storage, total_layers);
processPlatformAdhesion(storage);
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
processDerivedWallsSkinInfill(mesh, print_layer_count);
processDerivedWallsSkinInfill(mesh, total_layers);
}
}
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate)
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh"))
{
processInfillMesh(storage, mesh_order_idx, mesh_order, total_layers);
processInfillMesh(storage, mesh_idx, mesh_order, total_layers);
}
// TODO: make progress more accurate!!
@@ -288,27 +261,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(total_layers);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
bool process_infill = mesh.getSettingInMicrons("infill_line_distance") > 0;
if (!process_infill)
{ // do process infill anyway if it's modified by modifier meshes
for (unsigned int other_mesh_order_idx(mesh_order_idx + 1); other_mesh_order_idx < mesh_order.size(); ++other_mesh_order_idx)
{
unsigned int other_mesh_idx = mesh_order[other_mesh_order_idx];
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (other_mesh.getSettingBoolean("infill_mesh"))
{
AABB3D aabb = storage.meshgroup->meshes[mesh_idx].getAABB();
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx].getAABB();
aabb.expandXY(mesh.getSettingInMicrons("xy_offset"));
other_aabb.expandXY(other_mesh.getSettingInMicrons("xy_offset"));
if (aabb.hit(other_aabb))
{
process_infill = true;
}
}
}
}
// skin & infill
// Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
@@ -320,32 +273,31 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
{
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(mesh, layer_number, process_infill);
processSkinsAndInfill(mesh, layer_number);
}
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
}
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
mesh.layer_nr_max_filled_layer = -1;
for (unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{
SliceLayer& layer = mesh.layers[layer_idx];
std::vector<PolygonsPart> new_parts;
Polygons new_open_polylines;
for (unsigned int other_mesh_idx : mesh_order)
{ // limit the infill mesh's outline to within the infill of all meshes with lower order
{
if (other_mesh_idx == mesh_idx)
{
break; // all previous meshes have been processed
}
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (layer_idx >= other_mesh.layers.size())
{ // there can be no interaction between the infill mesh and this other non-infill mesh
{
continue;
}
@@ -354,30 +306,91 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
for (SliceLayerPart& part : layer.parts)
{
for (SliceLayerPart& other_part : other_layer.parts)
{ // limit the outline of each part of this infill mesh to the infill of parts of the other mesh with lower infill mesh order
{
if (!part.boundaryBox.hit(other_part.boundaryBox))
{ // early out
{
continue;
}
Polygons new_outline = part.outline.intersection(other_part.getOwnInfillArea());
Polygons& infill = other_part.infill_area;
Polygons new_outline = part.outline.intersection(infill);
if (new_outline.size() == 1)
{ // we don't have to call splitIntoParts, because a single polygon can only be a single part
{
PolygonsPart outline_part_here;
outline_part_here.add(new_outline[0]);
new_parts.push_back(outline_part_here);
}
else if (new_outline.size() > 1)
{ // we don't know whether it's a multitude of parts because of newly introduced holes, or because the polygon has been split up
{
std::vector<PolygonsPart> new_parts_here = new_outline.splitIntoParts();
for (PolygonsPart& new_part_here : new_parts_here)
{
new_parts.push_back(new_part_here);
}
}
// change the infill area of the non-infill mesh which is to be filled with e.g. lines
other_part.infill_area_own = other_part.getOwnInfillArea().difference(part.outline);
// note: don't change the part.infill_area, because we change the structure of that area, while the basic area in which infill is printed remains the same
// the infill area remains the same for combing
infill = infill.difference(part.outline);
other_part.infill_area_per_combine.back() = infill;
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (SliceLayerPart& other_part : other_layer.parts)
{
Polygons& infill = other_part.infill_area;
Polygons new_open_polylines_here = layer.openPolyLines.intersectPolylines(infill);
new_open_polylines.add(new_open_polylines_here);
if (layer_idx == 37)
{
Polygons wrong_line;
{ PolygonRef line = wrong_line.newPoly();
line.emplace_back(130422,112525);
line.emplace_back(130272,112525); }
{ PolygonRef line = wrong_line.newPoly();
line.emplace_back(130422,112525);
line.emplace_back(130272,112525); }
{ PolygonRef line = wrong_line.newPoly();
line.emplace_back(132322,112525);
line.emplace_back(130422,112525); }
Polygons intersected = wrong_line.intersectPolylines(infill);
{
AABB aabb(infill);
aabb.expand(MM2INT(5.0));
SVG svg("debug_layer_37.html",aabb);
svg.writeAreas(infill);
svg.writeLines(layer.openPolyLines, SVG::Color::BLUE);
svg.writeLines(new_open_polylines_here, SVG::Color::GREEN);
// svg.writePoints(wrong_line, true, 1);
}
{
AABB aabb(infill);
aabb.expand(MM2INT(5.0));
SVG svg("debug_layer_37_part.html",aabb);
svg.writeAreas(infill);
svg.writeLines(wrong_line, SVG::Color::BLUE);
svg.writeLines(intersected, SVG::Color::GREEN);
// svg.writePoints(wrong_line, true, 1);
}
{
AABB aabb(infill);
aabb.expand(MM2INT(5.0));
SVG svg("debug_layer_37_polylines.html",aabb);
svg.writeLines(layer.openPolyLines, SVG::Color::BLACK);
// svg.writePoints(wrong_line, true, 1);
}
std::cerr << "start\n";
for (PolygonRef poly : layer.openPolyLines)
{
std::cerr << " { PolygonRef line = wrong_line.newPoly();\n line.emplace_back" << poly[0] << ";\n line.emplace_back"<<poly[1]<<"; }" << std::endl;
}
for (PolygonRef poly : infill)
for (Point& p : poly)
std::cerr << " infill point " << p << std::endl;
std::cerr << "written\n";
}
// TODO: Don't adjust infill area for open polylines? \/
// infill = infill.difference(layer.openPolyLines.offsetPolyLine(mesh.getSettingInMicrons("wall_line_width") / 2));
// other_part.infill_area_per_combine.back() = infill;
}
}
}
@@ -389,20 +402,13 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
layer.parts.back().outline = part;
layer.parts.back().boundaryBox.calculate(part);
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
mesh.layer_nr_max_filled_layer = layer_idx; // last set by the highest non-empty layer
}
layer.openPolyLines = new_open_polylines;
}
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
// combine infill
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
@@ -412,6 +418,17 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh,
{
processFuzzyWalls(mesh);
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
SliceLayer& layer = mesh.layers[1];
{
AABB aabb(layer.openPolyLines);
aabb.expand(MM2INT(5.0));
SVG svg("debug_layer_37_polylines.html",aabb);
svg.writeLines(layer.openPolyLines, SVG::Color::BLACK);
// svg.writePoints(wrong_line, true, 1);
}
}
}
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
@@ -486,7 +503,7 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, cons
}
}
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
@@ -497,10 +514,9 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"));
if (process_infill)
{ // process infill when infill density > 0
// or when other infill meshes want to modify this infill
// if (mesh.getSettingInMicrons("infill_line_distance") > 0)
// TODO: only compute the area if there are any infill meshes to be computed
{
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
@@ -563,7 +579,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
storage.draft_protection_shield = draft_shield.approxConvexHull(draft_shield_dist);
storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist);
}
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
+5 -12
Ver Arquivo
@@ -70,22 +70,22 @@ private:
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, skin and infill
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_idx The index of the mesh to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed (used for infill meshes)
* \param total_layers The total number of layers over all objects
* \param inset_skin_progress_estimate The progress stage estimate calculator
*/
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate);
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_idx The index of the mesh to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed
* \param total_layers The total number of layers over all objects
*/
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers);
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers);
/*!
* Process features which are derived from the basic walls, skin, and infill:
@@ -125,9 +125,8 @@ private:
* Generate the skin areas.
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the skin areas.
* \param process_infill Generate infill areas
*/
void processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill);
void processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr);
/*!
* Generate the polygons where the draft screen should be.
@@ -146,12 +145,6 @@ private:
/*!
* Make the outer wall 'fuzzy'
*
* Introduce new vertices and move existing vertices in or out by a random distance, based on the fuzzy skin settings.
*
* This only changes the outer wall.
*
* \param[in,out] mesh where the outer wall is retrieved and stored in.
*/
void processFuzzyWalls(SliceMeshStorage& mesh);
+1 -4
Ver Arquivo
@@ -169,12 +169,9 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
int n_patterns = 2; // alternating patterns between layers
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
int extra_infill_shift = 0;
generateGroundpoly(storage);
int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position
for (int extruder = 0; extruder < extruder_count; extruder++)
{
int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
@@ -187,7 +184,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle);
infill_comp.generate(result_polygons, result_lines);
}
}
+3 -3
Ver Arquivo
@@ -1,6 +1,6 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef WALLS_COMPUTATION_H
#define WALLS_COMPUTATION_H
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
@@ -66,4 +66,4 @@ private:
};
}//namespace cura
#endif//WALLS_COMPUTATION_H
#endif//INSET_H
+4 -2
Ver Arquivo
@@ -329,7 +329,7 @@ void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
#endif
}
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, const Polygons& polygons, int line_width)
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
{
#ifdef ARCUS
if (polygons.size() == 0)
@@ -400,7 +400,7 @@ void CommandSocket::sendLayerData()
#ifdef ARCUS
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
log("End sliced object called. Sending %d layers.", private_data->current_layer_count);
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
if (private_data->sliced_objects >= private_data->object_count)
{
@@ -412,6 +412,8 @@ void CommandSocket::sendLayerData()
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_layers.clear();
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
}
#endif
}
+2 -2
Ver Arquivo
@@ -59,12 +59,12 @@ public:
/*!
* Send a polygon to the engine. This is used for the layerview in the GUI
*/
void sendPolygons(cura::PrintFeatureType type, int layer_nr, const cura::Polygons& polygons, int line_width);
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, const cura::Polygons& polygons, int line_width);
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
+3 -11
Ver Arquivo
@@ -69,7 +69,6 @@ void GCodeExport::preSetup(const MeshGroup* settings)
extruder_attr[extruder_nr].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[extruder_nr].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[extruder_nr].material_guid = train->getSettingString("material_guid");
extruder_attr[extruder_nr].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[extruder_nr].end_code = train->getSettingString("machine_extruder_end_code");
@@ -90,8 +89,6 @@ void GCodeExport::preSetup(const MeshGroup* settings)
{
new_line = "\n";
}
estimateCalculator.setFirmwareDefaults(settings);
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
@@ -117,7 +114,7 @@ void GCodeExport::setInitialTemp(int extruder_nr, double temp)
}
std::string GCodeExport::getFileHeader(const double* print_time, const std::vector<double>& filament_used, const std::vector<std::string>& mat_ids)
std::string GCodeExport::getFileHeader(const double* print_time, const std::vector<double>& filament_used, const std::vector<int16_t>& mat_ids)
{
std::ostringstream prefix;
switch (flavor)
@@ -142,9 +139,9 @@ std::string GCodeExport::getFileHeader(const double* print_time, const std::vect
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.VOLUME_USED:" << static_cast<int>(filament_used[extr_nr]) << new_line;
}
if (mat_ids.size() == extruder_count && mat_ids[extr_nr] != "")
if (mat_ids.size() == extruder_count)
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line;
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line; // TODO: convert to hexadecimal format
}
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.DIAMETER:" << float(INT2MM(getNozzleSize(extr_nr))) << new_line;
}
@@ -204,11 +201,6 @@ Point GCodeExport::getExtruderOffset(int id)
return extruder_attr[id].nozzle_offset;
}
std::string GCodeExport::getMaterialGUID(int extruder_nr)
{
return extruder_attr[extruder_nr].material_guid;
}
Point GCodeExport::getGcodePos(int64_t x, int64_t y, int extruder_train)
{
if (use_extruder_offset_to_offset_coords) { return Point(x,y) - getExtruderOffset(extruder_train); }
+3 -7
Ver Arquivo
@@ -46,8 +46,6 @@ private:
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string material_guid; //!< The GUID for the material used by this extruder
std::string start_code;
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
@@ -158,10 +156,10 @@ public:
*
* \param print_time The total print time in seconds of the whole gcode (if known)
* \param filament_used The total mm^3 filament used for each extruder or a vector of the wrong size of unknown
* \param mat_ids The material GUIDs for each material.
* \param mat_ids The material ids for each material.
* \return The string representing the file header
*/
std::string getFileHeader(const double* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<std::string>& mat_ids = std::vector<std::string>());
std::string getFileHeader(const double* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<int16_t>& mat_ids = std::vector<int16_t>());
void setLayerNr(unsigned int layer_nr);
@@ -172,9 +170,7 @@ public:
int getNozzleSize(int extruder_nr);
Point getExtruderOffset(int id);
std::string getMaterialGUID(int extruder_nr); //!< returns the GUID of the material used for the nozzle with id \p extruder_nr
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
void setFlavor(EGCodeFlavor flavor);
+95 -160
Ver Arquivo
@@ -22,49 +22,6 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
return *this;
}
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
: extruder(extruder)
, required_temp(-1)
, start_position(start_position)
, layer_nr(layer_nr)
, layer_thickness(layer_thickness)
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
, retraction_config(retraction_config)
, extrudeSpeedFactor(1.0)
, travelSpeedFactor(1.0)
, extraTime(0.0)
, totalPrintTime(0)
{
}
void ExtruderPlan::setExtrudeSpeedFactor(double speedFactor)
{
this->extrudeSpeedFactor = speedFactor;
}
double ExtruderPlan::getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void ExtruderPlan::setTravelSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1.0;
this->travelSpeedFactor = speedFactor;
}
double ExtruderPlan::getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void ExtruderPlan::setFanSpeed(double _fan_speed)
{
fan_speed = _fan_speed;
}
double ExtruderPlan::getFanSpeed()
{
return fan_speed;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
@@ -89,7 +46,7 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, bool is_inside_mesh, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, z(z)
@@ -99,13 +56,17 @@ GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int
, last_extruder_previous_layer(current_extruder)
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(current_extruder))
, comb_boundary_inside(computeCombBoundaryInside(combing_mode))
, fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder)
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
{
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
extruder_plans.emplace_back(current_extruder);
comb = nullptr;
was_inside = is_inside_mesh;
is_inside = false; // assumes the next move will not be to inside a layer part (overwritten just before going into a layer part)
setExtrudeSpeedFactor(1.0);
setTravelSpeedFactor(1.0);
extraTime = 0.0;
totalPrintTime = 0.0;
if (combing_mode != CombingMode::OFF)
{
comb = new Comb(storage, layer_nr, comb_boundary_inside, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
@@ -145,7 +106,7 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
}
else
{
Polygons comb_boundary;
Polygons layer_walls;
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
@@ -153,7 +114,7 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
{
for (SliceLayerPart& part : layer.parts)
{
comb_boundary.add(part.infill_area);
layer_walls.add(part.infill_area);
}
}
else
@@ -162,10 +123,10 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
{
continue;
}
layer.getSecondOrInnermostWalls(comb_boundary);
layer.getSecondOrInnermostWalls(layer_walls);
}
}
return comb_boundary;
return layer_walls;
}
}
@@ -202,7 +163,7 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
extruder_plans.emplace_back(extruder);
}
last_planned_extruder_setting_base = storage.meshgroup->getExtruderTrain(extruder);
@@ -340,6 +301,10 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
path = getLatestPathWithConfig(&storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
}
#ifdef ASSERT_INSANE_OUTPUT
assert(p != no_point && p != Point(0, 0) && "Uninitialised travel location!");
#endif //ASSERT_INSANE_OUTPUT
path->points.push_back(p);
lastPosition = p;
}
@@ -419,8 +384,7 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
}
}
void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrudeTime)
void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrudeTime)
{
double totalTime = travelTime + extrudeTime;
if (totalTime < minTime && extrudeTime > 0.0)
@@ -429,13 +393,16 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
if (minExtrudeTime < 1)
minExtrudeTime = 1;
double factor = extrudeTime / minExtrudeTime;
for (GCodePath& path : paths)
for(ExtruderPlan& extr_plan : extruder_plans)
{
if (path.isTravelPath())
continue;
double speed = path.config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path.config->getSpeed();
for (GCodePath& path : extr_plan.paths)
{
if (path.isTravelPath())
continue;
double speed = path.config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path.config->getSpeed();
}
}
//Only slow down for the minimal time if that will be slower.
@@ -452,10 +419,13 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
double inv_factor = 1.0 / factor; // cause multiplication is faster than division
// Adjust stored naive time estimates
estimates.extrude_time *= inv_factor;
for (GCodePath& path : paths)
for(ExtruderPlan& extr_plan : extruder_plans)
{
path.estimates.extrude_time *= inv_factor;
extr_plan.estimates.extrude_time *= inv_factor;
for (GCodePath& path : extr_plan.paths)
{
path.estimates.extrude_time *= inv_factor;
}
}
if (minTime - (extrudeTime * inv_factor) - travelTime > 0.1)
@@ -463,75 +433,79 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
this->extraTime = minTime - (extrudeTime * inv_factor) - travelTime;
}
this->totalPrintTime = (extrudeTime * inv_factor) + travelTime;
}else{
this->totalPrintTime = totalTime;
}
}
TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
{
TimeMaterialEstimates ret;
Point p0 = start_position;
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
for (GCodePath& path : paths)
for(ExtruderPlan& extr_plan : extruder_plans)
{
bool is_extrusion_path = false;
double* path_time_estimate;
double& material_estimate = path.estimates.material;
if (!path.isTravelPath())
for (GCodePath& path : extr_plan.paths)
{
is_extrusion_path = true;
path_time_estimate = &path.estimates.extrude_time;
}
else
{
if (path.retract)
bool is_extrusion_path = false;
double* path_time_estimate;
double& material_estimate = path.estimates.material;
if (!path.isTravelPath())
{
path_time_estimate = &path.estimates.retracted_travel_time;
is_extrusion_path = true;
path_time_estimate = &path.estimates.extrude_time;
}
else
{
path_time_estimate = &path.estimates.unretracted_travel_time;
}
if (path.retract != was_retracted)
{ // handle retraction times
double retract_unretract_time;
if (path.retract)
{
retract_unretract_time = retraction_config.distance / retraction_config.speed;
path_time_estimate = &path.estimates.retracted_travel_time;
}
else
{
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
path_time_estimate = &path.estimates.unretracted_travel_time;
}
if (path.retract != was_retracted)
{ // handle retraction times
double retract_unretract_time;
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extr_plan.extruder];
if (path.retract)
{
retract_unretract_time = retraction_config.distance / retraction_config.speed;
}
else
{
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
}
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
}
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
}
}
for(Point& p1 : path.points)
{
double length = vSizeMM(p0 - p1);
if (is_extrusion_path)
for(Point& p1 : path.points)
{
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
double length = vSizeMM(p0 - p1);
if (is_extrusion_path)
{
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
}
double thisTime = length / path.config->getSpeed();
*path_time_estimate += thisTime;
p0 = p1;
}
double thisTime = length / path.config->getSpeed();
*path_time_estimate += thisTime;
p0 = p1;
extr_plan.estimates += path.estimates;
}
estimates += path.estimates;
ret += extr_plan.estimates;
}
return estimates;
return ret;
}
void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time)
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
{
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
totalPrintTime = estimates.getTotalTime();
if (force_minimal_layer_time)
{
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
}
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
/*
min layer time
:
@@ -549,11 +523,11 @@ void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_t
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
fan_speed = fsml.cool_fan_speed_min;
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
if (force_minimal_layer_time && totalLayerTime < fsml.cool_min_layer_time)
if (totalLayerTime < fsml.cool_min_layer_time)
{
fan_speed = fsml.cool_fan_speed_max;
}
else if (force_minimal_layer_time && totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
double fan_speed_diff = fsml.cool_fan_speed_max - fsml.cool_fan_speed_min;
@@ -582,27 +556,6 @@ void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_t
}
}
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
{
TimeMaterialEstimates ret;
for (ExtruderPlan& extruder_plan : extruder_plans)
{
ret += extruder_plan.computeNaiveTimeEstimates();
}
return ret;
}
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
{
for (unsigned int extr_plan_idx = 0; extr_plan_idx < extruder_plans.size(); extr_plan_idx++)
{
ExtruderPlan& extruder_plan = extruder_plans[extr_plan_idx];
bool force_minimal_layer_time = extr_plan_idx == extruder_plans.size() - 1;
extruder_plan.processFanSpeedAndMinimalLayerTime(force_minimal_layer_time);
}
}
void GCodePlanner::writeGCode(GCodeExport& gcode)
{
@@ -614,6 +567,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
gcode.setZ(z);
gcode.writeFanCommand(fan_speed);
GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
@@ -621,8 +575,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
for(unsigned int extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++)
{
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
if (extruder != extruder_plan.extruder)
{
@@ -637,7 +589,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
// prime extruder if it hadn't been used yet
gcode.writePrimeTrain(storage.meshgroup->getExtruderTrain(extruder)->getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&retraction_config);
if (extruder_plan.prev_extruder_standby_temp)
{ // turn off previous extruder
@@ -645,9 +596,10 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
gcode.writeTemperatureCommand(prev_extruder, *extruder_plan.prev_extruder_standby_temp, wait);
}
}
gcode.writeFanCommand(extruder_plan.getFanSpeed());
std::vector<GCodePath>& paths = extruder_plan.paths;
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
return a.path_idx < b.path_idx;
} );
@@ -683,9 +635,9 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
double speed = path.config->getSpeed();
if (path.isTravelPath())// Only apply the extrudeSpeed to extrusion moves
speed *= extruder_plan.getTravelSpeedFactor();
speed *= getTravelSpeedFactor();
else
speed *= extruder_plan.getExtrudeSpeedFactor();
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO
@@ -778,40 +730,24 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
}
path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path
}
} // paths for this extruder /\ .
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
if (train->getSettingBoolean("cool_lift_head") && extruder_plan.extraTime > 0.0)
{
gcode.writeComment("Small layer, adding delay");
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
gcode.writeRetraction(&retraction_config);
if (extruder_plan_idx == extruder_plans.size() - 1 || !train->getSettingBoolean("machine_extruder_end_pos_abs"))
{ // only move the head if it's the last extruder plan; otherwise it's already at the switching bay area
// or do it anyway when we switch extruder in-place
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), storage.travel_config_per_extruder[extruder].getSpeed(), 0);
// TODO: is this safe?! wouldn't the head move into the sides then?!
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config_per_extruder[extruder].getSpeed(), 0);
}
gcode.writeDelay(extruder_plan.extraTime);
}
extruder_plan.handleAllRemainingInserts(gcode);
} // extruder plans /\ .
}
gcode.updateTotalPrintTime();
}
void GCodePlanner::overrideFanSpeeds(double speed)
{
for (ExtruderPlan& extruder_plan : extruder_plans)
if (storage.getSettingBoolean("cool_lift_head") && extraTime > 0.0)
{
extruder_plan.setFanSpeed(speed);
gcode.writeComment("Small layer, adding delay");
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
gcode.writeRetraction(&retraction_config);
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), storage.travel_config_per_extruder[extruder].getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config_per_extruder[extruder].getSpeed(), 0); // TODO: is this safe?! wouldn't the head move into the sides then?!
gcode.writeDelay(extraTime);
}
}
void GCodePlanner::completeConfigs()
{
storage.support_config.setLayerHeight(layer_thickness);
@@ -918,8 +854,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
{
return false;
}
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
std::vector<GCodePath>& paths = extruder_plan.paths;
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
GCodePath& path = paths[path_idx];
if (path_idx + 1 >= paths.size()
||
@@ -934,7 +869,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
double extrude_speed = path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(); // travel speed
double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed
int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
+43 -109
Ver Arquivo
@@ -50,15 +50,14 @@ struct NozzleTempInsert
}
};
class ExtruderPlan; // forward declaration so that TimeMaterialEstimates can be a friend
class GCodePlanner; // forward declaration so that TimeMaterialEstimates can be a friend
/*!
* Time and material estimates for a portion of paths, e.g. layer, extruder plan, path.
*/
class TimeMaterialEstimates
{
friend class ExtruderPlan; // cause there the naive estimates are calculated
friend class GCodePlanner;
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
@@ -263,9 +262,6 @@ public:
}
};
class GCodePlanner; // forward declaration so that ExtruderPlan can be a friend
class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend
/*!
* An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train.
*
@@ -273,9 +269,7 @@ class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a frie
*/
class ExtruderPlan
{
friend class GCodePlanner; // TODO: GCodePlanner still does a lot which should actually be handled in this class.
friend class LayerPlanBuffer; // TODO: LayerPlanBuffer handles paths directly
protected:
public:
std::vector<GCodePath> paths; //!< The paths planned for this extruder
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
@@ -284,16 +278,19 @@ protected:
std::optional<double> prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder.
TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan.
public:
/*!
* Simple contructor.
*
* \warning Doesn't set the required temperature yet.
*
* \param extruder The extruder number for which this object is a plan.
* \param start_position The position the head is when this extruder plan starts
*/
ExtruderPlan(int extruder, Point start_position, int layer_nr, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
ExtruderPlan(int extruder)
: extruder(extruder)
, required_temp(-1)
{
}
/*!
* Add a new Insert, constructed with the given arguments
@@ -342,95 +339,6 @@ public:
inserts.pop_front();
}
}
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*
* \param force_minimal_layer_time Whether we should apply speed changes and perhaps a head lift in order to meet the minimal layer time
*/
void processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time);
/*!
* Set the extrude speed factor. This is used for printing slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the extrusion move speed
*/
void setExtrudeSpeedFactor(double speedFactor);
/*!
* Get the extrude speed factor. This is used for printing slower than normal.
*
* \return The factor by which to alter the extrusion move speed
*/
double getExtrudeSpeedFactor();
/*!
* Set the travel speed factor. This is used for performing non-extrusion travel moves slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the non-extrusion move speed
*/
void setTravelSpeedFactor(double speedFactor);
/*!
* Get the travel speed factor. This is used for travelling slower than normal.
*
* Limited to at most 1.0
*
* \return The factor by which to alter the non-extrusion move speed
*/
double getTravelSpeedFactor();
/*!
* Get the fan speed computed for this extruder plan
*
* \warning assumes ExtruderPlan::processFanSpeedAndMinimalLayerTime has already been called
*
* \return The fan speed computed in processFanSpeedAndMinimalLayerTime
*/
double getFanSpeed();
protected:
Point start_position; //!< The position the print head was at at the start of this extruder plan
int layer_nr; //!< The layer number at which we are currently printing.
int layer_thickness; //!< The thickness of this layer in Z-direction
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
const RetractionConfig& retraction_config; //!< The retraction settings for the extruder of this plan
double extrudeSpeedFactor; //!< The factor by which to alter the extrusion move speed
double travelSpeedFactor; //!< The factor by which to alter the non-extrusion move speed
double extraTime; //!< Extra waiting time at the and of this extruder plan, so that the filament can cool
double totalPrintTime; //!< The total naive time estimate for this extruder plan
double fan_speed; //!< The fan speed to be used during this extruder plan
/*!
* Set the fan speed to be used while printing this extruder plan
*
* \param fan_speed The speed for the fan
*/
void setFanSpeed(double fan_speed);
/*!
* Force the minimal layer time to hold by slowing down and lifting the head if required.
*
*/
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
};
class LayerPlanBuffer; // forward declaration to prevent circular dependency
@@ -470,8 +378,15 @@ private:
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
Comb* comb;
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings;
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
double extrudeSpeedFactor;
double travelSpeedFactor;
double fan_speed;
double extraTime;
double totalPrintTime;
private:
/*!
@@ -500,16 +415,14 @@ private:
public:
/*!
*
* \param fan_speed_layer_time_settings_per_extruder The fan speed and layer time settings for each extruder.
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
void overrideFanSpeeds(double speed);
/*!
* Get the settings base of the last extruder planned.
* \return the settings base of the last extruder planned.
@@ -575,7 +488,28 @@ public:
return extruder_plans.back().extruder;
}
void setExtrudeSpeedFactor(double speedFactor)
{
this->extrudeSpeedFactor = speedFactor;
}
double getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void setTravelSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1.0;
this->travelSpeedFactor = speedFactor;
}
double getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void setFanSpeed(double _fan_speed)
{
fan_speed = _fan_speed;
}
/*!
* Add a travel path to a certain point, retract if needed and when avoiding boundary crossings:
@@ -643,15 +577,15 @@ public:
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \warning This function recomputes values which are already computed if you've called processFanSpeedAndMinimalLayerTime
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
/*!
* Write the planned paths to gcode
*
+27 -76
Ver Arquivo
@@ -6,17 +6,6 @@
namespace cura {
int Infill::computeScanSegmentIdx(int x, int line_width)
{
if (x < 0)
{
return (x + 1) / line_width - 1;
// - 1 because -1 belongs to scansegment -1
// + 1 because -line_width belongs to scansegment -1
}
return x / line_width;
}
void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
{
if (in_outline.size() == 0) return;
@@ -29,13 +18,7 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
generateGridInfill(result_lines);
break;
case EFillMethod::LINES:
generateLineInfill(result_lines, line_distance, fill_angle, 0);
break;
case EFillMethod::CUBIC:
generateCubicInfill(result_lines);
break;
case EFillMethod::TETRAHEDRAL:
generateTetrahedralInfill(result_lines);
generateLineInfill(result_lines, line_distance, fill_angle);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(result_lines);
@@ -66,38 +49,18 @@ void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int in
void Infill::generateGridInfill(Polygons& result)
{
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 90, 0);
}
void Infill::generateCubicInfill(Polygons& result)
{
int64_t shift = one_over_sqrt_2 * z;
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle + 120, shift);
generateLineInfill(result, line_distance, fill_angle + 240, shift);
}
void Infill::generateTetrahedralInfill(Polygons& result)
{
int shift = int64_t(one_over_sqrt_2 * z) % line_distance;
shift = std::min(shift, line_distance - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, line_distance / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle, -shift);
generateLineInfill(result, line_distance, fill_angle + 90, shift);
generateLineInfill(result, line_distance, fill_angle + 90, -shift);
generateLineInfill(result, line_distance, fill_angle);
generateLineInfill(result, line_distance, fill_angle + 90);
}
void Infill::generateTriangleInfill(Polygons& result)
{
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 60, 0);
generateLineInfill(result, line_distance, fill_angle + 120, 0);
generateLineInfill(result, line_distance, fill_angle);
generateLineInfill(result, line_distance, fill_angle + 60);
generateLineInfill(result, line_distance, fill_angle + 120);
}
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t shift)
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
{
auto addLine = [&](Point from, Point to)
{
@@ -121,7 +84,7 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * line_distance + shift; x < boundary.max.X; x += line_distance)
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
{
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
@@ -137,17 +100,19 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
}
}
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t shift)
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
{
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
generateLinearBasedInfill(outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags, shift);
bool safe_outline_offset = false;
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
bool safe_outline_offset = true;
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
@@ -155,18 +120,18 @@ void Infill::generateZigZagInfill(Polygons& result, const int line_distance, con
if (connected_zigzags)
{
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
else
{
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
@@ -193,7 +158,7 @@ void Infill::generateZigZagInfill(Polygons& result, const int line_distance, con
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
*/
void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift)
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
if (line_distance == 0)
{
@@ -203,9 +168,7 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
{
return;
}
int shift = extra_shift + this->shift;
Polygons outline;
if (outline_offset != 0)
{
@@ -225,19 +188,10 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
outline.applyMatrix(rotation_matrix);
if (shift < 0)
{
shift = line_distance - (-shift) % line_distance;
}
else
{
shift = shift % line_distance;
}
AABB boundary(outline);
int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance);
int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx;
int scanline_min_idx = boundary.min.X / line_distance;
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
@@ -263,29 +217,26 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
continue;
}
int scanline_idx0;
int scanline_idx1;
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
int direction = 1;
if (p0.X < p1.X)
if (p0.X > p1.X)
{
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >)
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
}
else
{
direction = -1;
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >)
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
{
int x = scanline_idx * line_distance + shift;
int x = scanline_idx * line_distance;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!");
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
@@ -305,7 +256,7 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift);
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
}
}//namespace cura
+6 -35
Ver Arquivo
@@ -27,14 +27,11 @@ class Infill
int line_distance; //!< The distance between two infill lines / polygons
int infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
int64_t z; //!< height of the layer for which we generate infill
int64_t shift; //!< shift of the scanlines in the direction perpendicular to the fill_angle
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0)
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, int64_t z, int64_t shift, bool connected_zigzags = false, bool use_endpieces = false)
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
@@ -42,8 +39,6 @@ public:
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, z(z)
, shift(shift)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
@@ -57,17 +52,7 @@ public:
void generate(Polygons& result_polygons, Polygons& result_lines);
private:
/*!
* Function which returns the scanline_idx for a given x coordinate
*
* For negative \p x this is different from simple division.
*
* \warning \p line_distance is assumed to be positive
*
* \param x the point to get the scansegment index for
* \param line_distance the width of the scan segments
*/
static inline int computeScanSegmentIdx(int x, int line_distance);
/*!
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
@@ -82,18 +67,6 @@ private:
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a shifting triangular grid of infill lines, which combine with consecutive layers into a cubic pattern
* \param result (output) The resulting lines
*/
void generateCubicInfill(Polygons& result);
/*!
* Generate a double shifting square grid of infill lines, which combine with consecutive layers into a tetrahedral pattern
* \param result (output) The resulting lines
*/
void generateTetrahedralInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
@@ -108,9 +81,8 @@ private:
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
* \param total_shift total shift of the scanlines in the direction perpendicular to the fill_angle.
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t total_shift);
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
@@ -121,9 +93,8 @@ private:
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t extra_shift);
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
@@ -134,14 +105,14 @@ private:
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift);
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
/*!
*
+33 -7
Ver Arquivo
@@ -1,6 +1,8 @@
#include "mesh.h"
#include "utils/logoutput.h"
// #define LOG_MESH_ERRORS
namespace cura
{
@@ -53,10 +55,9 @@ void Mesh::finish()
for(unsigned int i=0; i<faces.size(); i++)
{
MeshFace& face = faces[i];
// faces are connected via the outside
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i, face.vertex_index[2]);
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i, face.vertex_index[0]);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i, face.vertex_index[1]);
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i); // faces are connected via the outside
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i);
}
}
@@ -117,13 +118,17 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
*/
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const
{
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
for(int f : vertices[idx0].connected_faces) // search through all faces connected to the first vertex and find those that are also connected to the second
{
if (f == notFaceIdx)
{
for (int i = 0; i<3; i++) // find the vertex which is not idx0 or idx1
if (faces[f].vertex_index[i] != idx0 && faces[f].vertex_index[i] != idx1)
notFaceVertexIdx = faces[f].vertex_index[i];
continue;
}
if ( faces[f].vertex_index[0] == idx1 // && faces[f].vertex_index[1] == idx0 // next face should have the right direction!
@@ -132,12 +137,27 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
) candidateFaces.push_back(f);
}
if (candidateFaces.size() == 0) { cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() == 0)
{
#ifdef LOG_MESH_ERRORS
cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
#endif
return -1;
}
if (candidateFaces.size() == 1) { return candidateFaces[0]; }
if (notFaceVertexIdx < 0)
{
#ifdef LOG_MESH_ERRORS
cura::logError("Couldn't find third point on face %i.\n", notFaceIdx);
#endif
return -1;
}
#ifdef LOG_MESH_ERRORS
if (candidateFaces.size() % 2 == 0) cura::log("Warning! Edge with uneven number of faces connecting it!(%i)\n", candidateFaces.size()+1);
#endif
FPoint3 vn = vertices[idx1].p - vertices[idx0].p;
FPoint3 n = vn / vn.vSize(); // the normal of the plane in which all normals of faces connected to the edge lie => the normalized normal
@@ -146,7 +166,9 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
// the normals below are abnormally directed! : these normals all point counterclockwise (viewed from idx1 to idx0) from the face, irrespective of the direction of the face.
FPoint3 n0 = FPoint3(vertices[notFaceVertexIdx].p - vertices[idx0].p).cross(v0);
#ifdef LOG_MESH_ERRORS
if (n0.vSize() <= 0) cura::log("Warning! Face %i has zero area!", notFaceIdx);
#endif
double smallestAngle = 1000; // more then 2 PI (impossible angle)
int bestIdx = -1;
@@ -170,7 +192,9 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
if (angle == 0)
{
#ifdef LOG_MESH_ERRORS
cura::log("Warning! Overlapping faces: face %i and face %i.\n", notFaceIdx, candidateFace);
#endif
std::cerr<< n.vSize() <<"; "<<n1.vSize()<<";"<<n0.vSize() <<std::endl;
}
if (angle < smallestAngle)
@@ -179,7 +203,9 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
bestIdx = candidateFace;
}
}
#ifdef LOG_MESH_ERRORS
if (bestIdx < 0) cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
#endif
return bestIdx;
}
+3 -11
Ver Arquivo
@@ -87,19 +87,11 @@ public:
private:
int findIndexOfVertex(const Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
/*!
* Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
*
* In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*
* \param idx0 the first vertex index
* \param idx1 the second vertex index
* \param notFaceIdx the index of a face which shouldn't be returned
* \param notFaceVertexIdx should be the third vertex of face \p notFaceIdx.
* \return the face index of a face sharing the edge from \p idx0 to \p idx1
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*/
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const;
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const;
};
}//namespace cura
+1 -3
Ver Arquivo
@@ -52,15 +52,13 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
{
continue;
}
AABB3D aabb(volume->mesh->getAABB());
aabb.expandXY(overlap); // expand to account for the case where two models and their bounding boxes are adjacent along the X or Y-direction
for (unsigned int layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++)
{
Polygons all_other_volumes;
for (Slicer* other_volume : volumes)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| !other_volume->mesh->getAABB().hit(aabb)
|| !other_volume->mesh->getAABB().hit(volume->mesh->getAABB())
)
{
continue;
-1
Ver Arquivo
@@ -162,7 +162,6 @@ int SettingRegistry::loadJSONsettings(std::string filename, SettingsBase* settin
bool found = getDefinitionFile(json_document["inherits"].GetString(), child_filename);
if (!found)
{
cura::logError("Inherited JSON file \"%s\" not found\n", json_document["inherits"].GetString());
return -1;
}
err = loadJSONsettings(child_filename, settings_base, warn_base_file_duplicates); // load child first
+7 -26
Ver Arquivo
@@ -190,37 +190,22 @@ FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) co
return ret; //Empty at this point.
}
std::regex regex("(\\[([^,\\[]*),([^,\\]]*)\\])");
// match with:
// - the last opening bracket '['
// - then a bunch of characters until the first comma
// - a comma
// - a bunch of cahracters until the first closing bracket ']'
// matches with any substring which looks like "[ 124.512 , 124.1 ]"
// default constructor = end-of-sequence:
std::regex_token_iterator<std::string::iterator> rend;
int submatches[] = { 1, 2, 3 }; // match whole pair, first number and second number of a pair
std::regex_token_iterator<std::string::iterator> match_iter(value_string.begin(), value_string.end(), regex, submatches);
while (match_iter != rend)
std::regex_token_iterator<std::string::iterator> a(value_string.begin(), value_string.end(), regex, submatches);
while (a != rend)
{
match_iter++; // match the whole pair
if (match_iter == rend)
a++; // match the whole pair
if (a == rend)
{
break;
}
std::string first_substring = *match_iter++;
std::string second_substring = *match_iter++;
try
{
double first = std::stod(first_substring);
double second = std::stod(second_substring);
ret.data.emplace_back(first, second);
}
catch (const std::invalid_argument& e)
{
logError("Couldn't read 2D graph element [%s,%s] in setting '%s'. Ignored.\n", first_substring.c_str(), second_substring.c_str(), key.c_str());
}
double first = std::stod(*a++);
double second = std::stod(*a++);
ret.data.emplace_back(first, second);
}
return ret;
}
@@ -251,10 +236,6 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "cubic")
return EFillMethod::CUBIC;
if (value == "tetrahedral")
return EFillMethod::TETRAHEDRAL;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
-2
Ver Arquivo
@@ -102,8 +102,6 @@ enum class EFillMethod
{
LINES,
GRID,
CUBIC,
TETRAHEDRAL,
TRIANGLES,
CONCENTRIC,
ZIG_ZAG,
+22 -120
Ver Arquivo
@@ -1,5 +1,4 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <cmath> // std::ceil
#include "skin.h"
#include "utils/polygonUtils.h"
@@ -36,7 +35,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_
if (int(part.insets.size()) < wall_line_count)
{
continue; // the last wall is not present, the part should only get inter perimeter gaps, but no skin.
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin.
}
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
@@ -125,7 +124,7 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
skin_part.insets[i] = skin_part.insets[i - 1].offset(-extrusionWidth);
}
// optimize polygons: remove unnecessary verts
// optimize polygons: remove unnnecesary verts
skin_part.insets[i].simplify();
if (skin_part.insets[i].size() < 1)
{
@@ -144,6 +143,7 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extr
{
if (int(part.insets.size()) < wall_line_count)
{
part.infill_area_per_combine.emplace_back(); // put empty polygons as initial infill_per_combine
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
@@ -161,100 +161,18 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extr
infill.removeSmallAreas(MIN_AREA_SIZE);
part.infill_area = infill.offset(infill_skin_overlap);
}
}
void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh, unsigned int gradual_infill_step_height, unsigned int max_infill_steps)
{
// no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density]
float layer_skip_count = 8; // skip every so many layers as to ignore small gaps in the model making computation more easy
if (!mesh.getSettingBoolean("skin_no_small_gaps_heuristic"))
{
layer_skip_count = 1;
}
unsigned int gradual_infill_step_layer_count = gradual_infill_step_height / mesh.getSettingInMicrons("layer_height"); // The difference in layer count between consecutive density infill areas
// make gradual_infill_step_height divisable by layer_skip_count
float n_skip_steps_per_gradual_step = std::max(1.0f, std::ceil(gradual_infill_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_infill_step_layer_count
layer_skip_count = gradual_infill_step_layer_count / n_skip_steps_per_gradual_step;
size_t min_layer = mesh.getSettingAsCount("bottom_layers");
size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
for (size_t layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{ // loop also over layers which don't contain infill cause of bottom_ and top_layer to initialize their infill_area_per_combine_per_density
SliceLayer& layer = mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
assert(part.infill_area_per_combine_per_density.size() == 0 && "infill_area_per_combine_per_density is supposed to be uninitialized");
const Polygons& infill_area = part.getOwnInfillArea();
if (infill_area.size() == 0 || layer_idx < min_layer || layer_idx > max_layer)
{ // initialize infill_area_per_combine_per_density empty
part.infill_area_per_combine_per_density.emplace_back(); // create a new infill_area_per_combine
part.infill_area_per_combine_per_density.back().emplace_back(); // put empty infill area in the newly constructed infill_area_per_combine
// note: no need to copy part.infill_area, cause it's the empty vector anyway
continue;
}
Polygons less_dense_infill = infill_area; // one step less dense with each infill_step
for (unsigned int infill_step = 0; infill_step < max_infill_steps; infill_step++)
{
size_t min_layer = layer_idx + infill_step * gradual_infill_step_layer_count + layer_skip_count;
size_t max_layer = layer_idx + (infill_step + 1) * gradual_infill_step_layer_count;
for (float upper_layer_idx = min_layer; static_cast<unsigned int>(upper_layer_idx) <= max_layer; upper_layer_idx += layer_skip_count)
{
if (static_cast<unsigned int>(upper_layer_idx) >= mesh.layers.size())
{
less_dense_infill.clear();
break;
}
SliceLayer& upper_layer = mesh.layers[static_cast<unsigned int>(upper_layer_idx)];
Polygons relevent_upper_polygons;
for (SliceLayerPart& upper_layer_part : upper_layer.parts)
{
if (!upper_layer_part.boundaryBox.hit(part.boundaryBox))
{
continue;
}
relevent_upper_polygons.add(upper_layer_part.getOwnInfillArea());
}
less_dense_infill = less_dense_infill.intersection(relevent_upper_polygons);
}
if (less_dense_infill.size() == 0)
{
break;
}
// add new infill_area_per_combine for the current density
part.infill_area_per_combine_per_density.emplace_back();
std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back();
const Polygons more_dense_infill = infill_area.difference(less_dense_infill);
infill_area_per_combine_current_density.push_back(more_dense_infill);
if (less_dense_infill.size() == 0)
{
break;
}
}
part.infill_area_per_combine_per_density.emplace_back();
std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back();
infill_area_per_combine_current_density.push_back(infill_area);
part.infill_area_own = nullptr; // clear infill_area_own, it's not needed any more.
assert(part.infill_area_per_combine_per_density.size() != 0 && "infill_area_per_combine_per_density is now initialized");
}
part.infill_area_per_combine.push_back(part.infill_area);
}
}
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount)
{
if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
// Note that *all* parts should have an [infill_area_per_combine] with one element in it, which up till now only contains the exact same polygons as [infill].
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
{
return;
}
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
{
return;
}
@@ -269,46 +187,30 @@ void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount)
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
{
SliceLayer* layer = &mesh.layers[layer_idx];
for(unsigned int combine_count_here = 1; combine_count_here < amount; combine_count_here++)
for(unsigned int n = 1;n < amount;n++)
{
if(layer_idx < combine_count_here)
if(layer_idx < n)
{
break;
}
size_t lower_layer_idx = layer_idx - combine_count_here;
if (lower_layer_idx < min_layer)
SliceLayer* layer2 = &mesh.layers[layer_idx - n];
for(SliceLayerPart& part : layer->parts)
{
break;
}
SliceLayer* lower_layer = &mesh.layers[lower_layer_idx];
for (SliceLayerPart& part : layer->parts)
{
for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++)
{ // go over each density of gradual infill (these density areas overlap!)
std::vector<Polygons>& infill_area_per_combine = part.infill_area_per_combine_per_density[density_idx];
Polygons result;
for (SliceLayerPart& lower_layer_part : lower_layer->parts)
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
{
if(part.boundaryBox.hit(part2.boundaryBox))
{
if (part.boundaryBox.hit(lower_layer_part.boundaryBox))
{
Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.infill_area).offset(-200).offset(200);
result.add(intersection); // add area to be thickened
infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here
if (density_idx < lower_layer_part.infill_area_per_combine_per_density.size())
{ // only remove from *same density* areas on layer below
// If there are no same density areas, then it's ok to print them anyway
// Don't remove other density areas
unsigned int lower_density_idx = density_idx;
std::vector<Polygons>& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx];
lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (thickened) layer
}
}
Polygons intersection = part.infill_area_per_combine[n - 1].intersection(part2.infill_area_per_combine[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area_per_combine[n - 1] = part.infill_area_per_combine[n - 1].difference(intersection);
part2.infill_area_per_combine[0] = part2.infill_area_per_combine[0].difference(intersection);
}
infill_area_per_combine.push_back(result);
}
part.infill_area_per_combine.push_back(result);
}
}
}
-22
Ver Arquivo
@@ -72,28 +72,6 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extr
*/
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount);
/*!
* Class containing all skin and infill area computation functions
*/
class SkinInfillAreaComputation
{
public:
/*!
* Generate infill areas which cause a gradually less dense infill structure from top to bottom.
*
* The areas generated overlap, so that more dense infill adds on to less dense infill.
* That way you don't have infill lines which are broken when they cross a border between separated infill areas - if they would be as such.
*
* This function also guarantees that the SliceLayerPart::infill_area_per_combine_per_density is initialized with at least one item.
* The last item in the list will be equal to the infill_area after this function.
*
* \param gradual_infill_step_height // The height difference between consecutive density infill areas
* \param max_infill_steps the maximum exponent of division of infill density. At 5 the least dense infill will be 2^4 * infill_line_distance i.e. one 16th as dense
*/
static void generateGradualInfill(SliceMeshStorage& mesh, unsigned int gradual_infill_step_height, unsigned int max_infill_steps);
};
}//namespace cura
#endif//SKIN_H
+1 -1
Ver Arquivo
@@ -40,7 +40,7 @@ void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLe
if (get_convex_hull)
{
skirt_polygons = skirt_polygons.approxConvexHull();
skirt_polygons = skirt_polygons.convexHull();
}
skirt_primary_extruder.add(skirt_polygons);
-12
Ver Arquivo
@@ -5,18 +5,6 @@
namespace cura
{
Polygons& SliceLayerPart::getOwnInfillArea()
{
if (infill_area_own)
{
return *infill_area_own;
}
else
{
return infill_area;
}
}
Polygons SliceLayer::getOutlines(bool external_polys_only) const
{
Polygons ret;
+27 -66
Ver Arquivo
@@ -3,7 +3,6 @@
#define SLICE_DATA_STORAGE_H
#include "utils/intpoint.h"
#include "utils/optional.h"
#include "utils/polygon.h"
#include "utils/NoCopy.h"
#include "utils/AABB.h"
@@ -19,11 +18,11 @@ namespace cura
* A SkinPart is a connected area designated as top and/or bottom skin.
* Surrounding each non-bridged skin area with an outline may result in better top skins.
* It's filled during FffProcessor.processSliceData(.) and used in FffProcessor.writeGCode(.) to generate the final gcode.
*/
*/
class SkinPart
{
public:
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
};
/*!
@@ -39,41 +38,8 @@ public:
Polygons print_outline; //!< An approximation to the outline of what's actually printed, based on the outer wall. Too small parts will be omitted compared to the outline.
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
/*!
* The areas inside of the mesh.
* Like SliceLayerPart::outline, this class member is not used to actually determine the feature area,
* but is used to compute the inside comb boundary.
*/
Polygons infill_area;
/*!
* The areas which need to be filled with sparse (0-99%) infill.
* Like SliceLayerPart::outline, this class member is not used to actually determine the feature area,
* but is used to compute the infill_area_per_combine_per_density.
*
* These polygons may be cleared once they have been used to generate gradual infill and/or infill combine.
*
* If these polygons are not initialized, simply use the normal infill area.
*/
std::optional<Polygons> infill_area_own;
/*!
* The areas which need to be filled with sparse (0-99%) infill for different thicknesses.
* The infill_area is an array to support thicker layers of sparse infill and areas of different infill density.
* infill_area[x][n] is infill_area of (n+1) layers thick.
*
* infill_area[0] corresponds to the most dense infill area.
* infill_area[x] will lie fully inside infill_area[x+1].
* infill_area_per_combine_per_density.back()[0] == part.infill area initially
*/
std::vector<std::vector<Polygons>> infill_area_per_combine_per_density;
/*!
* Get the infill_area_own (or when it's not instantiated: the normal infill_area)
* \see SliceLayerPart::infill_area_own
* \return the own infill area
*/
Polygons& getOwnInfillArea();
Polygons infill_area; //!< The areas which need to be filled with sparse (0-99%) infill. Like SliceLayerPart::outline, this class member is not used to actually determine the feature area, but is used to compute the infill_area_per_combine and the inside comb boundary.
std::vector<Polygons> infill_area_per_combine; //!< The areas which need to be filled with sparse (0-99%) infill for different thicknesses. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
};
/*!
@@ -87,7 +53,7 @@ public:
int printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft.
std::vector<SliceLayerPart> parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model.
Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
/*!
* Get the all outlines of all layer parts in this layer.
*
@@ -95,7 +61,7 @@ public:
* \return A collection of all the outline polygons
*/
Polygons getOutlines(bool external_polys_only = false) const;
/*!
* Get the all outlines of all layer parts in this layer.
* Add those polygons to @p result.
@@ -104,13 +70,13 @@ public:
* \param result The result: a collection of all the outline polygons
*/
void getOutlines(Polygons& result, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* \return The collection of all polygons thus obtained
*/
Polygons getSecondOrInnermostWalls() const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* Add those polygons to @p result.
@@ -131,9 +97,9 @@ class SupportStorage
{
public:
bool generated; //!< whether generateSupportGrid(.) has completed (successfully)
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
std::vector<SupportLayer> supportLayers;
SupportStorage() : generated(false), layer_nr_max_filled_layer(-1) { }
@@ -146,21 +112,16 @@ class SliceMeshStorage : public SettingsMessenger // passes on settings from a M
public:
std::vector<SliceLayer> layers;
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content (modified while infill meshes are processed)
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
GCodePathConfig inset0_config;
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
SliceMeshStorage(SettingsBaseVirtual* settings, unsigned int slice_layer_count)
: SettingsMessenger(settings)
, layer_nr_max_filled_layer(0)
, inset0_config(PrintFeatureType::OuterWall)
, insetX_config(PrintFeatureType::InnerWall)
, skin_config(PrintFeatureType::Skin)
SliceMeshStorage(SettingsBaseVirtual* settings)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(PrintFeatureType::OuterWall), insetX_config(PrintFeatureType::InnerWall), skin_config(PrintFeatureType::Skin)
{
layers.reserve(slice_layer_count);
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(PrintFeatureType::Infill);
@@ -171,10 +132,10 @@ class SliceDataStorage : public SettingsMessenger, NoCopy
{
public:
MeshGroup* meshgroup; // needed to pass on the per extruder settings.. (TODO: put this somewhere else? Put the per object settings here directly, or a pointer only to the per object settings.)
Point3 model_size, model_min, model_max;
std::vector<SliceMeshStorage> meshes;
std::vector<RetractionConfig> retraction_config_per_extruder; //!< Retraction config per extruder.
std::vector<RetractionConfig> extruder_switch_retraction_config_per_extruder; //!< Retraction config per extruder for when performing an extruder switch
@@ -182,26 +143,26 @@ public:
std::vector<GCodePathConfig> skirt_config; //!< config for skirt per extruder
std::vector<CoastingConfig> coasting_config; //!< coasting config per extruder
GCodePathConfig raft_base_config;
GCodePathConfig raft_interface_config;
GCodePathConfig raft_surface_config;
GCodePathConfig support_config;
GCodePathConfig support_roof_config;
SupportStorage support;
Polygons skirt[MAX_EXTRUDERS]; //!< Skirt polygons per extruder, ordered from inner to outer polygons
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
int max_object_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder
PrimeTower primeTower;
std::vector<Polygons> oozeShield; //oozeShield per layer
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
Point wipePoint;
/*!
* Construct the initial retraction_config_per_extruder
*/
@@ -226,11 +187,11 @@ public:
* \param meshgroup The mesh group to load into this data storage, if any.
*/
SliceDataStorage(MeshGroup* meshgroup);
~SliceDataStorage()
{
}
/*!
* Get all outlines within a given layer.
*
@@ -239,7 +200,7 @@ public:
* \param external_polys_only whether to disregard all hole polygons
*/
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
*
+15 -5
Ver Arquivo
@@ -452,21 +452,21 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
}
Slicer::Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keep_none_closed, bool extensive_stitching)
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
: mesh(mesh)
{
assert(slice_layer_count > 0);
assert(layer_count > 0);
layers.resize(slice_layer_count);
layers.resize(layer_count);
for(int32_t layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
for(int32_t layer_nr = 0; layer_nr < layer_count; layer_nr++)
{
layers[layer_nr].z = initial + thickness * layer_nr;
}
for(unsigned int mesh_idx = 0; mesh_idx < mesh->faces.size(); mesh_idx++)
{
const MeshFace& face = mesh->faces[mesh_idx];
MeshFace& face = mesh->faces[mesh_idx];
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
@@ -514,6 +514,16 @@ Slicer::Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_cou
{
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
}
int surface_thickness = mesh->getSettingInMicrons("magic_surface_thickness");
if (surface_thickness)
{
for (SlicerLayer& layer : layers)
{
layer.polygons = layer.polygons.unionPolygons(layer.openPolylines.offsetPolyLine(surface_thickness / 2));
layer.openPolylines.clear();
}
}
}
}//namespace cura
+5 -21
Ver Arquivo
@@ -124,31 +124,15 @@ public:
const Mesh* mesh; //!< The sliced mesh
Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keepNoneClosed, bool extensiveStitching);
/*!
* Linear interpolation
*
* Get the Y of a point with X \p x in the line through (\p x0, \p y0) and (\p x1, \p y1)
*/
int64_t interpolate(int64_t x, int64_t x0, int64_t x1, int64_t y0, int64_t y1) const
{
int64_t dx_01 = x1 - x0;
int64_t num = (y1 - y0) * (x - x0);
num += num > 0 ? dx_01/2 : -dx_01/2; // add in offset to round result
int64_t y = y0 + num / dx_01;
return y;
}
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
{
SlicerSegment seg;
seg.start.X = interpolate(z, p0.z, p1.z, p0.x, p1.x);
seg.start.Y = interpolate(z, p0.z, p1.z, p0.y, p1.y);
seg.end .X = interpolate(z, p0.z, p2.z, p0.x, p2.x);
seg.end .Y = interpolate(z, p0.z, p2.z, p0.y, p2.y);
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
return seg;
}
+1 -19
Ver Arquivo
@@ -3,30 +3,12 @@
#include <string.h>
#include <algorithm>
#include "timeEstimate.h"
#include "settings/settings.h"
namespace cura
{
#define MINIMUM_PLANNER_SPEED 0.05// (mm/sec)
void TimeEstimateCalculator::setFirmwareDefaults(const SettingsBaseVirtual* settings_base)
{
max_feedrate[X_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_feedrate_x");
max_feedrate[Y_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_feedrate_y");
max_feedrate[Z_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_feedrate_z");
max_feedrate[E_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_feedrate_e");
max_acceleration[X_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_acceleration_x");
max_acceleration[Y_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_acceleration_y");
max_acceleration[Z_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_acceleration_z");
max_acceleration[E_AXIS] = settings_base->getSettingInMillimetersPerSecond("machine_max_acceleration_e");
max_xy_jerk = settings_base->getSettingInMillimetersPerSecond("machine_max_jerk_xy");
max_z_jerk = settings_base->getSettingInMillimetersPerSecond("machine_max_jerk_z");
max_e_jerk = settings_base->getSettingInMillimetersPerSecond("machine_max_jerk_e");
minimumfeedrate = settings_base->getSettingInMillimetersPerSecond("machine_minimum_feedrate");
acceleration = settings_base->getSettingInMillimetersPerSecond("machine_acceleration");
}
template<typename T> const T square(const T& a) { return a * a; }
void TimeEstimateCalculator::setPosition(Position newPos)
+1 -9
Ver Arquivo
@@ -6,9 +6,7 @@
namespace cura
{
class SettingsBaseVirtual;
/*!
* The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
* Some of this code has been adapted from the Marlin sources.
@@ -73,12 +71,6 @@ private:
std::vector<Block> blocks;
public:
/*!
* Set the movement configuration of the firmware.
*
* \param settings_base Where to get the settings from
*/
void setFirmwareDefaults(const SettingsBaseVirtual* settings_base);
void setPosition(Position newPos);
void plan(Position newPos, double feedRate);
void addTime(double time);
+1 -1
Ver Arquivo
@@ -13,7 +13,7 @@ AABB::AABB()
{
}
AABB::AABB(Point&min, Point& max)
AABB::AABB(const Point&min, const Point& max)
: min(min), max(max)
{
}
+1 -1
Ver Arquivo
@@ -18,7 +18,7 @@ public:
Point min, max;
AABB(); //!< initializes with invalid min and max
AABB(Point& min, Point& max); //!< initializes with given min and max
AABB(const Point& min, const Point& max); //!< initializes with given min and max
AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons
void calculate(const Polygons& polys); //!< Calculates the aabb for the given polygons (throws away old min and max data of this aabb)
+6 -20
Ver Arquivo
@@ -15,15 +15,12 @@ AABB3D::AABB3D()
bool AABB3D::hit(const AABB3D& other) const
{
if ( max.x < other.min.y
|| min.x > other.max.y
|| max.y < other.min.y
|| min.y > other.max.y
|| max.z < other.min.z
|| min.z > other.max.z)
{
return false;
}
if (max.x < other.min.y) return false;
if (min.x > other.max.y) return false;
if (max.y < other.min.y) return false;
if (min.y > other.max.y) return false;
if (max.z < other.min.z) return false;
if (min.z > other.max.z) return false;
return true;
}
@@ -49,16 +46,5 @@ void AABB3D::offset(Point offset)
max += offset;
}
void AABB3D::expandXY(int outset)
{
min -= Point3(outset, outset, 0);
max += Point3(outset, outset, 0);
if (min.x > max.x || min.y > max.y)
{ // make this AABB3D invalid
*this = AABB3D();
}
}
}//namespace cura
-7
Ver Arquivo
@@ -49,13 +49,6 @@ struct AABB3D
* \param offset The offset with which to offset the AABB3D.
*/
void offset(Point offset);
/*!
* Offset the bounding box in the horizontal direction; outward or inward.
*
* \param outset the distance (positive or negative) to expand the bounding box outward
*/
void expandXY(int outset);
};
}//namespace cura
+71 -11
Ver Arquivo
@@ -50,7 +50,7 @@ private:
const double scale;
public:
SVG(const char* filename, AABB aabb, Point canvas_size = Point(1024 * 4, 1024 * 4))
SVG(const char* filename, AABB aabb, Point canvas_size = Point(1024, 1024))
: aabb(aabb)
, aabb_size(aabb.max - aabb.min)
, border(200,100)
@@ -63,6 +63,10 @@ public:
}
fprintf(out, "<!DOCTYPE html><html><body>\n");
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.X, canvas_size.Y);
// fprintf(out, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
// fprintf(out, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
// fprintf(out, "</marker>");
}
~SVG()
@@ -71,7 +75,7 @@ public:
fprintf(out, "</body></html>");
fclose(out);
}
/*!
* transform a point in real space to canvas space
*/
@@ -80,8 +84,46 @@ public:
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + border;
}
private:
// void _writeLines(PolygonRef polygon, Color color = Color::GRAY)
// {
// for(unsigned int n=0; n<polygon.size(); n++)
// {
// if (n == 0)
// fprintf(out, "M");
// else
// fprintf(out, "L");
// Point pf = transform(polygon[n]);
// fprintf(out, "%lli,%lli ", pf.X, pf.Y);
// }
// fprintf(out, "Z\n");
// }
public:
// void writeLines(Polygons& polygons, Color color = Color::GRAY, Color outline_color = Color::BLACK)
// {
// fprintf(out, "<g fill-rule='evenodd' style=\"fill: %s; stroke:%s;stroke-width:1\">\n", toString(color).c_str(), toString(outline_color).c_str());
// fprintf(out, "<path marker-mid='url(#MidMarker)' d=\"");
// for(PolygonRef poly : polygons)
// {
// _writeLines(poly, outline_color);
// }
// fprintf(out, "\"/>");
// fprintf(out, "</g>\n");
// }
// void writeLines(PolygonRef poly, Color color = Color::GRAY, Color outline_color = Color::BLACK)
// {
// fprintf(out, "<g fill-rule='evenodd' style=\"fill: %s; stroke:%s;stroke-width:1\">\n", toString(color).c_str(), toString(outline_color).c_str());
// fprintf(out, "<path marker-mid='url(#MidMarker)' d=\"");
// writeLines(poly, outline_color);
// fprintf(out, "\"/>");
// fprintf(out, "</g>\n");
// }
void writeAreas(const Polygons& polygons, Color color = Color::GRAY, Color outline_color = Color::BLACK)
{
for(PolygonsPart& parts : polygons.splitIntoParts())
{
for(unsigned int j=0;j<parts.size();j++)
@@ -100,7 +142,7 @@ public:
}
}
}
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
{
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
@@ -111,7 +153,7 @@ public:
}
fprintf(out,"\" />\n"); //The end of the polygon tag.
}
void writePoint(const Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
Point pf = transform(p);
@@ -122,7 +164,6 @@ public:
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10px;\" fill=\"black\">%lli,%lli</text>\n",pf.X, pf.Y, p.X, p.Y);
}
}
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
for (Point& p : poly)
@@ -130,7 +171,7 @@ public:
writePoint(p, write_coords, size, color);
}
}
void writePoints(Polygons& polygons, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
for (PolygonRef poly : polygons)
@@ -138,7 +179,7 @@ public:
writePoints(poly, write_coords, size, color);
}
}
/*!
* \brief Draws a polyline on the canvas.
*
@@ -150,7 +191,7 @@ public:
* \param color The colour of the line segments. If this is not specified,
* black will be used.
*/
void writeLines(std::vector<Point> polyline, Color color = Color::BLACK)
void writeLines(PolygonRef polyline, Color color = Color::BLACK)
{
if(polyline.size() <= 1) //Need at least 2 points.
{
@@ -158,7 +199,7 @@ public:
}
Point transformed = transform(polyline[0]); //Element 0 must exist due to the check above.
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"1\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"50\" stroke-opacity=\"0.3\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
for(size_t point = 1;point < polyline.size();point++)
{
transformed = transform(polyline[point]);
@@ -166,12 +207,31 @@ public:
}
fprintf(out,"\" />\n"); //Write the end of the tag.
}
/*!
* \brief Draws a polyline on the canvas.
*
* The polyline is the set of line segments between each pair of consecutive
* points in the specified vector.
*
* \param polyline A set of points between which line segments must be
* drawn.
* \param color The colour of the line segments. If this is not specified,
* black will be used.
*/
void writeLines(Polygons& polylines,Color color = Color::BLACK)
{
for (PolygonRef polyline : polylines)
{
writeLines(polyline, color);
}
}
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:2;stroke-opacity:0.3\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
}
/*!
+3 -18
Ver Arquivo
@@ -3,15 +3,10 @@
#define GETTIME_H
#ifdef __WIN32
#include <windows.h>
#include <windows.h>
#else
#ifdef USE_CPU_TIME
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <stddef.h>
#include <cassert>
#endif
namespace cura
@@ -20,21 +15,11 @@ static inline double getTime()
{
#ifdef __WIN32
return double(GetTickCount()) / 1000.0;
#else // not __WIN32
#if USE_CPU_TIME // Use cpu usage time if available, otherwise wall clock time
int ret;
struct rusage usage;
ret = getrusage(RUSAGE_SELF,&usage);
assert(ret==0);
double user_time = double(usage.ru_utime.tv_sec) + double(usage.ru_utime.tv_usec) / 1000000.0;
double sys_time = double(usage.ru_stime.tv_sec) + double(usage.ru_stime.tv_usec) / 1000000.0;
return user_time + sys_time;
#else // not USE_CPU_TIME
#else
struct timeval tv;
gettimeofday(&tv, nullptr);
return double(tv.tv_sec) + double(tv.tv_usec) / 1000000.0;
#endif // USE_CPU_TIME
#endif // __WIN32
#endif
}
class TimeKeeper
+2 -6
Ver Arquivo
@@ -59,12 +59,8 @@ public:
* \param null_ptr exactly [nullptr]
* \return this
*/
optional& operator=(std::nullptr_t null_ptr)
optional& operator=(void* null_ptr)
{
if (instance)
{
delete instance;
}
instance = nullptr;
return *this;
}
@@ -86,7 +82,7 @@ public:
{
if (other.instance)
{
instance = new T(*other.instance);
instance = new T(other.instance);
}
else
{
+26 -86
Ver Arquivo
@@ -156,92 +156,6 @@ Polygons PolygonRef::offset(int distance, ClipperLib::JoinType joinType, double
return ret;
}
Polygon Polygons::convexHull() const
{
// Implements Andrew's monotone chain convex hull algorithm
// See https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
using IntPoint = ClipperLib::IntPoint;
size_t num_points = 0U;
for (const ClipperLib::Path &path : paths)
{
num_points += path.size();
}
std::vector<IntPoint> all_points;
all_points.reserve(num_points);
for (const ClipperLib::Path &path : paths)
{
for (const IntPoint &point : path)
{
all_points.push_back(point);
}
}
struct HullSort
{
bool operator()(const IntPoint &a,
const IntPoint &b)
{
return (a.X < b.X) ||
(a.X == b.X && a.Y < b.Y);
}
};
std::sort(all_points.begin(), all_points.end(), HullSort());
// positive for left turn, 0 for straight, negative for right turn
auto ccw = [](const IntPoint &p0, const IntPoint &p1, const IntPoint &p2) -> int64_t {
IntPoint v01(p1.X - p0.X, p1.Y - p0.Y);
IntPoint v12(p2.X - p1.X, p2.Y - p1.Y);
return
static_cast<int64_t>(v01.X) * v12.Y -
static_cast<int64_t>(v01.Y) * v12.X;
};
Polygon hull_poly;
ClipperLib::Path &hull_points = *hull_poly;
hull_points.resize(num_points+1);
// index to insert next hull point, also number of valid hull points
size_t hull_idx = 0;
// Build lower hull
for (size_t pt_idx=0U; pt_idx!=num_points; ++pt_idx)
{
while (hull_idx >= 2 &&
ccw(hull_points[hull_idx-2], hull_points[hull_idx-1],
all_points[pt_idx]) <= 0)
{
--hull_idx;
}
hull_points[hull_idx] = all_points[pt_idx];
++hull_idx;
}
// Build upper hull
size_t min_upper_hull_chain_end_idx = hull_idx+1;
for (int pt_idx=num_points-2; pt_idx>=0; --pt_idx)
{
while (hull_idx >= min_upper_hull_chain_end_idx &&
ccw(hull_points[hull_idx-2], hull_points[hull_idx-1],
all_points[pt_idx]) <= 0)
{
--hull_idx;
}
hull_points[hull_idx] = all_points[pt_idx];
++hull_idx;
}
assert(hull_idx <= hull_points.size());
// Last point is duplicted with first. It is removed in the resize.
hull_points.resize(hull_idx - 2);
return hull_poly;
}
void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_distance_squared){
PolygonRef& thiss = *this;
@@ -447,6 +361,32 @@ void Polygons::splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Poly
}
}
void Polygons::toPolygonsAndPolylines(const ClipperLib::PolyNode& node, Polygons* polygons, Polygons* polylines)
{
if (!polygons && !polylines)
{
return;
}
for (int n = 0; n < node.ChildCount(); n++)
{
const ClipperLib::PolyNode& child = *node.Childs[n];
if (child.IsOpen())
{
if (polylines)
{
polylines->paths.push_back(child.Contour);
}
}
else
{
if (polygons)
{
polylines->paths.push_back(child.Contour);
}
}
toPolygonsAndPolylines(child, polygons, polylines);
}
}
}//namespace cura
+21 -13
Ver Arquivo
@@ -55,11 +55,6 @@ public:
return path->data();
}
const void* data() const
{
return path->data();
}
void add(const Point p)
{
path->push_back(p);
@@ -335,6 +330,15 @@ class Polygons
friend class PolygonRef;
protected:
ClipperLib::Paths paths;
/*!
* Split a PolyTree to its constituent polygons and polylines
*
* \param[in] polytree The tree to process
* \param[out] polygons (optional) The resulting polygons
* \param[out] polylines (optional) The resulting polylines
*/
static void toPolygonsAndPolylines(const ClipperLib::PolyNode& polytree, Polygons* polygons, Polygons* polylines);
public:
unsigned int size() const
{
@@ -439,6 +443,17 @@ public:
clipper.Execute(ClipperLib::ctIntersection, ret.paths);
return ret;
}
Polygons intersectPolylines(const Polygons& area) const
{
Polygons ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(paths, ClipperLib::ptSubject, false);
clipper.AddPaths(area.paths, ClipperLib::ptClip, true);
ClipperLib::PolyTree result;
clipper.Execute(ClipperLib::ctIntersection, result);
toPolygonsAndPolylines(result, nullptr, &ret);
return ret;
}
Polygons xorPolygons(const Polygons& other) const
{
Polygons ret;
@@ -508,20 +523,13 @@ public:
* \return the convex hull (approximately)
*
*/
Polygons approxConvexHull(int extra_outset = 0)
Polygons convexHull(int extra_outset = 0)
{
int overshoot = 100000; // 10 cm (hardcoded value)
return offset(overshoot, ClipperLib::jtRound).offset(-overshoot+extra_outset, ClipperLib::jtRound);
}
/*!
* Convex hull of all the points in the polygons.
* \return the convex hull
*
*/
Polygon convexHull() const;
Polygons smooth(int remove_length, int min_area) //!< removes points connected to small lines
{
Polygons ret;
+1 -1
Ver Arquivo
@@ -602,7 +602,7 @@ std::optional<ClosestPolygonPoint> PolygonUtils::findClose(Point from, const Pol
}
else
{
bool bs_arg = true; // doesn't mean anything. Just to make clear we call the variable arguments of the constructor.
bool bs_arg; // doesn't mean anything. Just to make clear we call the variable arguments of the constructor.
return std::optional<ClosestPolygonPoint>(bs_arg, best, best_point_poly_idx.point_idx, polygons[best_point_poly_idx.poly_idx], best_point_poly_idx.poly_idx);
}
}
+4 -5
Ver Arquivo
@@ -34,11 +34,10 @@ void GCodePlannerTest::setUp()
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
fan_speed_layer_time_settings.cool_min_speed = 0.5;
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder;
fan_speed_layer_time_settings_per_extruder.push_back(fan_speed_layer_time_settings);
// Slice layer z layer last current is inside fan speed and layer combing comb travel travel avoid
// storage nr height position extruder mesh time settings mode offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, false, fan_speed_layer_time_settings_per_extruder, CombingMode::OFF, 100, false, 50 );
// Slice layer z layer last current is inside fan speed and layer combing comb travel travel avoid
// storage nr height position extruder mesh time settings mode offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, false, fan_speed_layer_time_settings, CombingMode::OFF, 100, false, 50 );
}
void GCodePlannerTest::tearDown()
-43
Ver Arquivo
@@ -1,43 +0,0 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#define LARGE_TEST_SIZE 10000
#include "PolygonTest.h"
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(PolygonTest);
void PolygonTest::setUp()
{
ClipperLib::Path small_path;
small_polygon = PolygonRef(small_path);
small_polygon.emplace_back(0, 0);
small_polygon.emplace_back(0, 100);
small_polygon.emplace_back(100, 100);
small_polygon.emplace_back(100, 0);
ClipperLib::Path large_path;
large_polygon = PolygonRef(large_path);
for (size_t vertex_id = 0; vertex_id < LARGE_TEST_SIZE; ++vertex_id)
{
large_polygon.emplace_back(vertex_id % 2 * 1000, vertex_id * 10); //Creates a sawtooth shape.
}
large_polygon.emplace_back(-10, LARGE_TEST_SIZE * 10 + 10);
large_polygon.emplace_back(-10, 0);
}
void PolygonTest::tearDown()
{
//Nothing to do. The polygons don't need changing.
}
void PolygonTest::insideTest()
{
bool test = small_polygon.inside(Point(50, 50));
CPPUNIT_ASSERT(test);
}
}
-44
Ver Arquivo
@@ -1,44 +0,0 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef POLYGONTEST_H
#define POLYGONTEST_H
#include <clipper/clipper.hpp> //To create the paths.
#include <cppunit/TestFixture.h> //Making unit tests.
#include <cppunit/extensions/HelperMacros.h>
#include "../src/utils/polygon.h" //The polygon we're testing.
namespace cura
{
class PolygonTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(PolygonTest);
CPPUNIT_TEST(insideTest);
CPPUNIT_TEST_SUITE_END();
public:
/*!
* \brief Sets up the test suite to prepare for testing.
*/
void setUp();
/*!
* \brief Tears down the test suite when testing is done.
*/
void tearDown();
//The actual test cases.
void insideTest();
private:
Polygon small_polygon;
Polygon large_polygon;
};
}
#endif /* POLYGONTEST_H */