Comparar commits

..

19 Commits

Autor SHA1 Mensagem Data
Tim Kuipers e84ec85ca7 fix: spaghetti infill now uses connected zigzags to minimize non-extrusion moves (CURA-3238)
also note that due to the spaghetti_inset the zigzags are not connected to the walls (depending on user input)
2017-02-15 10:25:36 +01:00
Tim Kuipers 5f7ce40b89 fix: register layer as empty even if meshes don't have walls (CURA-3238) 2017-02-15 10:19:08 +01:00
Tim Kuipers c97097a7e2 indent only (CURA-3238) 2017-02-13 15:25:15 +01:00
Tim Kuipers 44612c2e71 fix: spaghetti infill now uses normal infill spacing and infill overlap settings (CURA-3238) 2017-02-13 15:24:52 +01:00
Tim Kuipers d1f64718ea feat: spaghetti_flow setting (CURA-3238) 2017-02-13 14:00:24 +01:00
Tim Kuipers a9390abd61 spaghetti infill: documentation (CURA-3238) 2017-02-13 13:54:17 +01:00
Tim Kuipers d3837f7efe feat: spaghetti_inset setting (CURA-3238) 2017-02-13 12:04:37 +01:00
Tim Kuipers 1890d51b03 fix: spaghetti infill: use infill line width rather than hardcoded value (CURA-3238) 2017-02-13 11:59:58 +01:00
Tim Kuipers 6d9eef9fa9 feat: spaghetti infill area inset (CURA-3238) 2017-02-13 11:40:11 +01:00
Tim Kuipers 76df240798 fix: forgot the actual SpaghettiInfill files (CURA-3238) 2017-02-13 11:40:11 +01:00
Tim Kuipers 0425676f13 feat: PolygonsPart::area() (CURA-3238) 2017-02-13 11:40:11 +01:00
Tim Kuipers ebd308f229 fix: small indent and const only (CURA-3238) 2017-02-13 11:40:11 +01:00
Tim Kuipers cad9a12ade feat: spaghetti infill (CURA-3238) 2017-02-13 11:40:11 +01:00
Tim Kuipers 95b2034739 feat: Polygon::polyLineLength() (CURA-3238) 2017-02-13 11:40:11 +01:00
Ghostkeeper ab7e035f6e Don't continue with compiling when function has error
This would give an exception which breaks the script and makes a very unclear chained error message.

Contributes to issue CURA-2572.
2017-02-03 17:05:28 +01:00
Ghostkeeper d0a149cf26 Fix error handling when setting has a parse error
The code was an integer. It needs to be converted to a string in order to be added to other strings.

Contributes to issue CURA-2572.
2017-02-03 17:05:28 +01:00
Tim Kuipers 4d35735aa8 Merge pull request #451 from smartavionics/mb-spiralize-bug
Fix spiralize args bug - untested but this just looked wrong to me.
2017-02-02 13:12:49 +01:00
Mark Burton 5e831f99e9 Fix spiralize args bug - untested but this just looked wrong to me. 2017-02-02 08:08:50 +00:00
Ghostkeeper aa14682087 Remove 'l' formatting character and simplify+speed-up int2mm formatting
The 'l' formatting character in combination with 'd' was not allowed with MinGW on Windows. Simply formatting with 'd' is sufficient since our coordinates are allowed to be cast to integers at this stage: We won't need to square them any more or anything.

Contributes to issue CURA-3274.
2017-01-31 10:35:28 +01:00
15 arquivos alterados com 414 adições e 40 exclusões
+1
Ver Arquivo
@@ -91,6 +91,7 @@ set(engine_SRCS # Except main.cpp.
src/Wireframe2gcode.cpp
src/infill/NoZigZagConnectorProcessor.cpp
src/infill/SpaghettiInfill.cpp
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
+75 -7
Ver Arquivo
@@ -794,12 +794,15 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
return;
}
if (mesh->getSettingAsCount("wall_line_count") > 0)
{ // don't switch extruder if there's nothing to print
bool empty = true;
const bool use_walls = mesh->getSettingAsCount("wall_line_count") > 0;
for (SliceLayerPart& part : layer->parts)
{
if (part.insets.size() > 0)
if (
(use_walls && part.insets.size() > 0)
|| (!use_walls && (part.getOwnInfillArea().size() > 0 || part.skin_parts.size() > 0))
)
{
empty = false;
break;
@@ -860,8 +863,7 @@ void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStor
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
@@ -870,8 +872,7 @@ void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStor
if (!mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
EFillMethod skin_pattern = (layer_nr == 0)?
@@ -898,7 +899,74 @@ void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStor
}
void FffGcodeWriter::processInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
{
if (mesh->getSettingBoolean("spaghetti_infill_enabled"))
{
processSpaghettiInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
else
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
}
void FffGcodeWriter::processSpaghettiInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
{
GCodePathConfig& config = mesh->infill_config[0];
const EFillMethod pattern = mesh->getSettingAsFillMethod("infill_pattern");
const unsigned int infill_line_width = config.getLineWidth();
const int64_t z = layer_nr * getSettingInMicrons("layer_height");
const int64_t infill_shift = 0;
const int64_t outline_offset = 0;
for (std::pair<PolygonsPart, double>& filling_area : part.spaghetti_infill_volumes)
{
Polygons infill_lines;
Polygons infill_polygons;
const PolygonsPart& area = filling_area.first;
const double total_volume = filling_area.second * getSettingAsRatio("spaghetti_flow");
assert(total_volume > 0.0);
Polygons* perimeter_gaps_output = nullptr;
const bool connected_zigzags = true;
const bool use_endpieces = false;
Infill infill_comp(pattern, area, outline_offset, infill_line_width, infill_line_distance, infill_overlap, infill_angle, z, infill_shift, perimeter_gaps_output, connected_zigzags, use_endpieces);
infill_comp.generate(infill_polygons, infill_lines, mesh);
const coord_t total_length = infill_polygons.polygonLength() + infill_lines.polyLineLength();
if (total_length > 0)
{
const double normal_volume = INT2MM(INT2MM(total_length * infill_line_width)) * mesh->getSettingInMillimeters("layer_height");
const float flow_ratio = total_volume / normal_volume;
assert(flow_ratio >= 0.9);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &config, nullptr, EZSeamType::SHORTEST, Point(0, 0), 0, false, flow_ratio);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addLinesByOptimizer(infill_lines, &config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"), flow_ratio);
}
else
{
gcode_layer.addLinesByOptimizer(infill_lines, &config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines, 0, flow_ratio);
}
}
else
{
Point middle = const_cast<PolygonsPart&>(area).outerPolygon().centerOfMass();
if (!area.inside(middle))
{
PolygonUtils::ensureInsideOrOutside(area, middle, infill_line_width / 2);
}
const double normal_volume = INT2MM(INT2MM(10 * infill_line_width)) * mesh->getSettingInMillimeters("layer_height");
const float flow_ratio = total_volume / normal_volume;
gcode_layer.addTravel(middle);
gcode_layer.addExtrusionMove(middle + Point(0,10), &config, SpaceFillType::Lines, flow_ratio);
}
}
}
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
{
@@ -1009,7 +1077,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
WallOverlapComputation* wall_overlap_computation(nullptr);
int wall_0_wipe_dist(0);
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, wall_overlap_computation, EZSeamType::SHORTEST, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), wall_0_wipe_dist);
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, wall_overlap_computation, EZSeamType::SHORTEST, z_seam_pos, wall_0_wipe_dist, spiralize);
}
}
int processed_inset_number = -1;
+27 -1
Ver Arquivo
@@ -336,7 +336,33 @@ private:
*
*/
void addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add sparse infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Add spaghetti infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processSpaghettiInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
*
+19 -9
Ver Arquivo
@@ -15,6 +15,7 @@
#include "WallsComputation.h"
#include "SkirtBrim.h"
#include "skin.h"
#include "infill/SpaghettiInfill.h"
#include "infill.h"
#include "raft.h"
#include "progress/Progress.h"
@@ -466,18 +467,27 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
//SubDivCube Pre-compute Octree
if (mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV)
// generate spaghetti infill filling areas and volumes
if (mesh.getSettingBoolean("spaghetti_infill_enabled"))
{
SubDivCube::precomputeOctree(mesh);
SpaghettiInfill::generateSpaghettiInfill(mesh);
}
else
{
// combine infill
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1))); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
//SubDivCube Pre-compute Octree
if (mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV)
{
SubDivCube::precomputeOctree(mesh);
}
// combine infill
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1))); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
}
// fuzzy skin
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
+7 -7
Ver Arquivo
@@ -355,21 +355,21 @@ void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillT
lastPosition = p;
}
void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize)
void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize, float flow_ratio)
{
Point p0 = polygon[start_idx];
addTravel(p0);
for (unsigned int point_idx = 1; point_idx < polygon.size(); point_idx++)
{
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
float flow = (wall_overlap_computation)? flow_ratio * wall_overlap_computation->getFlow(p0, p1) : flow_ratio;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[start_idx];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
float flow = (wall_overlap_computation)? flow_ratio * wall_overlap_computation->getFlow(p0, p1) : flow_ratio;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
if (wall_0_wipe_dist > 0)
@@ -403,7 +403,7 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, GCodePathConfig
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, Point z_seam_pos, coord_t wall_0_wipe_dist, bool spiralize)
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, Point z_seam_pos, coord_t wall_0_wipe_dist, bool spiralize, float flow_ratio)
{
if (polygons.size() == 0)
{
@@ -417,10 +417,10 @@ void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* c
orderOptimizer.optimize();
for (unsigned int poly_idx : orderOptimizer.polyOrder)
{
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, wall_0_wipe_dist, spiralize);
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, wall_0_wipe_dist, spiralize, flow_ratio);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist, float flow_ratio)
{
LineOrderOptimizer orderOptimizer(lastPosition);
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
@@ -436,7 +436,7 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
Point& p0 = polygon[start];
addTravel(p0);
Point& p1 = polygon[end];
addExtrusionMove(p1, config, space_fill_type);
addExtrusionMove(p1, config, space_fill_type, flow_ratio);
if (wipe_dist != 0)
{
int line_width = config->getLineWidth();
+6 -3
Ver Arquivo
@@ -387,8 +387,9 @@ public:
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param wall_0_wipe_dist The distance to travel along the polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
* \param flow_ratio The ratio with which to multiply the extrusion amount
*/
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false, float flow_ratio = 1.0);
/*!
* Add polygons to the gcode with optimized order.
@@ -406,8 +407,9 @@ public:
* \param z_seam_pos The location near where to start each part in case \p z_seam_type is 'back'
* \param wall_0_wipe_dist The distance to travel along each polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
* \param flow_ratio The ratio with which to multiply the extrusion amount
*/
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false, float flow_ratio = 1.0);
/*!
* Add lines to the gcode with optimized order.
@@ -415,8 +417,9 @@ public:
* \param config The config of the lines
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
* \param flow_ratio The ratio with which to multiply the extrusion amount
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0, float flow_ratio = 1.0);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
+139
Ver Arquivo
@@ -0,0 +1,139 @@
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#include "SpaghettiInfill.h"
namespace cura {
void SpaghettiInfill::generateSpaghettiInfill(SliceMeshStorage& mesh)
{
coord_t layer_height = mesh.getSettingInMicrons("layer_height");
coord_t line_width = mesh.getSettingInMicrons("infill_line_width");
double layer_height_mm = INT2MM(layer_height);
int spaghetti_max_layer_count = std::max(1, static_cast<int>(mesh.getSettingInMicrons("spaghetti_max_height") / layer_height));
// TODO: account for the initial layer height
coord_t filling_area_inset = mesh.getSettingInMicrons("spaghetti_inset");
if (mesh.getSettingInAngleDegrees("spaghetti_max_infill_angle") >= 90)
{
return; // infill cannot be combined into pillars
}
coord_t connection_inset_dist = tan(mesh.getSettingInAngleRadians("spaghetti_max_infill_angle")) * layer_height; // Horizontal component of the spaghetti_max_infill_angle
std::list<SpaghettiInfill::InfillPillar> pillar_base;
size_t min_layer = mesh.getSettingAsCount("bottom_layers") + 1;
size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
for (size_t layer_idx = min_layer; layer_idx <= max_layer; layer_idx++) //Skip every few layers, but extrude more.
{
SliceLayer& layer = mesh.layers[layer_idx];
// add infill parts to pillar_base
for (SliceLayerPart& slice_layer_part : layer.parts)
{
std::vector<PolygonsPart> part_infill_parts = slice_layer_part.getOwnInfillArea().splitIntoParts();
// add parts to pillar_base
for (PolygonsPart& infill_part : part_infill_parts)
{
SpaghettiInfill::InfillPillar& pillar = addPartToPillarBase(infill_part, pillar_base, connection_inset_dist);
pillar.top_slice_layer_part = &slice_layer_part;
pillar.last_layer_added = layer_idx;
pillar.layer_count++;
}
}
// handle finished pillars
for (auto it = pillar_base.begin(); it != pillar_base.end();)
{
InfillPillar& pillar = *it;
if (pillar.layer_count >= spaghetti_max_layer_count
|| pillar.last_layer_added < static_cast<int>(layer_idx)
)
{
pillar.addToTopSliceLayerPart(layer_height_mm, filling_area_inset, line_width);
auto to_be_erased = it;
++it;
pillar_base.erase(to_be_erased);
}
else
{
++it;
}
}
}
// handle unfinished pillars
for (auto it = pillar_base.begin(); it != pillar_base.end(); ++it)
{
it->addToTopSliceLayerPart(layer_height_mm, filling_area_inset, line_width);
}
}
void SpaghettiInfill::InfillPillar::addToTopSliceLayerPart(double layer_height_mm, coord_t filling_area_inset, coord_t line_width)
{
SliceLayerPart& slice_layer_part = *top_slice_layer_part;
double volume = total_area_mm2 * layer_height_mm;
assert(volume > 0.0);
// get filling area
Polygons filling_area = top_part.offset(-filling_area_inset);
assert(top_part.size() > 0 && top_part[0].size() > 0 && "the top part must be a non-zero area!");
if (filling_area.size() == 0)
{
AABB aabb(top_part);
Point inside = (aabb.min + aabb.max) / 2;
if (!top_part.inside(inside))
{
inside = top_part[0][0];
}
filling_area = PolygonsPart();
PolygonRef poly = filling_area.newPoly();
poly.emplace_back(inside + Point(-line_width / 2 - 10, line_width / 2 + 10));
poly.emplace_back(inside + Point(line_width / 2 + 10, line_width / 2 + 10));
poly.emplace_back(inside + Point(line_width / 2 + 10, -line_width / 2 - 10));
poly.emplace_back(inside + Point(-line_width / 2 - 10, -line_width / 2 - 10));
}
slice_layer_part.spaghetti_infill_volumes.emplace_back(top_part, volume);
}
bool SpaghettiInfill::InfillPillar::isConnected(const PolygonsPart& infill_part) const
{
Polygons insetted = infill_part.offset(-connection_inset_dist);
if (insetted.intersection(top_part).size() > 0)
{
return true;
}
else
{
return false;
}
}
SpaghettiInfill::InfillPillar& SpaghettiInfill::addPartToPillarBase(const PolygonsPart& infill_part, std::list<SpaghettiInfill::InfillPillar>& pillar_base, coord_t connection_inset_dist)
{
std::list<SpaghettiInfill::InfillPillar>::iterator ret = pillar_base.end();
for (auto it = pillar_base.begin(); it != pillar_base.end(); ++it)
{
InfillPillar& pillar = *it;
if (pillar.isConnected(infill_part))
{
pillar.total_area_mm2 += INT2MM(INT2MM(infill_part.area()));
pillar.top_part = infill_part;
if (ret != pillar_base.end())
{ // connecting two pillars of the layer below via one area on this layer
pillar.total_area_mm2 += ret->total_area_mm2;
pillar_base.erase(ret);
}
ret = it;
}
}
if (ret == pillar_base.end())
{ // couldn't connect to any existing pillar
pillar_base.emplace_back(infill_part, connection_inset_dist);
return pillar_base.back();
}
return *ret;
}
}//namespace cura
+95
Ver Arquivo
@@ -0,0 +1,95 @@
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_SPAGHETTI_INFILL_H
#define INFILL_SPAGHETTI_INFILL_H
#include <list>
#include "../utils/intpoint.h"
#include "../utils/polygon.h"
#include "../sliceDataStorage.h"
namespace cura {
/*!
* Spaghetti infill is a type of infill which fills every so many layers, but extrudes as much filament corresponding to the total unfilled volume under the filling area.
*
* A filling layer is inserted when a a pillar of infill areas is becoming too high, or when the angle between the filling areas is too shallow.
*
* The filling area might be smaller than the actual infill area, so that we fill the pillar from a smaller top area.
*
* Infill pillars can join each other if they are connected on the top. The total volume will then be extruded from the top.
*
* Where the model spits into two from bottom to top, one of the top pieces will be connected to the lower part as one big pillar, while a new pillar will be generated for the other top part.
* Which part the base will be connected to is arbitrary.
*
*/
class SpaghettiInfill
{
public:
/*!
* Generate the filling areas and corresponding volume to extrude over such areas for spaghetti infill.
*/
static void generateSpaghettiInfill(SliceMeshStorage& mesh);
protected:
struct InfillPillar
{
SliceLayerPart* top_slice_layer_part = nullptr; //!< A reference to the slice_layer_part from which the top part is generated
PolygonsPart top_part; //!< The top area of this pillar
double total_area_mm2; //!< The total volume of the pillar divided by the layer height
coord_t connection_inset_dist; //!< Horizontal component of the spaghetti_max_infill_angle: the distance insetted corresponding to the maximum angle which can be filled by spaghetti infill.
int layer_count; //!< The height of the pillar in numer of layers
int last_layer_added = -1; //!< The last layer from which areas got added to this pillar
/*!
* Basic constructor of a pillar from a single area, which is to be the top of the new pillar
*
* \param _top_part The area which is the base and the top of the new pillar
* \param connection_inset_dist Horizontal component of the spaghetti_max_infill_angle
*/
InfillPillar(const PolygonsPart& _top_part, coord_t connection_inset_dist)
: top_part(_top_part) // TODO: prevent copy construction! Is that possible?
, total_area_mm2(INT2MM(INT2MM(top_part.area())))
, connection_inset_dist(connection_inset_dist)
, layer_count(1)
{
}
/*!
* Check whether the top of this pillar is connected (enough) to the given \p infill_part.
* It is assumed the infill_part is on the layer directly above the top part of this pillar.
*
* \param infill_part The part to check for connectivity
* \return Whether the infill part can be incorporated in this pillar
*/
bool isConnected(const PolygonsPart& infill_part) const;
/*!
* Register the volume of this infill pillar in the sliceDataStorage.
* The filling area and the volume are saved in \ref SliceLayerPart::spaghetti_infill_volumes
*
* Note that the filling area is different from the infill area, because the spaghetti can curl toward the sides.
*
* \param layer_height_mm The layer height in millimeters
* \param filling_area_inset The inset from the boundary of the walls to get from the infill area to the filling area
* \param line_width The line width used to generate an area just large enough for infill lines to be generated, when the infill area would otherwise be too small to get infill
*/
void addToTopSliceLayerPart(double layer_height_mm, coord_t filling_area_inset, coord_t line_width);
};
private:
/*!
* Add an area to the pillar base:
* - add it to an existing pillar if possible
* - otherwise create a new pillar for this area
* The pillar to which the area was added is returned
*
* \param infill_part The area to add to the base
* \param pillar_base The collection of pillars used up till the current layer
* \param connection_inset_dist The distance insetted corresponding to the maximum angle which can be filled by spaghetti infill
*/
static InfillPillar& addPartToPillarBase(const PolygonsPart& infill_part, std::list<InfillPillar>& pillar_base, coord_t connection_inset_dist);
};
}//namespace cura
#endif//INFILL_SPAGHETTI_INFILL_H
+1 -4
Ver Arquivo
@@ -44,10 +44,7 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
}
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
const auto total_layers = slicer->layers.size();
assert(mesh.layers.size() == total_layers);
#pragma omp parallel for default(none) shared(mesh,slicer) firstprivate(union_layers,union_all_remove_holes) schedule(dynamic)
for(unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
+2
Ver Arquivo
@@ -74,6 +74,8 @@ public:
* \return the own infill area
*/
Polygons& getOwnInfillArea();
std::vector<std::pair<PolygonsPart, double>> spaghetti_infill_volumes; //!< For each filling volume on this layer, the area within which to fill and the total volume to fill over the area
};
/*!
+2 -6
Ver Arquivo
@@ -887,14 +887,10 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bo
}
}
log("slice of mesh took %.3f seconds\n",slice_timer.restart());
std::vector<SlicerLayer>& layers_ref = layers; // force layers not to be copied into the threads
#pragma omp parallel for default(none) shared(mesh,layers_ref) firstprivate(keep_none_closed, extensive_stitching)
for(unsigned int layer_nr=0; layer_nr<layers_ref.size(); layer_nr++)
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
{
layers_ref[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
}
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
}
+27
Ver Arquivo
@@ -190,6 +190,22 @@ unsigned int Polygons::findInside(Point p, bool border_result)
return ret;
}
int64_t Polygons::polyLineLength() const
{
int64_t length = 0;
for (unsigned int poly_idx = 0; poly_idx < paths.size(); poly_idx++)
{
Point p0 = paths[poly_idx][0];
for (unsigned int point_idx = 1; point_idx < paths[poly_idx].size(); point_idx++)
{
Point p1 = paths[poly_idx][point_idx];
length += vSize(p0 - p1);
p0 = p1;
}
}
return length;
}
Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double miter_limit) const
{
Polygons ret;
@@ -1116,6 +1132,17 @@ Polygons Polygons::smooth2(int remove_length, int min_area)
return ret;
}
double PolygonsPart::area() const
{
double area = 0;
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
{
area += operator[](poly_idx).area();
// note: holes have negative area
}
return area;
}
std::vector<PolygonsPart> Polygons::splitIntoParts(bool unionAll) const
{
std::vector<PolygonsPart> ret;
+5 -1
Ver Arquivo
@@ -874,6 +874,8 @@ public:
}
return length;
}
int64_t polyLineLength() const;
Point min() const
{
@@ -929,7 +931,7 @@ public:
return thiss[0];
}
bool inside(Point p)
bool inside(Point p) const
{
if (size() < 1)
return false;
@@ -942,6 +944,8 @@ public:
}
return true;
}
double area() const;
};
/*!
+4 -1
Ver Arquivo
@@ -1,3 +1,6 @@
//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_STRING_H
#define UTILS_STRING_H
@@ -35,7 +38,7 @@ static inline void writeInt2mm(const int64_t coord, std::ostream& ss)
{
constexpr size_t buffer_size = 24;
char buffer[buffer_size];
int char_count = sprintf(buffer, "%" PRId64, coord); // convert int to string
int char_count = sprintf(buffer, "%d", int(coord)); // convert int to string
#ifdef DEBUG
if (char_count + 1 >= int(buffer_size)) // + 1 for the null character
{
+4 -1
Ver Arquivo
@@ -235,11 +235,14 @@ class Setting:
tree = ast.parse(code, "eval")
compiled = compile(code, self._key, "eval")
except (SyntaxError, TypeError) as e:
print("Parse error in function (" + code + ") for setting", self._key + ":", str(e))
print("Parse error in function (" + str(code) + ") for setting", self._key + ":", str(e))
return None
except IllegalMethodError as e:
print("Use of illegal method", str(e), "in function (" + code + ") for setting", self._key)
return None
except Exception as e:
print("Exception in function (" + code + ") for setting", self._key + ":", str(e))
return None
return eval(compiled, globals(), locals)