Comparar commits
19 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| e84ec85ca7 | |||
| 5f7ce40b89 | |||
| c97097a7e2 | |||
| 44612c2e71 | |||
| d1f64718ea | |||
| a9390abd61 | |||
| d3837f7efe | |||
| 1890d51b03 | |||
| 6d9eef9fa9 | |||
| 76df240798 | |||
| 0425676f13 | |||
| ebd308f229 | |||
| cad9a12ade | |||
| 95b2034739 | |||
| ab7e035f6e | |||
| d0a149cf26 | |||
| 4d35735aa8 | |||
| 5e831f99e9 | |||
| aa14682087 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário