Comparar commits
11 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 2926c2e06e | |||
| 7f9dd1cd81 | |||
| bfbfa5c47f | |||
| d887b50eed | |||
| 9712301aa8 | |||
| cdb0a41243 | |||
| 3235fc856d | |||
| d97f67967b | |||
| 64abe6b620 | |||
| cfc2dcb0ad | |||
| 43a40f86b7 |
@@ -7,14 +7,8 @@
|
||||
NUL
|
||||
*.gcode
|
||||
|
||||
## Directories used for other stuff
|
||||
Trash/*
|
||||
output/*
|
||||
callgrind/*
|
||||
|
||||
## Building result.
|
||||
build/*
|
||||
debug_build/*
|
||||
*.pyc
|
||||
*.exe
|
||||
*.a
|
||||
|
||||
+1
-1
@@ -89,6 +89,7 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/wallOverlap.cpp
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
src/multithreadOpenMP.cpp
|
||||
|
||||
src/infill/NoZigZagConnectorProcessor.cpp
|
||||
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
|
||||
@@ -106,7 +107,6 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/progress/Progress.cpp
|
||||
src/progress/ProgressStageEstimator.cpp
|
||||
|
||||
src/settings/PathConfigStorage.cpp
|
||||
src/settings/SettingConfig.cpp
|
||||
src/settings/SettingContainer.cpp
|
||||
src/settings/SettingRegistry.cpp
|
||||
|
||||
+209
-168
@@ -25,11 +25,11 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
// set the initial extruder of this meshgroup
|
||||
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
|
||||
{ // first meshgroup
|
||||
planner_state.current_extruder = getStartExtruder(storage);
|
||||
current_extruder_planned = getStartExtruder(storage);
|
||||
}
|
||||
else
|
||||
{
|
||||
planner_state.current_extruder = gcode.getExtruderNr();
|
||||
current_extruder_planned = gcode.getExtruderNr();
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
@@ -42,9 +42,12 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
setConfigCoasting(storage);
|
||||
|
||||
setConfigRetraction(storage);
|
||||
|
||||
|
||||
initConfigs(storage);
|
||||
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ //Skirt and brim.
|
||||
storage.skirt_brim_config[extruder].setLayerHeight(getSettingInMicrons("layer_height_0"));
|
||||
skirt_brim_is_processed[extruder] = false;
|
||||
}
|
||||
|
||||
@@ -68,6 +71,17 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
|
||||
gcode.writeLayerCountComment(total_layers);
|
||||
|
||||
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
if (has_raft)
|
||||
{
|
||||
processRaft(storage, total_layers);
|
||||
// process filler layers to fill the airgap with helper object (support etc) so that they stick better to the raft.
|
||||
for (int layer_nr = -Raft::getFillerLayerCount(storage); layer_nr < 0; layer_nr++)
|
||||
{
|
||||
processLayer(storage, layer_nr, total_layers);
|
||||
}
|
||||
}
|
||||
|
||||
{ // calculate the mesh order for each extruder
|
||||
int extruder_count = storage.meshgroup->getExtruderCount();
|
||||
mesh_order_per_extruder.reserve(extruder_count);
|
||||
@@ -77,25 +91,14 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
}
|
||||
}
|
||||
|
||||
int process_layer_starting_layer_nr = 0;
|
||||
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
if (has_raft)
|
||||
#pragma omp parallel default(none) shared(storage, total_layers)
|
||||
{
|
||||
processRaft(storage, total_layers);
|
||||
// process filler layers to fill the airgap with helper object (support etc) so that they stick better to the raft.
|
||||
process_layer_starting_layer_nr = -Raft::getFillerLayerCount(storage);
|
||||
}
|
||||
|
||||
for (unsigned int layer_nr = process_layer_starting_layer_nr; layer_nr < total_layers; layer_nr++)
|
||||
{
|
||||
GCodePlanner& gcode_layer = processLayer(storage, layer_nr, total_layers);
|
||||
layer_plan_buffer.push(gcode_layer);
|
||||
planner_state = gcode_layer.getPlanningState();
|
||||
GCodePlanner* to_be_written = layer_plan_buffer.processBuffer();
|
||||
if (to_be_written)
|
||||
#pragma omp single nowait
|
||||
{
|
||||
to_be_written->writeGCode(gcode);
|
||||
delete to_be_written;
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
processLayer(storage, layer_nr, total_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +178,42 @@ void FffGcodeWriter::setConfigRetraction(SliceDataStorage& storage)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage)
|
||||
void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
{
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ //Skirt and brim.
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
storage.skirt_brim_config[extruder].init(train->getSettingInMillimetersPerSecond("skirt_brim_speed"), train->getSettingInMillimetersPerSecond("acceleration_skirt_brim"), train->getSettingInMillimetersPerSecond("jerk_skirt_brim"), train->getSettingInMicrons("skirt_brim_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.travel_config_per_extruder[extruder].init(train->getSettingInMillimetersPerSecond("speed_travel"), train->getSettingInMillimetersPerSecond("acceleration_travel"), train->getSettingInMillimetersPerSecond("jerk_travel"), 0, 0);
|
||||
}
|
||||
|
||||
{ // support
|
||||
SettingsBase* infill_train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
|
||||
storage.support_config.init(infill_train->getSettingInMillimetersPerSecond("speed_support_infill"), infill_train->getSettingInMillimetersPerSecond("acceleration_support_infill"), infill_train->getSettingInMillimetersPerSecond("jerk_support_infill"), infill_train->getSettingInMicrons("support_line_width"), infill_train->getSettingInPercentage("material_flow"));
|
||||
|
||||
SettingsBase* interface_train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_interface_extruder_nr"));
|
||||
storage.support_skin_config.init(interface_train->getSettingInMillimetersPerSecond("speed_support_interface"), interface_train->getSettingInMillimetersPerSecond("acceleration_support_interface"), interface_train->getSettingInMillimetersPerSecond("jerk_support_interface"), interface_train->getSettingInMicrons("support_interface_line_width"), interface_train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_0"), mesh.getSettingInMillimetersPerSecond("jerk_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
|
||||
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"));
|
||||
mesh.perimeter_gap_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom"), mesh.getSettingInMicrons("wall_line_width_x") / 2, mesh.getSettingInPercentage("material_flow"));
|
||||
// the perimeter gap config follows the skin config, but has a different line width:
|
||||
// wall_line_width_x divided by two because the gaps are between 0 and 1 times the wall line width
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
storage.primeTower.initConfigs(storage.meshgroup);
|
||||
}
|
||||
|
||||
unsigned int FffGcodeWriter::getStartExtruder(SliceDataStorage& storage)
|
||||
{
|
||||
int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
|
||||
if (getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::NONE)
|
||||
@@ -194,7 +232,7 @@ unsigned int FffGcodeWriter::getStartExtruder(const SliceDataStorage& storage)
|
||||
return start_extruder_nr;
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processStartingCode(const SliceDataStorage& storage, const unsigned int start_extruder_nr)
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage, const unsigned int start_extruder_nr)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
@@ -257,12 +295,12 @@ void FffGcodeWriter::processStartingCode(const SliceDataStorage& storage, const
|
||||
gcode.writeTemperatureCommand(start_extruder_nr, print_temp_here, wait);
|
||||
gcode.writePrimeTrain(train.getSettingInMillimetersPerSecond("speed_travel"));
|
||||
extruder_prime_is_planned[start_extruder_nr] = true;
|
||||
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[start_extruder_nr];
|
||||
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[start_extruder_nr];
|
||||
gcode.writeRetraction(retraction_config);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage)
|
||||
void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
{
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
@@ -274,11 +312,11 @@ void FffGcodeWriter::processNextMeshGroupCode(const SliceDataStorage& storage)
|
||||
|
||||
gcode.setZ(max_object_height + 5000);
|
||||
gcode.writeMove(gcode.getPositionXY(), storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
planner_state.last_position = Point(storage.model_min.x, storage.model_min.y);
|
||||
gcode.writeMove(planner_state.last_position, storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
|
||||
gcode.writeMove(last_position_planned, storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int total_layers)
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
@@ -287,7 +325,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
|
||||
int z = 0;
|
||||
|
||||
{ // set configs
|
||||
storage.raft_base_config.init(train->getSettingInMillimetersPerSecond("raft_base_speed"), train->getSettingInMillimetersPerSecond("raft_base_acceleration"), train->getSettingInMillimetersPerSecond("raft_base_jerk"), train->getSettingInMicrons("raft_base_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_base_config.setLayerHeight(train->getSettingInMicrons("raft_base_thickness"));
|
||||
|
||||
storage.raft_interface_config.init(train->getSettingInMillimetersPerSecond("raft_interface_speed"), train->getSettingInMillimetersPerSecond("raft_interface_acceleration"), train->getSettingInMillimetersPerSecond("raft_interface_jerk"), train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_interface_config.setLayerHeight(train->getSettingInMicrons("raft_interface_thickness"));
|
||||
|
||||
storage.raft_surface_config.init(train->getSettingInMillimetersPerSecond("raft_surface_speed"), train->getSettingInMillimetersPerSecond("raft_surface_acceleration"), train->getSettingInMillimetersPerSecond("raft_surface_jerk"), train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
|
||||
}
|
||||
|
||||
const int initial_raft_layer_nr = -Raft::getTotalExtraLayers(storage);
|
||||
|
||||
@@ -303,7 +350,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
int layer_height = train->getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
|
||||
GCodePlanner& gcode_layer = *new GCodePlanner(storage, layer_nr, z, layer_height, planner_state, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(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"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
@@ -313,31 +363,26 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
CommandSocket::getInstance()->sendOptimizedLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
|
||||
Polygons wall = storage.raftOutline.offset(-gcode_layer.configs_storage.raft_base_config.getLineWidth() / 2);
|
||||
gcode_layer.addPolygonsByOptimizer(wall, &gcode_layer.configs_storage.raft_base_config);
|
||||
Polygons wall = storage.raftOutline.offset(-storage.raft_base_config.getLineWidth() / 2);
|
||||
gcode_layer.addPolygonsByOptimizer(wall, &storage.raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
double fill_angle = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, wall, offset_from_poly_outline, gcode_layer.configs_storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
|
||||
Infill infill_comp(EFillMethod::LINES, wall, offset_from_poly_outline, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(raft_polygons, raftLines);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &gcode_layer.configs_storage.raft_base_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
|
||||
|
||||
if (getExtrudersNeedPrimeDuringFirstLayer())
|
||||
{
|
||||
ensureAllExtrudersArePrimed(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
|
||||
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_base_fan_speed"));
|
||||
|
||||
layer_plan_buffer.push(gcode_layer);
|
||||
planner_state = gcode_layer.getPlanningState();
|
||||
GCodePlanner* to_be_written = layer_plan_buffer.processBuffer();
|
||||
if (to_be_written)
|
||||
{
|
||||
to_be_written->writeGCode(gcode);
|
||||
delete to_be_written;
|
||||
}
|
||||
}
|
||||
|
||||
{ // raft interface layer
|
||||
@@ -345,7 +390,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
|
||||
GCodePlanner& gcode_layer = *new GCodePlanner(storage, layer_nr, z, layer_height, planner_state, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(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"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
gcode_layer.setExtruder(extruder_nr); // reset to extruder number, because we might have primed in the last layer
|
||||
@@ -358,21 +406,16 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
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, gcode_layer.configs_storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
|
||||
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_comp.generate(raft_polygons, raftLines);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &gcode_layer.configs_storage.raft_interface_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_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.processFanSpeedAndMinimalLayerTime();
|
||||
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_interface_fan_speed"));
|
||||
|
||||
layer_plan_buffer.push(gcode_layer);
|
||||
planner_state = gcode_layer.getPlanningState();
|
||||
GCodePlanner* to_be_written = layer_plan_buffer.processBuffer();
|
||||
if (to_be_written)
|
||||
{
|
||||
to_be_written->writeGCode(gcode);
|
||||
delete to_be_written;
|
||||
}
|
||||
}
|
||||
|
||||
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
|
||||
@@ -382,7 +425,10 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
const int layer_nr = initial_raft_layer_nr + 2 + raftSurfaceLayer - 1; // 2: 1 base layer, 1 interface layer
|
||||
z += layer_height;
|
||||
const int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
|
||||
GCodePlanner& gcode_layer = *new GCodePlanner(storage, layer_nr, z, layer_height, planner_state, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(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"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
@@ -393,25 +439,20 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage, unsigned int t
|
||||
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, gcode_layer.configs_storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle, z, extra_infill_shift);
|
||||
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_comp.generate(raft_polygons, raft_lines);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &gcode_layer.configs_storage.raft_surface_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_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.processFanSpeedAndMinimalLayerTime();
|
||||
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_surface_fan_speed"));
|
||||
|
||||
layer_plan_buffer.push(gcode_layer);
|
||||
planner_state = gcode_layer.getPlanningState();
|
||||
GCodePlanner* to_be_written = layer_plan_buffer.processBuffer();
|
||||
if (to_be_written)
|
||||
{
|
||||
to_be_written->writeGCode(gcode);
|
||||
delete to_be_written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int layer_nr, unsigned int total_layers) const
|
||||
void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, std::max(0, layer_nr) + 1, total_layers);
|
||||
logDebug("GcodeWriter processing layer %i of %i\n", layer_nr, total_layers);
|
||||
@@ -463,14 +504,15 @@ GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int
|
||||
}
|
||||
|
||||
coord_t max_inner_wall_width = 0;
|
||||
for (const SettingsBaseVirtual& mesh_settings : storage.meshes)
|
||||
for (SettingsBaseVirtual& mesh_settings : storage.meshes)
|
||||
{
|
||||
max_inner_wall_width = std::max(max_inner_wall_width, mesh_settings.getSettingInMicrons((mesh_settings.getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"));
|
||||
}
|
||||
int64_t comb_offset_from_outlines = max_inner_wall_width * 2;
|
||||
|
||||
|
||||
GCodePlanner& gcode_layer = *new GCodePlanner(storage, layer_nr, z, layer_thickness, planner_state, 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.createPlanner(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);
|
||||
|
||||
if (include_helper_parts && layer_nr == 0)
|
||||
{ // process the skirt or the brim of the starting extruder.
|
||||
@@ -501,10 +543,10 @@ GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int
|
||||
|
||||
if (layer_nr >= 0)
|
||||
{
|
||||
const std::vector<unsigned int>& mesh_order = mesh_order_per_extruder[extruder_nr];
|
||||
std::vector<unsigned int>& mesh_order = mesh_order_per_extruder[extruder_nr];
|
||||
unsigned int mesh_order_idx_starting_mesh = 0;
|
||||
{ // calculate mesh_order_idx_starting_mesh
|
||||
Point layer_start_position = planner_state.last_position;
|
||||
Point layer_start_position = last_position_planned;
|
||||
if (storage.getSettingBoolean("start_layers_at_same_position"))
|
||||
{
|
||||
layer_start_position = Point(storage.getSettingInMicrons("layer_start_x"), storage.getSettingInMicrons("layer_start_y"));
|
||||
@@ -512,12 +554,12 @@ GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int
|
||||
coord_t best_dist2 = std::numeric_limits<coord_t>::max();
|
||||
for (unsigned int mesh_order_idx = 0; mesh_order_idx < mesh_order.size(); mesh_order_idx++)
|
||||
{
|
||||
const unsigned int mesh_idx = mesh_order[mesh_order_idx];
|
||||
const SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
for (const SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
unsigned int mesh_idx = mesh_order[mesh_order_idx];
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
{
|
||||
const Point middle = (part.boundaryBox.min + part.boundaryBox.max) / 2;
|
||||
const coord_t dist2 = vSize2(middle - layer_start_position);
|
||||
Point middle = (part.boundaryBox.min + part.boundaryBox.max) / 2;
|
||||
coord_t dist2 = vSize2(middle - layer_start_position);
|
||||
if (dist2 < best_dist2)
|
||||
{
|
||||
best_dist2 = dist2;
|
||||
@@ -531,15 +573,14 @@ GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int
|
||||
{
|
||||
unsigned int mesh_order_idx = (mesh_iterator_idx + mesh_order_idx_starting_mesh) % mesh_order.size();
|
||||
unsigned int mesh_idx = mesh_order[mesh_order_idx];
|
||||
const SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
|
||||
const PathConfigStorage::MeshPathConfigs& mesh_config = gcode_layer.configs_storage.mesh_configs[mesh_idx];
|
||||
SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
addMeshLayerToGCode_meshSurfaceMode(storage, mesh, mesh_config, gcode_layer, layer_nr);
|
||||
addMeshLayerToGCode_meshSurfaceMode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addMeshLayerToGCode(storage, mesh, mesh_config, gcode_layer, layer_nr);
|
||||
addMeshLayerToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,13 +597,17 @@ GCodePlanner& FffGcodeWriter::processLayer(const SliceDataStorage& storage, int
|
||||
int prev_extruder = gcode_layer.getExtruder(); // most likely the same extruder as we are extruding with now
|
||||
addPrimeTower(storage, gcode_layer, layer_nr, prev_extruder);
|
||||
}
|
||||
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
|
||||
return gcode_layer;
|
||||
}
|
||||
|
||||
bool FffGcodeWriter::getExtrudersNeedPrimeDuringFirstLayer() const
|
||||
bool FffGcodeWriter::getExtrudersNeedPrimeDuringFirstLayer()
|
||||
{
|
||||
switch(gcode.getFlavor())
|
||||
{
|
||||
@@ -573,7 +618,7 @@ bool FffGcodeWriter::getExtrudersNeedPrimeDuringFirstLayer() const
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::ensureAllExtrudersArePrimed(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr) const
|
||||
void FffGcodeWriter::ensureAllExtrudersArePrimed(SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr)
|
||||
{
|
||||
// Add prime for all extruders which haven't primed yet.
|
||||
|
||||
@@ -587,23 +632,23 @@ void FffGcodeWriter::ensureAllExtrudersArePrimed(const SliceDataStorage& storage
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int extruder_nr) const
|
||||
void FffGcodeWriter::processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int extruder_nr)
|
||||
{
|
||||
if (skirt_brim_is_processed[extruder_nr])
|
||||
{
|
||||
return;
|
||||
}
|
||||
const Polygons& skirt_brim = storage.skirt_brim[extruder_nr];
|
||||
const_cast<bool&>(skirt_brim_is_processed[extruder_nr]) = true; // TODO: resolve const cast!
|
||||
Polygons& skirt_brim = storage.skirt_brim[extruder_nr];
|
||||
skirt_brim_is_processed[extruder_nr] = true;
|
||||
if (skirt_brim.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gcode_layer.addTravel(skirt_brim.back().closestPointTo(gcode_layer.getLastPosition()));
|
||||
gcode_layer.addPolygonsByOptimizer(skirt_brim, &gcode_layer.configs_storage.skirt_brim_config[extruder_nr]);
|
||||
gcode_layer.addTravel(PolygonRef{skirt_brim.back()}.closestPointTo(gcode_layer.getLastPosition()));
|
||||
gcode_layer.addPolygonsByOptimizer(skirt_brim, &storage.skirt_brim_config[extruder_nr]);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr) const
|
||||
void FffGcodeWriter::processOozeShield(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr)
|
||||
{
|
||||
if (layer_nr == 0 && storage.getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::BRIM)
|
||||
{
|
||||
@@ -611,11 +656,11 @@ void FffGcodeWriter::processOozeShield(const SliceDataStorage& storage, GCodePla
|
||||
}
|
||||
if (storage.oozeShield.size() > 0 && layer_nr < storage.oozeShield.size())
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(storage.oozeShield[layer_nr], &gcode_layer.configs_storage.skirt_brim_config[0]); //TODO: Skirt and brim configuration index should correspond to draft shield extruder number.
|
||||
gcode_layer.addPolygonsByOptimizer(storage.oozeShield[layer_nr], &storage.skirt_brim_config[0]); //TODO: Skirt and brim configuration index should correspond to draft shield extruder number.
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processDraftShield(const SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr) const
|
||||
void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr)
|
||||
{
|
||||
if (storage.draft_protection_shield.size() == 0)
|
||||
{
|
||||
@@ -642,10 +687,10 @@ void FffGcodeWriter::processDraftShield(const SliceDataStorage& storage, GCodePl
|
||||
}
|
||||
}
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &gcode_layer.configs_storage.skirt_brim_config[0]); //TODO: Skirt and brim configuration index should correspond to draft shield extruder number.
|
||||
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_brim_config[0]); //TODO: Skirt and brim configuration index should correspond to draft shield extruder number.
|
||||
}
|
||||
|
||||
std::vector<int> FffGcodeWriter::calculateExtruderOrder(const SliceDataStorage& storage, int current_extruder) const
|
||||
std::vector<int> FffGcodeWriter::calculateExtruderOrder(SliceDataStorage& storage, int current_extruder)
|
||||
{
|
||||
int extruder_count = storage.getSettingAsCount("machine_extruder_count");
|
||||
std::vector<int> ret;
|
||||
@@ -662,17 +707,17 @@ std::vector<int> FffGcodeWriter::calculateExtruderOrder(const SliceDataStorage&
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(const SliceDataStorage& storage, int extruder_nr) const
|
||||
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int extruder_nr)
|
||||
{
|
||||
OrderOptimizer<unsigned int> mesh_idx_order_optimizer;
|
||||
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
const SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
if (mesh.getSettingAsIndex("extruder_nr") == extruder_nr)
|
||||
{
|
||||
const Mesh& mesh_data = storage.meshgroup->meshes[mesh_idx];
|
||||
const Point3 middle = (mesh_data.getAABB().min + mesh_data.getAABB().max) / 2;
|
||||
Mesh& mesh_data = storage.meshgroup->meshes[mesh_idx];
|
||||
Point3 middle = (mesh_data.getAABB().min + mesh_data.getAABB().max) / 2;
|
||||
mesh_idx_order_optimizer.addItem(Point(middle.x, middle.y), mesh_idx);
|
||||
}
|
||||
}
|
||||
@@ -687,7 +732,7 @@ std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(const SliceDataStor
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
@@ -714,12 +759,12 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage&
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh_config.inset0_config, nullptr, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), mesh->getSettingBoolean("magic_spiralize"));
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), mesh->getSettingBoolean("magic_spiralize"));
|
||||
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, mesh_config, gcode_layer, layer_nr);
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
const SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
@@ -734,11 +779,11 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(const SliceDataStorage& storage
|
||||
lines.add(p);
|
||||
}
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh_config.inset0_config, SpaceFillType::PolyLines);
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
|
||||
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
@@ -780,7 +825,7 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
|
||||
Point layer_start_position = planner_state.last_position;
|
||||
Point layer_start_position = last_position_planned;
|
||||
if (storage.getSettingBoolean("start_layers_at_same_position"))
|
||||
{
|
||||
layer_start_position = Point(storage.getSettingInMicrons("layer_start_x"), storage.getSettingInMicrons("layer_start_y"));
|
||||
@@ -795,15 +840,15 @@ void FffGcodeWriter::addMeshLayerToGCode(const SliceDataStorage& storage, const
|
||||
for (int part_idx : part_order_optimizer.polyOrder)
|
||||
{
|
||||
const SliceLayerPart& part = layer->parts[part_idx];
|
||||
addMeshPartToGCode(storage, mesh, mesh_config, part, gcode_layer, layer_nr);
|
||||
addMeshPartToGCode(storage, mesh, part, gcode_layer, layer_nr);
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, mesh_config, gcode_layer, layer_nr);
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
bool skin_alternate_rotation = mesh->getSettingBoolean("skin_alternate_rotation") && ( mesh->getSettingAsCount("top_layers") >= 4 || mesh->getSettingAsCount("bottom_layers") >= 4 );
|
||||
|
||||
@@ -825,18 +870,18 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, mesh_config, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
|
||||
processSingleLayerInfill(gcode_layer, mesh, mesh_config, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
|
||||
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);
|
||||
}
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
|
||||
processInsets(gcode_layer, mesh, mesh_config, part, layer_nr, z_seam_type, z_seam_pos);
|
||||
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type, z_seam_pos);
|
||||
|
||||
if (!mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, mesh_config, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
|
||||
processSingleLayerInfill(gcode_layer, mesh, mesh_config, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
|
||||
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);
|
||||
}
|
||||
|
||||
EFillMethod skin_pattern = (layer_nr == 0)?
|
||||
@@ -851,7 +896,7 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S
|
||||
skin_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = mesh->getSettingInMicrons("skin_overlap_mm");
|
||||
processSkinAndPerimeterGaps(gcode_layer, mesh, mesh_config, part, layer_nr, skin_overlap, skin_angle);
|
||||
processSkinAndPerimeterGaps(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle);
|
||||
|
||||
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
|
||||
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
|
||||
@@ -865,7 +910,7 @@ void FffGcodeWriter::addMeshPartToGCode(const SliceDataStorage& storage, const S
|
||||
|
||||
|
||||
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle) const
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
{
|
||||
int64_t z = layer_nr * getSettingInMicrons("layer_height");
|
||||
if (infill_line_distance > 0)
|
||||
@@ -873,7 +918,7 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const Sl
|
||||
//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++)
|
||||
{
|
||||
const unsigned int infill_line_width = mesh_config.infill_config[combine_idx].getLineWidth();
|
||||
const unsigned int infill_line_width = mesh->infill_config[combine_idx].getLineWidth();
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
@@ -890,19 +935,19 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const Sl
|
||||
Infill infill_comp(infill_pattern, part.infill_area_per_combine_per_density[density_idx][combine_idx], 0, infill_line_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift);
|
||||
infill_comp.generate(infill_polygons, infill_lines, mesh);
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh_config.infill_config[combine_idx]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh_config.infill_config[combine_idx], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle) const
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
{
|
||||
if (infill_line_distance == 0 || part.infill_area_per_combine_per_density[0].size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const unsigned int infill_line_width = mesh_config.infill_config[0].getLineWidth();
|
||||
const unsigned int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
//Combine the 1 layer thick infill with the top/bottom skin and print that as one thing.
|
||||
Polygons infill_polygons;
|
||||
@@ -946,18 +991,18 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, const S
|
||||
Infill infill_comp(pattern, part.infill_area_per_combine_per_density[density_idx][0], 0, infill_line_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift);
|
||||
infill_comp.generate(infill_polygons, infill_lines, mesh);
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh_config.infill_config[0]);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh_config.infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh_config.infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos) const
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos)
|
||||
{
|
||||
bool compensate_overlap_0 = mesh->getSettingBoolean("travel_compensate_overlapping_walls_0_enabled");
|
||||
bool compensate_overlap_x = mesh->getSettingBoolean("travel_compensate_overlapping_walls_x_enabled");
|
||||
@@ -974,7 +1019,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
{ // 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_config.insetX_config, wall_overlap_computation, EZSeamType::SHORTEST, z_seam_pos, wall_0_wipe_dist, spiralize);
|
||||
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);
|
||||
}
|
||||
}
|
||||
int processed_inset_number = -1;
|
||||
@@ -990,26 +1035,26 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
if (!compensate_overlap_0)
|
||||
{
|
||||
WallOverlapComputation* wall_overlap_computation(nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh_config.inset0_config, wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons outer_wall = part.insets[0];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh_config.inset0_config, &wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!compensate_overlap_x)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[processed_inset_number], &mesh_config.insetX_config);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[processed_inset_number], &mesh->insetX_config);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons outer_wall = part.insets[processed_inset_number];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_x"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh_config.insetX_config, &wall_overlap_computation);
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->insetX_config, &wall_overlap_computation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,11 +1062,11 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
}
|
||||
|
||||
|
||||
void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int skin_angle) const
|
||||
void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int skin_angle)
|
||||
{
|
||||
int64_t z = layer_nr * getSettingInMicrons("layer_height");
|
||||
const unsigned int skin_line_width = mesh_config.skin_config.getLineWidth();
|
||||
const unsigned int perimeter_gaps_line_width = mesh_config.perimeter_gap_config.getLineWidth();
|
||||
const unsigned int skin_line_width = mesh->skin_config.getLineWidth();
|
||||
const unsigned int perimeter_gaps_line_width = mesh->perimeter_gap_config.getLineWidth();
|
||||
|
||||
constexpr int perimeter_gaps_extra_offset = 15; // extra offset so that the perimeter gaps aren't created everywhere due to rounding errors
|
||||
bool fill_perimeter_gaps = mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE
|
||||
@@ -1063,24 +1108,24 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
{
|
||||
for (const Polygons& skin_perimeter : skin_part.insets)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh_config.insetX_config); // add polygons to gcode in inward order
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->insetX_config); // add polygons to gcode in inward order
|
||||
}
|
||||
if (skin_part.insets.size() > 0)
|
||||
{
|
||||
inner_skin_outline = &skin_part.insets.back();
|
||||
offset_from_inner_skin_outline = -mesh_config.insetX_config.getLineWidth() / 2;
|
||||
offset_from_inner_skin_outline = -mesh->insetX_config.getLineWidth() / 2;
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
{
|
||||
// add perimeter gaps between the outer skin inset and the innermost wall
|
||||
const Polygons outer = skin_part.outline;
|
||||
const Polygons inner = skin_part.insets[0].offset(mesh_config.insetX_config.getLineWidth() / 2 + perimeter_gaps_extra_offset);
|
||||
const Polygons inner = skin_part.insets[0].offset(mesh->insetX_config.getLineWidth() / 2 + perimeter_gaps_extra_offset);
|
||||
perimeter_gaps.add(outer.difference(inner));
|
||||
|
||||
for (unsigned int inset_idx = 1; inset_idx < skin_part.insets.size(); inset_idx++)
|
||||
{ // add perimeter gaps between consecutive skin walls
|
||||
const Polygons outer = skin_part.insets[inset_idx - 1].offset(-1 * mesh_config.insetX_config.getLineWidth() / 2 - perimeter_gaps_extra_offset);
|
||||
const Polygons inner = skin_part.insets[inset_idx].offset(mesh_config.insetX_config.getLineWidth() / 2);
|
||||
const Polygons outer = skin_part.insets[inset_idx - 1].offset(-1 * mesh->insetX_config.getLineWidth() / 2 - perimeter_gaps_extra_offset);
|
||||
const Polygons inner = skin_part.insets[inset_idx].offset(mesh->insetX_config.getLineWidth() / 2);
|
||||
perimeter_gaps.add(outer.difference(inner));
|
||||
}
|
||||
}
|
||||
@@ -1097,15 +1142,15 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
Infill infill_comp(pattern, *inner_skin_outline, offset_from_inner_skin_outline, skin_line_width, skin_line_width, skin_overlap, skin_angle, z, extra_infill_shift, perimeter_gaps_output);
|
||||
infill_comp.generate(skin_polygons, skin_lines);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh_config.skin_config);
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
|
||||
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh_config.skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh_config.skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
@@ -1115,18 +1160,18 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
int offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, perimeter_gaps_line_width, perimeter_gaps_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(gap_polygons, gap_lines);
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh_config.perimeter_gap_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh->perimeter_gap_config, SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
{ // handle perimeter gaps of normal insets
|
||||
Polygons perimeter_gaps;
|
||||
int line_width = mesh_config.inset0_config.getLineWidth();
|
||||
int line_width = mesh->inset0_config.getLineWidth();
|
||||
for (unsigned int inset_idx = 0; inset_idx < part.insets.size() - 1; inset_idx++)
|
||||
{
|
||||
const Polygons outer = part.insets[inset_idx].offset(-1 * line_width / 2 - perimeter_gaps_extra_offset);
|
||||
line_width = mesh_config.insetX_config.getLineWidth();
|
||||
line_width = mesh->insetX_config.getLineWidth();
|
||||
|
||||
Polygons inner = part.insets[inset_idx + 1].offset(line_width / 2);
|
||||
perimeter_gaps.add(outer.difference(inner));
|
||||
@@ -1156,11 +1201,11 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, perimeter_gaps_line_width, perimeter_gaps_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(gap_polygons, gap_lines);
|
||||
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh_config.perimeter_gap_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh->perimeter_gap_config, SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr) const
|
||||
bool FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
{
|
||||
bool support_added = false;
|
||||
if (!storage.support.generated || layer_nr > storage.support.layer_nr_max_filled_layer)
|
||||
@@ -1171,7 +1216,7 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, GCodePla
|
||||
int support_interface_extruder_nr = getSettingAsIndex("support_interface_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr <= 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
const SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
|
||||
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
|
||||
if (support_layer.skin.size() == 0 && support_layer.supportAreas.size() == 0)
|
||||
{
|
||||
return support_added;
|
||||
@@ -1189,14 +1234,12 @@ bool FffGcodeWriter::addSupportToGCode(const SliceDataStorage& storage, GCodePla
|
||||
return support_added;
|
||||
}
|
||||
|
||||
bool FffGcodeWriter::addSupportInfillToGCode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
bool FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, layer_nr)]; // account for negative layer numbers for raft filler layers
|
||||
|
||||
bool added = false;
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
|| support_layer.supportAreas.size() == 0)
|
||||
|| storage.support.supportLayers[layer_nr].supportAreas.size() == 0)
|
||||
{
|
||||
return added;
|
||||
}
|
||||
@@ -1205,21 +1248,21 @@ bool FffGcodeWriter::addSupportInfillToGCode(const SliceDataStorage& storage, GC
|
||||
|
||||
const ExtruderTrain& infill_extr = *storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
|
||||
int support_line_distance = infill_extr.getSettingInMicrons("support_line_distance"); // first layer line distance must be the same as the second layer line distance
|
||||
const int support_line_width = gcode_layer.configs_storage.support_infill_config.getLineWidth();
|
||||
const int support_line_width = storage.support_config.getLineWidth();
|
||||
EFillMethod support_pattern = infill_extr.getSettingAsFillMethod("support_pattern"); // first layer pattern must be same as other layers
|
||||
if (layer_nr <= 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
|
||||
|
||||
int infill_extruder_nr_here = (layer_nr <= 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
const ExtruderTrain& infill_extr_here = *storage.meshgroup->getExtruderTrain(infill_extruder_nr_here);
|
||||
|
||||
const Polygons& support = support_layer.supportAreas;
|
||||
Polygons& support = storage.support.supportLayers[std::max(0, layer_nr)].supportAreas;
|
||||
|
||||
std::vector<PolygonsPart> support_islands = support.splitIntoParts();
|
||||
|
||||
PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPosition());
|
||||
for(unsigned int n=0; n<support_islands.size(); n++)
|
||||
{
|
||||
island_order_optimizer.addPolygon(support_islands[n][0]);
|
||||
island_order_optimizer.addPolygon(PolygonRef{support_islands[n][0]});
|
||||
}
|
||||
island_order_optimizer.optimize();
|
||||
|
||||
@@ -1236,7 +1279,7 @@ bool FffGcodeWriter::addSupportInfillToGCode(const SliceDataStorage& storage, GC
|
||||
if (boundary.size() > 0)
|
||||
{
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, infill_extruder_nr_here); // only switch extruder if we're sure we're going to switch
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &gcode_layer.configs_storage.support_infill_config);
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
|
||||
}
|
||||
offset_from_outline = -support_line_width;
|
||||
support_infill_overlap = infill_extr_here.getSettingInMicrons("infill_overlap_mm"); // support lines area should be expanded outward to overlap with the boundary polygon
|
||||
@@ -1253,22 +1296,20 @@ bool FffGcodeWriter::addSupportInfillToGCode(const SliceDataStorage& storage, GC
|
||||
if (support_lines.size() > 0 || support_polygons.size() > 0)
|
||||
{
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, infill_extruder_nr_here); // only switch extruder if we're sure we're going to switch
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &gcode_layer.configs_storage.support_infill_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &gcode_layer.configs_storage.support_infill_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr) const
|
||||
bool FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
const SupportLayer& support_layer = storage.support.supportLayers[std::max(0, layer_nr)];
|
||||
|
||||
bool added = false;
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
|| support_layer.skin.size() == 0)
|
||||
|| storage.support.supportLayers[std::max(0, layer_nr)].skin.size() == 0)
|
||||
{
|
||||
return added;
|
||||
}
|
||||
@@ -1283,7 +1324,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, GCo
|
||||
|
||||
|
||||
bool all_roofs_are_low = true;
|
||||
for (const SliceMeshStorage& mesh : storage.meshes)
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingInMicrons("support_roof_height") >= 2 * getSettingInMicrons("layer_height"))
|
||||
{
|
||||
@@ -1312,7 +1353,7 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, GCo
|
||||
Polygons* perimeter_gaps = nullptr;
|
||||
bool use_endpieces = true;
|
||||
bool connected_zigzags = false;
|
||||
Infill infill_comp(pattern, support_layer.skin, outline_offset, gcode_layer.configs_storage.support_interface_config.getLineWidth(), support_line_distance, support_skin_overlap, fillAngle, z, extra_infill_shift, perimeter_gaps, connected_zigzags, use_endpieces);
|
||||
Infill infill_comp(pattern, storage.support.supportLayers[std::max(0, layer_nr)].skin, outline_offset, storage.support_skin_config.getLineWidth(), support_line_distance, support_skin_overlap, fillAngle, z, extra_infill_shift, perimeter_gaps, connected_zigzags, use_endpieces);
|
||||
Polygons support_polygons;
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines);
|
||||
@@ -1320,14 +1361,14 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, GCo
|
||||
if (support_lines.size() > 0 || support_polygons.size() > 0)
|
||||
{
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, skin_extruder_nr);
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &gcode_layer.configs_storage.support_interface_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &gcode_layer.configs_storage.support_interface_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_skin_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
added = true;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr) const
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
{
|
||||
if (extruder_nr == -1) // an object with extruder_nr==-1 means it will be printed with any current nozzle
|
||||
return;
|
||||
@@ -1338,7 +1379,7 @@ void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, GCode
|
||||
|
||||
if (extruder_changed)
|
||||
{
|
||||
if (!const_cast<bool&>(extruder_prime_is_planned[extruder_nr])) // TODO: const cast!
|
||||
if (!extruder_prime_is_planned[extruder_nr])
|
||||
{
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
|
||||
@@ -1349,7 +1390,7 @@ void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, GCode
|
||||
|
||||
gcode_layer.planPrime();
|
||||
|
||||
const_cast<bool&>(extruder_prime_is_planned[extruder_nr]) = true; // TODO: const cast!
|
||||
extruder_prime_is_planned[extruder_nr] = true;
|
||||
}
|
||||
|
||||
assert(extruder_prime_is_planned[extruder_nr] && "extruders should be primed before they are used!");
|
||||
@@ -1364,7 +1405,7 @@ void FffGcodeWriter::setExtruder_addPrime(const SliceDataStorage& storage, GCode
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addPrimeTower(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int prev_extruder) const
|
||||
void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int prev_extruder)
|
||||
{
|
||||
if (!getSettingBoolean("prime_tower_enable"))
|
||||
{
|
||||
|
||||
+42
-41
@@ -74,19 +74,18 @@ private:
|
||||
|
||||
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.
|
||||
|
||||
|
||||
GCodePlanner::PlanningState planner_state;
|
||||
|
||||
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
|
||||
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
|
||||
public:
|
||||
FffGcodeWriter(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, max_object_height(0)
|
||||
, layer_plan_buffer(this, gcode)
|
||||
, extruder_prime_is_planned {} // initialize all values in array with [false]
|
||||
, planner_state{ no_point
|
||||
, 0 // changed somewhere early in FffGcodeWriter::writeGCode
|
||||
, false
|
||||
}
|
||||
, last_position_planned(no_point)
|
||||
, current_extruder_planned(0) // changed somewhere early in FffGcodeWriter::writeGCode
|
||||
, is_inside_mesh_layer_part(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -173,6 +172,17 @@ private:
|
||||
* \param[out] storage The data storage to which to save the configurations
|
||||
*/
|
||||
void setConfigRetraction(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Initialize the GcodePathConfig config parameters which don't change over
|
||||
* all layers, for each feature.
|
||||
*
|
||||
* The features are: skirt or brim, support and for each mesh: outer wall,
|
||||
* inner walls, skin, infill (and combined infill).
|
||||
*
|
||||
* \param[out] storage The data storage to which to save the configurations.
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Get the extruder with which to start the print.
|
||||
@@ -182,7 +192,7 @@ private:
|
||||
*
|
||||
* \param[in] storage where to get settings from.
|
||||
*/
|
||||
unsigned int getStartExtruder(const SliceDataStorage& storage);
|
||||
unsigned int getStartExtruder(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
@@ -192,14 +202,14 @@ private:
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param[in] start_extruder_nr The extruder with which to start the print.
|
||||
*/
|
||||
void processStartingCode(const SliceDataStorage& storage, const unsigned int start_extruder_nr);
|
||||
void processStartingCode(SliceDataStorage& storage, const unsigned int start_extruder_nr);
|
||||
|
||||
/*!
|
||||
* Move up and over the already printed meshgroups to print the next meshgroup.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
*/
|
||||
void processNextMeshGroupCode(const SliceDataStorage& storage);
|
||||
void processNextMeshGroupCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
|
||||
@@ -207,7 +217,7 @@ private:
|
||||
* \param[in,out] storage where the slice data is stored.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(const SliceDataStorage& storage, unsigned int total_layers);
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
|
||||
@@ -218,16 +228,15 @@ private:
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param total_layers The total number of layers.
|
||||
* \return The layer plans
|
||||
*/
|
||||
GCodePlanner& processLayer(const SliceDataStorage& storage, int layer_nr, unsigned int total_layers) const;
|
||||
void processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Whether the extruders need to be primed separately just before they are used.
|
||||
*
|
||||
* \return whether the extruders need to be primed separately just before they are used
|
||||
*/
|
||||
bool getExtrudersNeedPrimeDuringFirstLayer() const;
|
||||
bool getExtrudersNeedPrimeDuringFirstLayer();
|
||||
|
||||
/*!
|
||||
* Plan priming of all used extruders which haven't been primed yet
|
||||
@@ -235,7 +244,7 @@ private:
|
||||
* \param layer_plan The initial planning of the g-code of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void ensureAllExtrudersArePrimed(const SliceDataStorage& storage, GCodePlanner& layer_plan, const int layer_nr) const;
|
||||
void ensureAllExtrudersArePrimed(SliceDataStorage& storage, GCodePlanner& layer_plan, const int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the skirt or the brim to the layer plan \p gcodeLayer.
|
||||
@@ -245,7 +254,7 @@ private:
|
||||
* \param extruder_nr The extruder train for which to process the skirt or
|
||||
* brim.
|
||||
*/
|
||||
void processSkirtBrim(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr) const;
|
||||
void processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Adds the ooze shield to the layer plan \p gcodeLayer.
|
||||
@@ -254,7 +263,7 @@ private:
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processOozeShield(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr) const;
|
||||
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Adds the draft protection screen to the layer plan \p gcodeLayer.
|
||||
@@ -263,7 +272,7 @@ private:
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processDraftShield(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr) const;
|
||||
void processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Calculate in which order to plan the extruders
|
||||
@@ -272,7 +281,7 @@ private:
|
||||
* \param current_extruder The current extruder with which we last printed
|
||||
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
|
||||
*/
|
||||
std::vector<int> calculateExtruderOrder(const SliceDataStorage& storage, int current_extruder) const;
|
||||
std::vector<int> calculateExtruderOrder(SliceDataStorage& storage, int current_extruder);
|
||||
|
||||
/*!
|
||||
* Calculate in which order to plan the meshes of a specific extruder
|
||||
@@ -281,95 +290,88 @@ private:
|
||||
* \param extruder_nr The extruder for which to determine the order
|
||||
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
|
||||
*/
|
||||
std::vector<unsigned int> calculateMeshOrder(const SliceDataStorage& storage, int extruder_nr) const;
|
||||
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh_config the line config with which to print a print feature
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcodeLayer, int layer_nr) const;
|
||||
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh_config the line config with which to print a print feature
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshOpenPolyLinesToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const;
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcode_layer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcode_layer.
|
||||
* \param mesh_config the line config with which to print a print feature
|
||||
* \param gcode_layer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const;
|
||||
void addMeshLayerToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcode_layer.
|
||||
* \param mesh_config the line config with which to print a print feature
|
||||
* \param part The part to add
|
||||
* \param gcode_layer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr) const;
|
||||
void addMeshPartToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add thicker (multiple layers) 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 mesh_config the line config with which to print a print feature
|
||||
* \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 processMultiLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle) const;
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
|
||||
/*!
|
||||
* Add normal sparse infill for a given part in a layer.
|
||||
* \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 mesh_config the line config with which to print a print feature
|
||||
* \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 processSingleLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle) const;
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
|
||||
/*!
|
||||
* Generate the insets for the walls of a given layer part.
|
||||
* \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 mesh_config the line config with which to print a print feature
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
|
||||
* \param z_seam_pos The location near where to start the outer inset in case \p z_seam_type is 'back'
|
||||
*/
|
||||
void processInsets(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos) const;
|
||||
void processInsets(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos);
|
||||
|
||||
|
||||
/*!
|
||||
@@ -380,13 +382,12 @@ private:
|
||||
*
|
||||
* \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 mesh_config the line config with which to print a print feature
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param skin_overlap The distance by which the skin overlaps with the wall insets and the distance by which the perimeter gaps overlap with adjacent print features.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
*/
|
||||
void processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle) const;
|
||||
void processSkinAndPerimeterGaps(cura::GCodePlanner& gcode_layer, const cura::SliceMeshStorage* mesh, const cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
|
||||
|
||||
/*!
|
||||
* Add the support to the layer plan \p gcodeLayer of the current layer for all support parts with the given \p extruder_nr.
|
||||
@@ -395,7 +396,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \return whether any support was added to the layer plan
|
||||
*/
|
||||
bool addSupportToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr) const;
|
||||
bool addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr);
|
||||
/*!
|
||||
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
@@ -403,7 +404,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \return whether any support infill was added to the layer plan
|
||||
*/
|
||||
bool addSupportInfillToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr) const;
|
||||
bool addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
/*!
|
||||
* Add the support skins to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
@@ -411,7 +412,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \return whether any support skin was added to the layer plan
|
||||
*/
|
||||
bool addSupportRoofsToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr) const;
|
||||
bool addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
|
||||
@@ -423,7 +424,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param extruder_nr The extruder to which to switch
|
||||
*/
|
||||
void setExtruder_addPrime(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr) const;
|
||||
void setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Add the prime tower gcode for the current layer.
|
||||
@@ -432,7 +433,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param prev_extruder The current extruder with which we last printed.
|
||||
*/
|
||||
void addPrimeTower(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder) const;
|
||||
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
|
||||
|
||||
/*!
|
||||
* Add the end gcode and set all temperatures to zero.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "progress/ProgressEstimator.h"
|
||||
#include "progress/ProgressStageEstimator.h"
|
||||
#include "progress/ProgressEstimatorLinear.h"
|
||||
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -32,6 +32,17 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
#pragma omp parallel
|
||||
{
|
||||
#pragma omp master
|
||||
{
|
||||
#ifdef _OPENMP
|
||||
log("OpenMP enabled, number of threads used: %u\n", omp_get_num_threads());
|
||||
#else
|
||||
log("OpenMP disabled\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
@@ -346,9 +357,9 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
|
||||
|
||||
|
||||
// walls
|
||||
unsigned int processed_layer_count = 0;
|
||||
int processed_layer_count = 0;
|
||||
#pragma omp parallel for default(none) shared(mesh_layer_count, mesh, inset_skin_progress_estimate, processed_layer_count) schedule(dynamic)
|
||||
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
for(unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
{
|
||||
logDebug("Processing insets for layer %i of %i\n", layer_number, mesh_layer_count);
|
||||
processInsets(mesh, layer_number);
|
||||
@@ -420,7 +431,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
|
||||
#pragma omp atomic
|
||||
processed_layer_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order)
|
||||
@@ -459,7 +470,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
|
||||
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]);
|
||||
outline_part_here.add(PolygonRef{new_outline[0]});
|
||||
new_parts.push_back(outline_part_here);
|
||||
}
|
||||
else if (new_outline.size() > 1)
|
||||
@@ -520,6 +531,8 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateInsets only reads and writes data for the current layer
|
||||
*
|
||||
* processInsets only reads and writes data for the current layer
|
||||
*/
|
||||
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
|
||||
|
||||
+57
-26
@@ -6,34 +6,65 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
GCodePathConfig::GCodePathConfig(const GCodePathConfig& other)
|
||||
: type(other.type)
|
||||
, speed_derivatives(other.speed_derivatives)
|
||||
, line_width(other.line_width)
|
||||
, layer_thickness(other.layer_thickness)
|
||||
, flow(other.flow)
|
||||
, extrusion_mm3_per_mm(other.extrusion_mm3_per_mm)
|
||||
GCodePathConfig::BasicConfig::BasicConfig()
|
||||
: speed(0)
|
||||
, acceleration(0)
|
||||
, jerk(0)
|
||||
, line_width(0)
|
||||
, flow(100)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
GCodePathConfig::GCodePathConfig(PrintFeatureType type, int line_width, int layer_height, double flow, GCodePathConfig::SpeedDerivatives speed_derivatives)
|
||||
: type(type)
|
||||
, speed_derivatives(speed_derivatives)
|
||||
GCodePathConfig::BasicConfig::BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow)
|
||||
: speed(speed)
|
||||
, acceleration(acceleration)
|
||||
, jerk(jerk)
|
||||
, line_width(line_width)
|
||||
, layer_thickness(layer_height)
|
||||
, flow(flow)
|
||||
, extrusion_mm3_per_mm(calculateExtrusion())
|
||||
{
|
||||
}
|
||||
|
||||
void GCodePathConfig::smoothSpeed(GCodePathConfig::SpeedDerivatives first_layer_config, int layer_nr, int max_speed_layer_nr)
|
||||
void GCodePathConfig::BasicConfig::set(double speed, double acceleration, double jerk, int line_width, double flow)
|
||||
{
|
||||
double max_speed_layer = max_speed_layer_nr;
|
||||
speed_derivatives.speed = (speed_derivatives.speed * layer_nr) / max_speed_layer + (first_layer_config.speed * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
speed_derivatives.acceleration = (speed_derivatives.acceleration * layer_nr) / max_speed_layer + (first_layer_config.acceleration * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
speed_derivatives.jerk = (speed_derivatives.jerk * layer_nr) / max_speed_layer + (first_layer_config.jerk * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
this->speed = speed;
|
||||
this->acceleration = acceleration;
|
||||
this->jerk = jerk;
|
||||
this->line_width = line_width;
|
||||
this->flow = flow;
|
||||
}
|
||||
|
||||
|
||||
GCodePathConfig::GCodePathConfig(PrintFeatureType type)
|
||||
: extrusion_mm3_per_mm(0.0)
|
||||
, type(type)
|
||||
{
|
||||
}
|
||||
|
||||
void GCodePathConfig::init(double speed, double acceleration, double jerk, int line_width, double flow)
|
||||
{
|
||||
iconic_config.set(speed, acceleration, jerk, line_width, flow);
|
||||
current_config = iconic_config;
|
||||
}
|
||||
|
||||
void GCodePathConfig::setLayerHeight(int layer_height)
|
||||
{
|
||||
this->layer_thickness = layer_height;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
void GCodePathConfig::smoothSpeed(GCodePathConfig::BasicConfig first_layer_config, int layer_nr, double max_speed_layer)
|
||||
{
|
||||
current_config.speed = (iconic_config.speed * layer_nr) / max_speed_layer + (first_layer_config.speed * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
current_config.acceleration = (iconic_config.acceleration * layer_nr) / max_speed_layer + (first_layer_config.acceleration * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
current_config.jerk = (iconic_config.jerk * layer_nr) / max_speed_layer + (first_layer_config.jerk * (max_speed_layer - layer_nr) / max_speed_layer);
|
||||
}
|
||||
|
||||
void GCodePathConfig::setSpeedIconic()
|
||||
{
|
||||
current_config.speed = iconic_config.speed;
|
||||
current_config.acceleration = iconic_config.acceleration;
|
||||
current_config.jerk = iconic_config.jerk;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getExtrusionMM3perMM() const
|
||||
@@ -43,37 +74,37 @@ double GCodePathConfig::getExtrusionMM3perMM() const
|
||||
|
||||
double GCodePathConfig::getSpeed() const
|
||||
{
|
||||
return speed_derivatives.speed;
|
||||
return current_config.speed;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getAcceleration() const
|
||||
{
|
||||
return speed_derivatives.acceleration;
|
||||
return current_config.acceleration;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getJerk() const
|
||||
{
|
||||
return speed_derivatives.jerk;
|
||||
return current_config.jerk;
|
||||
}
|
||||
|
||||
int GCodePathConfig::getLineWidth() const
|
||||
{
|
||||
return line_width;
|
||||
return current_config.line_width;
|
||||
}
|
||||
|
||||
bool GCodePathConfig::isTravelPath() const
|
||||
{
|
||||
return line_width == 0;
|
||||
return current_config.line_width == 0;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getFlowPercentage() const
|
||||
{
|
||||
return flow;
|
||||
return current_config.flow;
|
||||
}
|
||||
|
||||
double GCodePathConfig::calculateExtrusion() const
|
||||
void GCodePathConfig::calculateExtrusion()
|
||||
{
|
||||
return INT2MM(line_width) * INT2MM(layer_thickness) * double(flow) / 100.0;
|
||||
extrusion_mm3_per_mm = INT2MM(current_config.line_width) * INT2MM(layer_thickness) * double(current_config.flow) / 100.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+40
-16
@@ -16,41 +16,65 @@ class GCodePathConfig
|
||||
friend class GCodePlannerTest;
|
||||
public:
|
||||
/*!
|
||||
* A simple wrapper class for all derivatives of position which are used when printing a line
|
||||
* The path config settings which may change from layer to layer
|
||||
*/
|
||||
struct SpeedDerivatives
|
||||
struct BasicConfig
|
||||
{
|
||||
double speed; //!< movement speed (mm/s)
|
||||
double acceleration; //!< acceleration of head movement (mm/s^2)
|
||||
double jerk; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s)
|
||||
double jerk; //!< jerk of the head movement (around stand still) (mm/s^3)
|
||||
int line_width; //!< width of the line extruded
|
||||
double flow; //!< extrusion flow modifier in %
|
||||
BasicConfig(); //!< basic contructor initializing with inaccurate values
|
||||
BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow); //!< basic contructor initializing all values
|
||||
void set(double speed, double acceleration, double jerk, int line_width, double flow); //!< Set all config values
|
||||
};
|
||||
const PrintFeatureType type; //!< name of the feature type
|
||||
private:
|
||||
SpeedDerivatives speed_derivatives; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called.
|
||||
const int line_width; //!< width of the line extruded
|
||||
const int layer_thickness; //!< current layer height in micron
|
||||
const double flow; //!< extrusion flow modifier in %
|
||||
const double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed
|
||||
BasicConfig iconic_config; //!< The basic path configuration iconic to this print feature type
|
||||
BasicConfig current_config; //!< The current path configuration for the current layer
|
||||
int layer_thickness; //!< current layer height in micron
|
||||
double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed
|
||||
public:
|
||||
GCodePathConfig(PrintFeatureType type, int line_width, int layer_height, double flow, SpeedDerivatives speed_derivatives); // , SpeedDerivatives slowdown_speed_derivatives, int layer_nr, int max_speed_layer_nr);
|
||||
const PrintFeatureType type; //!< name of the feature type
|
||||
|
||||
/*!
|
||||
* copy constructor
|
||||
* Basic constructor.
|
||||
*/
|
||||
GCodePathConfig(const GCodePathConfig& other);
|
||||
GCodePathConfig(PrintFeatureType type);
|
||||
|
||||
/*!
|
||||
* Initialize some of the member variables.
|
||||
*
|
||||
* \warning GCodePathConfig::setLayerHeight still has to be called before this object can be used.
|
||||
*
|
||||
* \param speed The regular speed with which to print this feature
|
||||
* \param line_width The line width for this feature
|
||||
* \param flow The flow modifier to apply to the extruded filament when printing this feature
|
||||
*/
|
||||
void init(double speed, double acceleration, double jerk, int line_width, double flow);
|
||||
|
||||
/*!
|
||||
* Set the layer height and (re)compute the extrusion_per_mm
|
||||
*/
|
||||
void setLayerHeight(int layer_height);
|
||||
|
||||
/*!
|
||||
* Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed.
|
||||
*
|
||||
* \warning This functions should not be called with @p layer_nr > @p max_speed_layer !
|
||||
*
|
||||
* \warning Calling this function twice will smooth the speed more toward \p first_layer_config
|
||||
*
|
||||
* \param first_layer_config The speed settings at layer zero
|
||||
* \param layer_nr The layer number
|
||||
* \param max_speed_layer The layer number for which the speed_iconic should be used.
|
||||
*/
|
||||
void smoothSpeed(SpeedDerivatives first_layer_config, int layer_nr, int max_speed_layer);
|
||||
void smoothSpeed(BasicConfig first_layer_config, int layer_nr, double max_speed_layer);
|
||||
|
||||
/*!
|
||||
* Set the speed config to the iconic speed config, i.e. the normal speed of the feature type for which this is a config.
|
||||
*
|
||||
* Does the same for acceleration and jerk.
|
||||
*/
|
||||
void setSpeedIconic();
|
||||
|
||||
/*!
|
||||
* Can only be called after the layer height has been set (which is done while writing the gcode!)
|
||||
@@ -79,7 +103,7 @@ public:
|
||||
double getFlowPercentage() const;
|
||||
|
||||
private:
|
||||
double calculateExtrusion() const;
|
||||
void calculateExtrusion();
|
||||
};
|
||||
|
||||
|
||||
|
||||
+48
-13
@@ -7,6 +7,31 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
void issueWriteGCode_impl(
|
||||
GCodeExport* p_gcode,
|
||||
GCodePlanner* p_front_buffer
|
||||
){
|
||||
#pragma omp task default(none) firstprivate(p_gcode, p_front_buffer)
|
||||
{ MULTITHREAD_TASK_CATCH_EXCEPTION(
|
||||
GCodeExport& gcode_ref = *p_gcode;
|
||||
#ifdef _OPENMP
|
||||
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode_ref.getOutputStreamLock());
|
||||
#endif
|
||||
p_front_buffer->writeGCode(gcode_ref);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
)}
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::issueWriteGCode()
|
||||
{
|
||||
assert(!(buffer.front().isGCodeWritten()) && "GCode shouldn't be written more than once");
|
||||
GCodeExport* p_gcode = &gcode;
|
||||
GCodePlanner* p_front_buffer = &buffer.front();
|
||||
issueWriteGCode_impl(p_gcode, p_front_buffer);
|
||||
}
|
||||
|
||||
|
||||
void LayerPlanBuffer::flush()
|
||||
@@ -15,9 +40,13 @@ void LayerPlanBuffer::flush()
|
||||
{
|
||||
insertTempCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode.getOutputStreamLock());
|
||||
#endif
|
||||
while (!buffer.empty())
|
||||
{
|
||||
buffer.front()->writeGCode(gcode);
|
||||
buffer.front().writeGCode(gcode);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
@@ -30,9 +59,10 @@ void LayerPlanBuffer::flush()
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
{
|
||||
double acc_time = 0.0;
|
||||
for (unsigned int path_idx = extruder_plan_before.paths.size() - 1; int(path_idx) != -1 ; path_idx--)
|
||||
std::vector<GCodePath>& extruder_plan_before_paths = extruder_plan_before.getPaths();
|
||||
for (unsigned int path_idx = extruder_plan_before_paths.size() - 1; int(path_idx) != -1 ; path_idx--)
|
||||
{
|
||||
GCodePath& path = extruder_plan_before.paths[path_idx];
|
||||
GCodePath& path = extruder_plan_before_paths[path_idx];
|
||||
const double time_this_path = path.estimates.getTotalTime();
|
||||
acc_time += time_this_path;
|
||||
if (acc_time > time_after_extruder_plan_start)
|
||||
@@ -195,9 +225,9 @@ void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan)
|
||||
if (preheat_config.getInitialPrintTemp(extruder) != 0)
|
||||
{ // handle heating from initial_print_temperature to printing_tempreature
|
||||
unsigned int path_idx;
|
||||
for (path_idx = 0; path_idx < extruder_plan.paths.size(); path_idx++)
|
||||
for (path_idx = 0; path_idx < extruder_plan.getPaths().size(); path_idx++)
|
||||
{
|
||||
GCodePath& path = extruder_plan.paths[path_idx];
|
||||
GCodePath& path = extruder_plan.getPaths()[path_idx];
|
||||
heated_pre_travel_time += path.estimates.getTotalTime();
|
||||
if (!path.isTravelPath())
|
||||
{
|
||||
@@ -224,9 +254,9 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
double heated_post_travel_time = 0; // The time after the last extrude move toward the end of the extruder plan during which the nozzle is stable at the final print temperature
|
||||
{ // compute heated_post_travel_time
|
||||
unsigned int path_idx;
|
||||
for (path_idx = last_extruder_plan.paths.size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
for (path_idx = last_extruder_plan.getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
{
|
||||
GCodePath& path = last_extruder_plan.paths[path_idx];
|
||||
GCodePath& path = last_extruder_plan.getPaths()[path_idx];
|
||||
if (!path.isTravelPath())
|
||||
{
|
||||
break;
|
||||
@@ -306,9 +336,9 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
{ // insert temp command in precool_extruder_plan
|
||||
double extrusion_time_seen = 0;
|
||||
unsigned int path_idx;
|
||||
for (path_idx = precool_extruder_plan->paths.size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
for (path_idx = precool_extruder_plan->getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
{
|
||||
GCodePath& path = precool_extruder_plan->paths[path_idx];
|
||||
GCodePath& path = precool_extruder_plan->getPaths()[path_idx];
|
||||
extrusion_time_seen += path.estimates.getTotalTime();
|
||||
if (extrusion_time_seen >= cool_down_time)
|
||||
{
|
||||
@@ -324,17 +354,22 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
|
||||
void LayerPlanBuffer::insertTempCommands()
|
||||
{
|
||||
if (buffer.back()->extruder_plans.size() == 0 || (buffer.back()->extruder_plans.size() == 1 && buffer.back()->extruder_plans[0].paths.size() == 0))
|
||||
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].getPathsList().empty()))
|
||||
{ // disregard empty layer
|
||||
buffer.pop_back();
|
||||
return;
|
||||
}
|
||||
for (ExtruderPlan& plan: buffer.back().extruder_plans)
|
||||
{
|
||||
plan.convertListToVector();
|
||||
}
|
||||
|
||||
|
||||
std::vector<ExtruderPlan*> extruder_plans;
|
||||
extruder_plans.reserve(buffer.size() * 2);
|
||||
for (GCodePlanner* layer_plan : buffer)
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
{
|
||||
for (ExtruderPlan& extr_plan : layer_plan->extruder_plans)
|
||||
for (ExtruderPlan& extr_plan : layer_plan.extruder_plans)
|
||||
{
|
||||
extruder_plans.push_back(&extr_plan);
|
||||
}
|
||||
@@ -342,7 +377,7 @@ void LayerPlanBuffer::insertTempCommands()
|
||||
|
||||
|
||||
// insert commands for all extruder plans on this layer
|
||||
GCodePlanner& layer_plan = *buffer.back();
|
||||
GCodePlanner& layer_plan = buffer.back();
|
||||
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
|
||||
{
|
||||
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
|
||||
|
||||
+26
-24
@@ -42,7 +42,7 @@ class LayerPlanBuffer : SettingsMessenger
|
||||
|
||||
std::vector<bool> extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the printing_temperature
|
||||
public:
|
||||
std::list<GCodePlanner*> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
@@ -54,39 +54,41 @@ public:
|
||||
{
|
||||
preheat_config.setConfig(settings);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Push a new layer plan into the buffer
|
||||
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
|
||||
*/
|
||||
void push(GCodePlanner& layer_plan)
|
||||
{
|
||||
buffer.push_back(&layer_plan);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Process all layers in the buffer
|
||||
* This inserts the temperature commands to start warming for a given layer in earlier layers
|
||||
*
|
||||
* Pop out the earliest layer in the buffer if the buffer size is exceeded
|
||||
* \return A nullptr or the popped gcode_layer
|
||||
*/
|
||||
GCodePlanner* processBuffer()
|
||||
template<typename... Args>
|
||||
GCodePlanner& createPlanner(Args&&... constructor_args)
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertTempCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
|
||||
}
|
||||
buffer.emplace_back(constructor_args...);
|
||||
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
GCodePlanner* ret = buffer.front();
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
buffer.pop_front();
|
||||
return ret;
|
||||
issueWriteGCode();
|
||||
}
|
||||
return buffer.back();
|
||||
}
|
||||
|
||||
/*
|
||||
* Write GCode for the oldest layer plan.
|
||||
*/
|
||||
void issueWriteGCode();
|
||||
|
||||
/*
|
||||
* Pop back the oldest layer plan if it exceeds the buffer size and it has been written to gcode.
|
||||
*/
|
||||
void trimBuffer()
|
||||
{
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
assert(buffer.front().isGCodeWritten() && "GCode should be written before planner is discarded");
|
||||
buffer.pop_front();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -17,7 +17,7 @@ class MergeInfillLines
|
||||
std::vector<GCodePath>& paths; //!< The paths currently under consideration
|
||||
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
|
||||
|
||||
const GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
|
||||
bool speed_equalize_flow_enabled; //!< Should the speed be varied with extrusion width
|
||||
double speed_equalize_flow_max; //!< Maximum speed when adjusting speed for flow
|
||||
@@ -64,18 +64,9 @@ public:
|
||||
/*!
|
||||
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
|
||||
*/
|
||||
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, const GCodePathConfig& travelConfig, int64_t nozzle_size, bool speed_equalize_flow_enabled, double speed_equalize_flow_max)
|
||||
: gcode(gcode)
|
||||
, layer_nr(layer_nr)
|
||||
, paths(paths)
|
||||
, extruder_plan(extruder_plan)
|
||||
, travelConfig(travelConfig)
|
||||
, nozzle_size(nozzle_size)
|
||||
, speed_equalize_flow_enabled(speed_equalize_flow_enabled)
|
||||
, speed_equalize_flow_max(speed_equalize_flow_max)
|
||||
{
|
||||
}
|
||||
|
||||
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size, bool speed_equalize_flow_enabled, double speed_equalize_flow_max)
|
||||
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size), speed_equalize_flow_enabled(speed_equalize_flow_enabled), speed_equalize_flow_max(speed_equalize_flow_max) { }
|
||||
|
||||
/*!
|
||||
* Check for lots of small moves and combine them into one large line.
|
||||
* Updates \p path_idx to the next path which is not combined.
|
||||
|
||||
+51
-14
@@ -15,7 +15,12 @@ namespace cura
|
||||
PrimeTower::PrimeTower(const SliceDataStorage& storage)
|
||||
: is_hollow(false)
|
||||
, wipe_from_middle(false)
|
||||
, current_pre_wipe_location_idx(0)
|
||||
{
|
||||
for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
|
||||
{
|
||||
last_prime_tower_poly_printed[extruder_nr] = -1;
|
||||
}
|
||||
enabled = storage.getSettingBoolean("prime_tower_enable")
|
||||
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 10;
|
||||
@@ -25,6 +30,34 @@ PrimeTower::PrimeTower(const SliceDataStorage& storage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrimeTower::initConfigs(const MeshGroup* meshgroup)
|
||||
{
|
||||
extruder_count = meshgroup->getExtruderCount();
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
config_per_extruder.emplace_back(PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
|
||||
}
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
const ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
|
||||
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMillimetersPerSecond("acceleration_prime_tower"), train->getSettingInMillimetersPerSecond("jerk_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::setConfigs(const MeshGroup* meshgroup, const int layer_thickness)
|
||||
{
|
||||
extruder_count = meshgroup->getExtruderCount();
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
GCodePathConfig& conf = config_per_extruder[extr];
|
||||
conf.setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
|
||||
{
|
||||
extruder_count = storage.meshgroup->getExtruderCount();
|
||||
@@ -94,13 +127,18 @@ void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
|
||||
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder) const
|
||||
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (gcodeLayer.getPrimeTowerIsPlanned())
|
||||
bool prime_tower_added = false;
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++)
|
||||
{
|
||||
prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr);
|
||||
}
|
||||
if (prime_tower_added)
|
||||
{ // don't print the prime tower if it has been printed already
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +159,7 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode
|
||||
// pre-wipe:
|
||||
if (pre_wipe)
|
||||
{
|
||||
preWipe(storage, gcodeLayer, layer_nr, new_extruder);
|
||||
preWipe(storage, gcodeLayer, new_extruder);
|
||||
}
|
||||
|
||||
addToGcode_denseInfill(gcodeLayer, layer_nr, new_extruder);
|
||||
@@ -131,21 +169,20 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
gcodeLayer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
|
||||
}
|
||||
|
||||
gcodeLayer.setPrimeTowerIsPlanned();
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const
|
||||
void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcodeLayer, const int layer_nr, const int extruder)
|
||||
{
|
||||
const ExtrusionMoves& pattern = patterns_per_extruder[extruder_nr][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
|
||||
ExtrusionMoves& pattern = patterns_per_extruder[extruder][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
|
||||
|
||||
const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr];
|
||||
GCodePathConfig& config = config_per_extruder[extruder];
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(pattern.polygons, &config);
|
||||
gcode_layer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
|
||||
gcodeLayer.addPolygonsByOptimizer(pattern.polygons, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
|
||||
last_prime_tower_poly_printed[extruder] = layer_nr;
|
||||
}
|
||||
|
||||
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const
|
||||
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
|
||||
{
|
||||
Point ret(0, 0);
|
||||
int absolute_starting_points = 0;
|
||||
@@ -225,10 +262,10 @@ void PrimeTower::generateWipeLocations(const SliceDataStorage& storage)
|
||||
PolygonUtils::spreadDots(segment_start, segment_end, number_of_pre_wipe_locations, pre_wipe_locations);
|
||||
}
|
||||
|
||||
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const
|
||||
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr)
|
||||
{
|
||||
int current_pre_wipe_location_idx = (pre_wipe_location_skip * layer_nr) % number_of_pre_wipe_locations;
|
||||
const ClosestPolygonPoint wipe_location = pre_wipe_locations[current_pre_wipe_location_idx];
|
||||
current_pre_wipe_location_idx = (current_pre_wipe_location_idx + pre_wipe_location_skip) % number_of_pre_wipe_locations;
|
||||
|
||||
ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
const int inward_dist = train.getSettingInMicrons("machine_nozzle_size") * 3 / 2 ;
|
||||
@@ -256,7 +293,7 @@ void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_la
|
||||
gcode_layer.addTravel(start);
|
||||
}
|
||||
float flow = 0.0001; // force this path being interpreted as an extrusion path, so that no Z hop will occur (TODO: really separately handle travel and extrusion moves)
|
||||
gcode_layer.addExtrusionMove(end, &gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr], SpaceFillType::None, flow);
|
||||
gcode_layer.addExtrusionMove(end, &config_per_extruder[extruder_nr], SpaceFillType::None, flow);
|
||||
}
|
||||
|
||||
void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
|
||||
|
||||
+27
-5
@@ -34,6 +34,7 @@ private:
|
||||
Polygons lines;
|
||||
};
|
||||
int extruder_count; //!< number of extruders
|
||||
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
|
||||
|
||||
bool is_hollow; //!< Whether the prime tower is hollow
|
||||
|
||||
@@ -46,6 +47,7 @@ private:
|
||||
const unsigned int pre_wipe_location_skip = 13; //!< How big the steps are when stepping through \ref PrimeTower::wipe_locations
|
||||
const unsigned int number_of_pre_wipe_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations
|
||||
// note that the above are two consecutive numbers in the Fibonacci sequence
|
||||
int current_pre_wipe_location_idx; //!< Index into \ref PrimeTower::wipe_locations of where to pre-wipe the nozzle
|
||||
|
||||
public:
|
||||
bool enabled; //!< Whether the prime tower is enabled.
|
||||
@@ -61,6 +63,21 @@ public:
|
||||
*/
|
||||
PrimeTower(const SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Initialize \ref PrimeTower::config_per_extruder with speed and line width settings.
|
||||
*
|
||||
* \param meshgroup Where to retrieve the setttings for each extruder
|
||||
*/
|
||||
void initConfigs(const MeshGroup* meshgroup);
|
||||
|
||||
/*!
|
||||
* Complete the \ref PrimeTower::config_per_extruder by settings the layer height.
|
||||
*
|
||||
* \param meshgroup Where to retrieve the setttings for each extruder
|
||||
* \param layer_thickness The current layer thickness
|
||||
*/
|
||||
void setConfigs(const MeshGroup* meshgroup, const int layer_thickness);
|
||||
|
||||
/*!
|
||||
* Generate the prime tower area to be used on each layer
|
||||
*
|
||||
@@ -87,7 +104,7 @@ public:
|
||||
* \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made
|
||||
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
|
||||
*/
|
||||
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder) const;
|
||||
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder);
|
||||
|
||||
/*!
|
||||
* \brief Subtract the prime tower from the support areas in storage.
|
||||
@@ -98,6 +115,12 @@ public:
|
||||
void subtractFromSupport(SliceDataStorage& storage);
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Layer number of the last layer in which a prime tower has been printed per extruder train.
|
||||
*
|
||||
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
|
||||
*/
|
||||
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
|
||||
|
||||
/*!
|
||||
* Find an approriate representation for the point representing the location before going to the prime tower
|
||||
@@ -107,7 +130,7 @@ private:
|
||||
* \param storage where to get settings from
|
||||
* \return that location
|
||||
*/
|
||||
Point getLocationBeforePrimeTower(const SliceDataStorage& storage) const;
|
||||
Point getLocationBeforePrimeTower(const SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* \param storage where to get settings from
|
||||
@@ -136,17 +159,16 @@ private:
|
||||
* \param extruder The extruder we just switched to, with which the prime
|
||||
* tower paths should be drawn.
|
||||
*/
|
||||
void addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder) const;
|
||||
void addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder);
|
||||
|
||||
/*!
|
||||
* Plan the moves for wiping the current nozzles oozed material before starting to print the prime tower.
|
||||
*
|
||||
* \param storage where to get settings from
|
||||
* \param[out] gcode_layer where to add the planned paths for wiping
|
||||
* \param layer_nr The layer number of the \p gcode_layer
|
||||
* \param extruder_nr The current extruder
|
||||
*/
|
||||
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const;
|
||||
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr);
|
||||
};
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ int SkirtBrim::generatePrimarySkirtBrimLines(SliceDataStorage& storage, int star
|
||||
//Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
|
||||
for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
|
||||
{
|
||||
double area = outer_skirt_brim_line[n].area();
|
||||
double area = PolygonRef{outer_skirt_brim_line[n]}.area();
|
||||
if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
|
||||
{
|
||||
outer_skirt_brim_line.remove(n--);
|
||||
|
||||
+8
-8
@@ -209,11 +209,11 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
|
||||
std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts();
|
||||
for (PolygonsPart& roof_part : roof_parts)
|
||||
{
|
||||
roof_outlines.add(roof_part[0]);
|
||||
roof_outlines.add(PolygonRef{roof_part[0]});
|
||||
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
|
||||
{
|
||||
roof_holes.add(roof_part[hole_idx]);
|
||||
roof_holes.back().reverse();
|
||||
roof_holes.add(PolygonRef{roof_part[hole_idx]});
|
||||
PolygonRef{roof_holes.back()}.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
|
||||
|
||||
std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts();
|
||||
for (PolygonsPart& supporting_part : supporting_parts)
|
||||
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
|
||||
supporting_outlines.add(PolygonRef{supporting_part[0]}); // only add outlines, not the holes
|
||||
|
||||
|
||||
|
||||
@@ -274,10 +274,10 @@ void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int dir
|
||||
Polygons floor_holes;
|
||||
for (PolygonsPart& floor_part : floor_parts)
|
||||
{
|
||||
floor_outlines.add(floor_part[0]);
|
||||
floor_outlines.add(PolygonRef{floor_part[0]});
|
||||
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
|
||||
{
|
||||
floor_holes.add(floor_part[hole_idx]);
|
||||
floor_holes.add(PolygonRef{floor_part[hole_idx]});
|
||||
//floor_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
|
||||
{
|
||||
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
|
||||
{
|
||||
ConstPolygonRef upperPart = parts1[prt];
|
||||
const PolygonRef upperPart = parts1[prt];
|
||||
|
||||
ClosestPolygonPoint closestInPoly = PolygonUtils::findClosest(start_close_to, upperPart);
|
||||
|
||||
@@ -439,7 +439,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
|
||||
for (unsigned int prt = 0 ; prt < supported.size(); prt++)
|
||||
{
|
||||
|
||||
ConstPolygonRef upperPart(supported[prt]);
|
||||
const PolygonRef upperPart = supported[prt];
|
||||
|
||||
|
||||
parts.emplace_back(prt);
|
||||
|
||||
+4
-4
@@ -29,9 +29,9 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
|
||||
for(unsigned int n=0; n<islands.size(); n++)
|
||||
{
|
||||
//Skip internal holes
|
||||
if (!islands[n].orientation())
|
||||
if (!PolygonRef{islands[n]}.orientation())
|
||||
continue;
|
||||
double area = fabs(islands[n].area());
|
||||
double area = fabs(PolygonRef{islands[n]}.area());
|
||||
if (area > area1)
|
||||
{
|
||||
if (area1 > area2)
|
||||
@@ -51,8 +51,8 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
|
||||
if (idx1 < 0 || idx2 < 0)
|
||||
return -1;
|
||||
|
||||
Point center1 = islands[idx1].centerOfMass();
|
||||
Point center2 = islands[idx2].centerOfMass();
|
||||
Point center1 = PolygonRef{islands[idx1]}.centerOfMass();
|
||||
Point center2 = PolygonRef{islands[idx2]}.centerOfMass();
|
||||
|
||||
return angle(center2 - center1);
|
||||
}
|
||||
|
||||
@@ -642,7 +642,6 @@ void CommandSocket::sendLayerData()
|
||||
{
|
||||
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : data.slice_data) //Note: This is in no particular order!
|
||||
{
|
||||
logDebug("Sending layer data for layer %i of %i.\n", entry.first, data.slice_data.size());
|
||||
private_data->socket->sendMessage(entry.second); //Send the actual layers.
|
||||
}
|
||||
data.sliced_objects = 0;
|
||||
@@ -668,7 +667,6 @@ void CommandSocket::sendOptimizedLayerData()
|
||||
{
|
||||
for (std::pair<const int, std::shared_ptr<cura::proto::LayerOptimized>> entry : data.slice_data) //Note: This is in no particular order!
|
||||
{
|
||||
logDebug("Sending layer data for layer %i of %i.\n", entry.first, data.slice_data.size());
|
||||
private_data->socket->sendMessage(entry.second); //Send the actual layers.
|
||||
}
|
||||
data.sliced_objects = 0;
|
||||
|
||||
@@ -270,7 +270,7 @@ void GCodeExport::setFlavor(EGCodeFlavor flavor)
|
||||
}
|
||||
}
|
||||
|
||||
EGCodeFlavor GCodeExport::getFlavor() const
|
||||
EGCodeFlavor GCodeExport::getFlavor()
|
||||
{
|
||||
return this->flavor;
|
||||
}
|
||||
@@ -922,7 +922,7 @@ void GCodeExport::finalize(const char* endCode)
|
||||
{
|
||||
writeFanCommand(0);
|
||||
writeCode(endCode);
|
||||
int64_t print_time = getTotalPrintTime();
|
||||
long print_time = getTotalPrintTime();
|
||||
int mat_0 = getTotalFilamentUsed(0);
|
||||
log("Print time: %d\n", print_time);
|
||||
log("Print time (readable): %dh %dm %ds\n", print_time / 60 / 60, (print_time / 60) % 60, print_time % 60);
|
||||
|
||||
+12
-1
@@ -13,6 +13,7 @@
|
||||
#include "MeshGroup.h"
|
||||
#include "commandSocket.h"
|
||||
#include "RetractionConfig.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -90,6 +91,9 @@ private:
|
||||
std::string machine_name;
|
||||
|
||||
std::ostream* output_stream;
|
||||
#ifdef _OPENMP
|
||||
omp_nest_lock_type output_stream_lock;
|
||||
#endif
|
||||
std::string new_line;
|
||||
|
||||
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
|
||||
@@ -177,6 +181,13 @@ public:
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_nest_lock_type& getOutputStreamLock()
|
||||
{
|
||||
return output_stream_lock;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool getExtruderIsUsed(const int extruder_nr) const; //!< Returns whether the extruder with the given index is used up until the current meshgroup
|
||||
|
||||
int getNozzleSize(const int extruder_nr) const;
|
||||
@@ -188,7 +199,7 @@ public:
|
||||
Point getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const;
|
||||
|
||||
void setFlavor(EGCodeFlavor flavor);
|
||||
EGCodeFlavor getFlavor() const;
|
||||
EGCodeFlavor getFlavor();
|
||||
|
||||
void setZ(int z);
|
||||
|
||||
|
||||
+156
-36
@@ -10,8 +10,9 @@
|
||||
namespace cura {
|
||||
|
||||
|
||||
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
|
||||
: extruder(extruder)
|
||||
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
|
||||
: is_paths_vector_initialised(false)
|
||||
, extruder(extruder)
|
||||
, heated_pre_travel_time(0)
|
||||
, initial_printing_temperature(-1)
|
||||
, printing_temperature(-1)
|
||||
@@ -54,11 +55,24 @@ double ExtruderPlan::getFanSpeed()
|
||||
{
|
||||
return fan_speed;
|
||||
}
|
||||
void ExtruderPlan::convertListToVector()
|
||||
{
|
||||
unsigned int number_of_paths = paths_list.size();
|
||||
paths_vector.reserve(number_of_paths);
|
||||
for (auto path : paths_list)
|
||||
{
|
||||
if (path.points.size())
|
||||
{
|
||||
paths_vector.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
is_paths_vector_initialised = true;
|
||||
}
|
||||
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
std::list<GCodePath>& paths = extruder_plans.back().getPathsList();
|
||||
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow) // spiralize can only change when a travel path is in between
|
||||
return &paths.back();
|
||||
paths.emplace_back();
|
||||
@@ -76,31 +90,29 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(const GCodePathConfig* config,
|
||||
|
||||
void GCodePlanner::forceNewPathStart()
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
std::list<GCodePath>& paths = extruder_plans.back().getPathsList();
|
||||
if (paths.size() > 0)
|
||||
paths[paths.size()-1].done = true;
|
||||
paths.back().done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(const SliceDataStorage& storage, int layer_nr, int z, int layer_thickness, PlanningState last_planned_state, const 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, 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)
|
||||
: storage(storage)
|
||||
, configs_storage(storage, layer_nr, layer_thickness)
|
||||
, layer_nr(layer_nr)
|
||||
, is_initial_layer(layer_nr == 0 - Raft::getTotalExtraLayers(storage))
|
||||
, z(z)
|
||||
, layer_thickness(layer_thickness)
|
||||
, start_position(last_planned_state.last_position)
|
||||
, lastPosition(last_planned_state.last_position)
|
||||
, has_prime_tower_planned(false)
|
||||
, last_extruder_previous_layer(last_planned_state.current_extruder)
|
||||
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(last_planned_state.current_extruder))
|
||||
, start_position(last_position)
|
||||
, lastPosition(last_position)
|
||||
, 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)
|
||||
, gcode_written(0)
|
||||
{
|
||||
int current_extruder = last_planned_state.current_extruder;
|
||||
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
|
||||
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
|
||||
comb = nullptr;
|
||||
was_inside = last_planned_state.is_inside_mesh_layer_part;
|
||||
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)
|
||||
if (combing_mode != CombingMode::OFF)
|
||||
{
|
||||
@@ -142,12 +154,12 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
|
||||
else
|
||||
{
|
||||
Polygons comb_boundary;
|
||||
for (const SliceMeshStorage& mesh : storage.meshes)
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
const SliceLayer& layer = mesh.layers[layer_nr];
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
if (mesh.getSettingAsCombingMode("retraction_combing") == CombingMode::NO_SKIN)
|
||||
{
|
||||
for (const SliceLayerPart& part : layer.parts)
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
comb_boundary.add(part.infill_area);
|
||||
}
|
||||
@@ -192,7 +204,7 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
}
|
||||
addTravel(end_pos); // + extruder_offset cause it
|
||||
}
|
||||
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
|
||||
if (extruder_plans.back().getPathsList().empty() && extruder_plans.back().inserts.empty())
|
||||
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
|
||||
extruder_plans.back().extruder = extruder;
|
||||
}
|
||||
@@ -244,16 +256,16 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
GCodePath& GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
GCodePath* path = nullptr;
|
||||
const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()];
|
||||
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[getExtruder()];
|
||||
GCodePathConfig& travel_config = storage.travel_config_per_extruder[getExtruder()];
|
||||
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[getExtruder()];
|
||||
|
||||
bool combed = false;
|
||||
|
||||
const SettingsBaseVirtual* extr = getLastPlannedExtruderTrainSettings();
|
||||
SettingsBaseVirtual* extr = getLastPlannedExtruderTrainSettings();
|
||||
|
||||
const bool perform_z_hops = extr->getSettingBoolean("retraction_hop_enabled");
|
||||
|
||||
const bool is_first_travel_of_extruder_after_switch = extruder_plans.back().paths.size() == 0 && (extruder_plans.size() > 1 || last_extruder_previous_layer != getExtruder());
|
||||
const bool is_first_travel_of_extruder_after_switch = extruder_plans.back().getPathsList().size() == 0 && (extruder_plans.size() > 1 || last_extruder_previous_layer != getExtruder());
|
||||
const bool bypass_combing = is_first_travel_of_extruder_after_switch && extr->getSettingBoolean("retraction_hop_after_extruder_switch");
|
||||
|
||||
if (comb != nullptr && !bypass_combing && lastPosition != no_point)
|
||||
@@ -336,7 +348,7 @@ GCodePath& GCodePlanner::addTravel_simple(Point p, GCodePath* path)
|
||||
{
|
||||
if (path == nullptr)
|
||||
{
|
||||
path = getLatestPathWithConfig(&configs_storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
|
||||
path = getLatestPathWithConfig(&storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
@@ -461,7 +473,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
|
||||
if (minExtrudeTime < 1)
|
||||
minExtrudeTime = 1;
|
||||
double factor = extrudeTime / minExtrudeTime;
|
||||
for (GCodePath& path : paths)
|
||||
for (GCodePath& path : getPathsList())
|
||||
{
|
||||
if (path.isTravelPath())
|
||||
continue;
|
||||
@@ -485,7 +497,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
|
||||
|
||||
// Adjust stored naive time estimates
|
||||
estimates.extrude_time *= inv_factor;
|
||||
for (GCodePath& path : paths)
|
||||
for (GCodePath& path : getPathsList())
|
||||
{
|
||||
path.estimates.extrude_time *= inv_factor;
|
||||
}
|
||||
@@ -503,7 +515,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
|
||||
Point p0 = start_position;
|
||||
|
||||
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
|
||||
for (GCodePath& path : paths)
|
||||
for (GCodePath& path : getPathsList())
|
||||
{
|
||||
bool is_extrusion_path = false;
|
||||
double* path_time_estimate;
|
||||
@@ -556,7 +568,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
|
||||
|
||||
void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time)
|
||||
{
|
||||
const FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
|
||||
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
|
||||
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
|
||||
totalPrintTime = estimates.getTotalTime();
|
||||
if (force_minimal_layer_time)
|
||||
@@ -639,6 +651,8 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
|
||||
|
||||
void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
{
|
||||
completeConfigs();
|
||||
|
||||
CommandSocket::setLayerForSend(layer_nr);
|
||||
CommandSocket::setSendCurrentPosition( gcode.getPositionXY() );
|
||||
gcode.setLayerNr(layer_nr);
|
||||
@@ -663,7 +677,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
for(unsigned int extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
|
||||
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder_plan.extruder];
|
||||
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder_plan.extruder];
|
||||
|
||||
if (extruder != extruder_plan.extruder)
|
||||
{
|
||||
@@ -703,7 +717,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
gcode.writeRetraction(retraction_config);
|
||||
}
|
||||
gcode.writeFanCommand(extruder_plan.getFanSpeed());
|
||||
std::vector<GCodePath>& paths = extruder_plan.paths;
|
||||
std::vector<GCodePath>& paths = extruder_plan.getPaths();
|
||||
|
||||
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
|
||||
return a.path_idx < b.path_idx;
|
||||
@@ -765,7 +779,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
else
|
||||
speed *= extruder_plan.getExtrudeSpeedFactor();
|
||||
|
||||
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, configs_storage.travel_config_per_extruder[extruder], nozzle_size, speed_equalize_flow_enabled, speed_equalize_flow_max).mergeInfillLines(path_idx)) // !! has effect on path_idx !!
|
||||
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config_per_extruder[extruder], nozzle_size, speed_equalize_flow_enabled, speed_equalize_flow_max).mergeInfillLines(path_idx)) // !! has effect on path_idx !!
|
||||
{ // !! has effect on path_idx !!
|
||||
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
|
||||
continue;
|
||||
@@ -788,7 +802,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
bool spiralize = path.spiralize;
|
||||
if (!spiralize) // normal (extrusion) move (with coasting
|
||||
{
|
||||
const CoastingConfig& coasting_config = storage.coasting_config[extruder];
|
||||
CoastingConfig& coasting_config = storage.coasting_config[extruder];
|
||||
bool coasting = coasting_config.coasting_enable;
|
||||
if (coasting)
|
||||
{
|
||||
@@ -859,15 +873,15 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
if (train->getSettingBoolean("cool_lift_head") && extruder_plan.extraTime > 0.0)
|
||||
{
|
||||
gcode.writeComment("Small layer, adding delay");
|
||||
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
|
||||
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(), configs_storage.travel_config_per_extruder[extruder].getSpeed(), 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), configs_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);
|
||||
}
|
||||
gcode.writeDelay(extruder_plan.extraTime);
|
||||
}
|
||||
@@ -876,6 +890,9 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
} // extruder plans /\ .
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
#pragma omp flush
|
||||
#pragma omp atomic update
|
||||
++gcode_written;
|
||||
}
|
||||
|
||||
void GCodePlanner::overrideFanSpeeds(double speed)
|
||||
@@ -887,9 +904,112 @@ void GCodePlanner::overrideFanSpeeds(double speed)
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::completeConfigs()
|
||||
{
|
||||
storage.support_config.setLayerHeight(layer_thickness);
|
||||
storage.support_skin_config.setLayerHeight(layer_thickness);
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.setLayerHeight(layer_thickness);
|
||||
|
||||
mesh.insetX_config.setLayerHeight(layer_thickness);
|
||||
mesh.skin_config.setLayerHeight(layer_thickness);
|
||||
mesh.perimeter_gap_config.setLayerHeight(layer_thickness);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
storage.primeTower.setConfigs(storage.meshgroup, layer_thickness);
|
||||
|
||||
processInitialLayersSpeedup();
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::processInitialLayersSpeedup()
|
||||
{
|
||||
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
if (layer_nr >= 0 && layer_nr < initial_speedup_layers)
|
||||
{
|
||||
GCodePathConfig::BasicConfig initial_layer_speed_config;
|
||||
int extruder_nr_support_infill = storage.getSettingAsIndex((layer_nr == 0)? "support_extruder_nr_layer_0" : "support_infill_extruder_nr");
|
||||
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("speed_print_layer_0");
|
||||
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("acceleration_print_layer_0");
|
||||
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("jerk_print_layer_0");
|
||||
|
||||
//Support (global).
|
||||
storage.support_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
//Support roof (global).
|
||||
int extruder_nr_support_skin = storage.getSettingAsIndex("support_interface_extruder_nr");
|
||||
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("speed_print_layer_0");
|
||||
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("acceleration_print_layer_0");
|
||||
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("jerk_print_layer_0");
|
||||
storage.support_skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
|
||||
{
|
||||
const ExtruderTrain* extruder_train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
initial_layer_speed_config.speed = extruder_train->getSettingInMillimetersPerSecond("speed_travel_layer_0");
|
||||
initial_layer_speed_config.acceleration = extruder_train->getSettingInMillimetersPerSecond("acceleration_travel_layer_0");
|
||||
initial_layer_speed_config.jerk = extruder_train->getSettingInMillimetersPerSecond("jerk_travel_layer_0");
|
||||
|
||||
//Travel speed (per extruder).
|
||||
storage.travel_config_per_extruder[extruder_nr].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
initial_layer_speed_config.speed = mesh.getSettingInMillimetersPerSecond("speed_print_layer_0");
|
||||
initial_layer_speed_config.acceleration = mesh.getSettingInMillimetersPerSecond("acceleration_print_layer_0");
|
||||
initial_layer_speed_config.jerk = mesh.getSettingInMillimetersPerSecond("jerk_print_layer_0");
|
||||
|
||||
//Outer wall speed (per mesh).
|
||||
mesh.inset0_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
//Inner wall speed (per mesh).
|
||||
mesh.insetX_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
//Skin speed (per mesh).
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
mesh.perimeter_gap_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
//Infill speed (per combine part per mesh).
|
||||
mesh.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (layer_nr == initial_speedup_layers) //At the topmost layer of the gradient, reset all speeds to the typical speeds.
|
||||
{
|
||||
storage.support_config.setSpeedIconic();
|
||||
storage.support_skin_config.setSpeedIconic();
|
||||
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
|
||||
{
|
||||
storage.travel_config_per_extruder[extruder_nr].setSpeedIconic();
|
||||
}
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.setSpeedIconic();
|
||||
mesh.insetX_config.setSpeedIconic();
|
||||
mesh.skin_config.setSpeedIconic();
|
||||
mesh.perimeter_gap_config.setSpeedIconic();
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setSpeedIconic();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].getPaths();
|
||||
for (unsigned int path_idx2 = path_idx + 1; path_idx2 < paths.size(); path_idx2++)
|
||||
{
|
||||
if (paths[path_idx2].getExtrusionMM3perMM() > 0)
|
||||
@@ -920,7 +1040,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_plan.getPaths();
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (path_idx + 1 >= paths.size()
|
||||
||
|
||||
|
||||
+75
-54
@@ -16,7 +16,6 @@
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "SpaceFillType.h"
|
||||
#include "GCodePathConfig.h"
|
||||
#include "settings/PathConfigStorage.h"
|
||||
|
||||
#include "utils/optional.h"
|
||||
|
||||
@@ -38,9 +37,12 @@ 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:
|
||||
std::vector<GCodePath> paths; //!< The paths planned for this extruder
|
||||
std::list<GCodePath> paths_list; //!< The paths planned for this extruder
|
||||
std::vector<GCodePath> paths_vector; //!< The paths planned for this extruder
|
||||
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
|
||||
|
||||
bool is_paths_vector_initialised; //!< Keeps information if content of \p paths_list has been copied to \p paths_vector
|
||||
|
||||
int extruder; //!< The extruder used for this paths in the current plan.
|
||||
double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature
|
||||
double initial_printing_temperature; //!< The required temperature at the start of this extruder plan.
|
||||
@@ -58,7 +60,7 @@ public:
|
||||
* \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, bool is_initial_layer, int layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
|
||||
ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
|
||||
|
||||
/*!
|
||||
* Add a new Insert, constructed with the given arguments
|
||||
@@ -102,7 +104,7 @@ public:
|
||||
while ( ! inserts.empty() )
|
||||
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
|
||||
NozzleTempInsert& insert = inserts.front();
|
||||
assert(insert.path_idx == paths.size());
|
||||
assert(insert.path_idx == getPaths().size());
|
||||
insert.write(gcode);
|
||||
inserts.pop_front();
|
||||
}
|
||||
@@ -157,6 +159,39 @@ public:
|
||||
* \return The fan speed computed in processFanSpeedAndMinimalLayerTime
|
||||
*/
|
||||
double getFanSpeed();
|
||||
|
||||
/*!
|
||||
* Move the paths data from the input list to the vector container
|
||||
*
|
||||
* \warning empties the \p paths_list which will no longer contain data. No references to the paths in \p paths_list should be kept.
|
||||
*/
|
||||
void convertListToVector();
|
||||
|
||||
/*!
|
||||
* Get the paths in a list container
|
||||
*
|
||||
* \warning should not be called after paths_list has been converted to paths variable
|
||||
*
|
||||
* \return The paths as a list
|
||||
*/
|
||||
std::list<GCodePath>& getPathsList()
|
||||
{
|
||||
assert(!is_paths_vector_initialised);
|
||||
return paths_list;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the paths in a vector container
|
||||
*
|
||||
* \warning should not be called before paths_list has been converted to paths variable
|
||||
*
|
||||
* \return The paths as a vector
|
||||
*/
|
||||
std::vector<GCodePath>& getPaths()
|
||||
{
|
||||
assert(is_paths_vector_initialised);
|
||||
return paths_vector;
|
||||
}
|
||||
protected:
|
||||
|
||||
Point start_position; //!< The position the print head was at at the start of this extruder plan
|
||||
@@ -166,7 +201,7 @@ protected:
|
||||
|
||||
int layer_thickness; //!< The thickness of this layer in Z-direction
|
||||
|
||||
const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
|
||||
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
|
||||
|
||||
@@ -216,25 +251,9 @@ class GCodePlanner : public NoCopy
|
||||
{
|
||||
friend class LayerPlanBuffer;
|
||||
friend class GCodePlannerTest;
|
||||
public:
|
||||
/*!
|
||||
* The state which is passed along between layer plans.
|
||||
* This is what a \ref GCodePlanner delivers to further computation in \ref FffGcodeWriter
|
||||
* This is the state which is currently planned, not which is written to gcode.
|
||||
*/
|
||||
struct PlanningState
|
||||
{
|
||||
Point last_position; //!< The position of the head before planning the next layer
|
||||
int current_extruder; //!< The extruder train in use before planning the next layer
|
||||
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
|
||||
};
|
||||
private:
|
||||
const SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
|
||||
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
|
||||
|
||||
public:
|
||||
const PathConfigStorage configs_storage; //!< The line configs for this layer for each feature type
|
||||
|
||||
private:
|
||||
int layer_nr; //!< The layer number of this layer plan
|
||||
int is_initial_layer; //!< Whether this is the first layer (which might be raft)
|
||||
|
||||
@@ -244,9 +263,7 @@ private:
|
||||
|
||||
Point start_position;
|
||||
Point lastPosition;
|
||||
|
||||
bool has_prime_tower_planned;
|
||||
|
||||
|
||||
std::vector<ExtruderPlan> extruder_plans; //!< should always contain at least one ExtruderPlan
|
||||
|
||||
int last_extruder_previous_layer; //!< The last id of the extruder with which was printed in the previous layer
|
||||
@@ -257,8 +274,10 @@ private:
|
||||
Comb* comb;
|
||||
|
||||
|
||||
const std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
|
||||
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
|
||||
|
||||
int gcode_written;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Either create a new path with the given config or return the last path if it already had that config.
|
||||
@@ -293,7 +312,7 @@ public:
|
||||
* \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(const SliceDataStorage& storage, int layer_nr, int z, int layer_height, PlanningState last_planned_state, const 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, 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();
|
||||
|
||||
void overrideFanSpeeds(double speed);
|
||||
@@ -311,21 +330,12 @@ private:
|
||||
Polygons computeCombBoundaryInside(CombingMode combing_mode);
|
||||
|
||||
public:
|
||||
int getLayerNr() const
|
||||
int getLayerNr()
|
||||
{
|
||||
return layer_nr;
|
||||
}
|
||||
|
||||
PlanningState getPlanningState() const
|
||||
{
|
||||
PlanningState ret;
|
||||
ret.last_position = lastPosition;
|
||||
ret.current_extruder = getExtruder();
|
||||
ret.is_inside_mesh_layer_part = was_inside;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point getLastPosition() const
|
||||
|
||||
Point getLastPosition()
|
||||
{
|
||||
return lastPosition;
|
||||
}
|
||||
@@ -333,25 +343,14 @@ public:
|
||||
/*!
|
||||
* return whether the last position planned was inside the mesh (used in combing)
|
||||
*/
|
||||
bool getIsInsideMesh() const
|
||||
bool getIsInsideMesh()
|
||||
{
|
||||
return was_inside;
|
||||
}
|
||||
|
||||
bool getPrimeTowerIsPlanned() const
|
||||
{
|
||||
return has_prime_tower_planned;
|
||||
}
|
||||
|
||||
void setPrimeTowerIsPlanned()
|
||||
{
|
||||
has_prime_tower_planned = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* send a line segment through the command socket from the previous point to the given point \p to
|
||||
*/
|
||||
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width) const
|
||||
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width)
|
||||
{
|
||||
CommandSocket::sendLineTo(print_feature_type, to, line_width);
|
||||
}
|
||||
@@ -375,7 +374,7 @@ public:
|
||||
/*!
|
||||
* Get the last planned extruder.
|
||||
*/
|
||||
int getExtruder() const
|
||||
int getExtruder()
|
||||
{
|
||||
return extruder_plans.back().extruder;
|
||||
}
|
||||
@@ -473,7 +472,29 @@ public:
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
*/
|
||||
void writeGCode(GCodeExport& gcode);
|
||||
|
||||
/*!
|
||||
* Has the planned paths been written to gcode
|
||||
*/
|
||||
int isGCodeWritten()
|
||||
{
|
||||
int gcode_written_tmp;
|
||||
#pragma omp atomic read
|
||||
gcode_written_tmp = gcode_written;
|
||||
return gcode_written_tmp;
|
||||
}
|
||||
/*!
|
||||
* Complete all GcodePathConfigs by
|
||||
* - altering speeds to conform to speed_print_layer_0 and
|
||||
* speed_travel_layer_0
|
||||
* - setting the layer_height (and thereby computing the extrusionMM3perMM)
|
||||
*/
|
||||
void completeConfigs();
|
||||
|
||||
/*!
|
||||
* Interpolate between the initial layer speeds and the eventual speeds.
|
||||
*/
|
||||
void processInitialLayersSpeedup();
|
||||
|
||||
/*!
|
||||
* Whether the current retracted path is to be an extruder switch retraction.
|
||||
* This function is used to avoid a G10 S1 after a G10.
|
||||
|
||||
+7
-4
@@ -3,6 +3,7 @@
|
||||
#include "layerPart.h"
|
||||
#include "settings/settings.h"
|
||||
#include "progress/Progress.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
#include "utils/SVG.h" // debug output
|
||||
|
||||
@@ -28,8 +29,8 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
|
||||
{
|
||||
for(unsigned int i=0; i<layer->polygons.size(); i++)
|
||||
{
|
||||
if (layer->polygons[i].orientation())
|
||||
layer->polygons[i].reverse();
|
||||
if (PolygonRef{layer->polygons[i]}.orientation())
|
||||
PolygonRef{layer->polygons[i]}.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,14 +46,16 @@ 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();
|
||||
// mesh.layers.resize(total_layers); TODO: put this back?
|
||||
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++)
|
||||
{
|
||||
{ MULTITHREAD_FOR_CATCH_EXCEPTION(
|
||||
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
|
||||
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
|
||||
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
|
||||
}
|
||||
)}
|
||||
handleMultithreadAbort();
|
||||
}
|
||||
|
||||
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers, int layer_nr)
|
||||
|
||||
+1
-15
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "settings/SettingsToGV.h"
|
||||
|
||||
#include <omp.h> // omp_get_num_threads
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -330,19 +328,7 @@ int main(int argc, char **argv)
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
#pragma omp master
|
||||
{
|
||||
#ifdef _OPENMP
|
||||
log("OpenMP multithreading enabled, likely number of threads to be used: %u\n", omp_get_num_threads());
|
||||
#else
|
||||
log("OpenMP multithreading disabled\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (stringcasecompare(argv[1], "connect") == 0)
|
||||
{
|
||||
connect(argc, argv);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "multithreadOpenMP.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
bool abort_execution = false;
|
||||
|
||||
#ifdef _OPENMP
|
||||
void handleMultithreadAbort()
|
||||
{
|
||||
if (checkMultithreadAbort())
|
||||
{
|
||||
std::exit(17);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,182 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#ifndef MULTITHREAD_OPENMP_H
|
||||
#define MULTITHREAD_OPENMP_H
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
// TODO: remove
|
||||
extern bool abort_execution;
|
||||
|
||||
#ifdef _OPENMP
|
||||
|
||||
class omp_lock_type
|
||||
{
|
||||
public:
|
||||
omp_lock_type()
|
||||
{
|
||||
omp_init_lock(&lock_object);
|
||||
}
|
||||
~omp_lock_type()
|
||||
{
|
||||
omp_destroy_lock(&lock_object);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
omp_set_lock(&lock_object);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
omp_unset_lock(&lock_object);
|
||||
}
|
||||
int test_lock()
|
||||
{
|
||||
return omp_test_lock(&lock_object);
|
||||
}
|
||||
private:
|
||||
omp_lock_t lock_object;
|
||||
omp_lock_type( const omp_lock_type& ) = delete;
|
||||
omp_lock_type& operator=( const omp_lock_type& ) = delete;
|
||||
};
|
||||
|
||||
class omp_nest_lock_type
|
||||
{
|
||||
public:
|
||||
omp_nest_lock_type()
|
||||
{
|
||||
omp_init_nest_lock(&lock_object);
|
||||
}
|
||||
~omp_nest_lock_type()
|
||||
{
|
||||
omp_destroy_nest_lock(&lock_object);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
omp_set_nest_lock(&lock_object);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
omp_unset_nest_lock(&lock_object);
|
||||
}
|
||||
int test_lock()
|
||||
{
|
||||
return omp_test_nest_lock(&lock_object);
|
||||
}
|
||||
private:
|
||||
omp_nest_lock_t lock_object;
|
||||
omp_nest_lock_type( const omp_nest_lock_type& ) = delete;
|
||||
omp_nest_lock_type& operator=( const omp_nest_lock_type& ) = delete;
|
||||
};
|
||||
|
||||
template <typename LockType>
|
||||
class omp_try_lock_guard_t
|
||||
{
|
||||
public:
|
||||
omp_try_lock_guard_t( LockType& lock_)
|
||||
: lock(lock_)
|
||||
{
|
||||
has_lock = lock.test_lock();
|
||||
}
|
||||
~omp_try_lock_guard_t()
|
||||
{
|
||||
if (has_lock)
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
int isLocked()
|
||||
{
|
||||
return has_lock;
|
||||
}
|
||||
private:
|
||||
LockType& lock;
|
||||
int has_lock;
|
||||
omp_try_lock_guard_t( const omp_try_lock_guard_t& ) = delete;
|
||||
omp_try_lock_guard_t<LockType>& operator=( const omp_try_lock_guard_t& ) = delete;
|
||||
};
|
||||
|
||||
template <typename LockType>
|
||||
class omp_lock_guard_t
|
||||
{
|
||||
public:
|
||||
omp_lock_guard_t( LockType& lock_)
|
||||
: lock(lock_)
|
||||
{
|
||||
lock.lock();
|
||||
}
|
||||
~omp_lock_guard_t()
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
private:
|
||||
LockType& lock;
|
||||
omp_lock_guard_t( const omp_lock_guard_t& ) = delete;
|
||||
omp_lock_guard_t& operator=( const omp_lock_guard_t& ) = delete;
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO: remove
|
||||
inline bool checkMultithreadAbort()
|
||||
{
|
||||
bool tmp_abort_execution;
|
||||
#pragma omp atomic read
|
||||
tmp_abort_execution = abort_execution;
|
||||
return tmp_abort_execution;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
inline void setMultithreadAbort()
|
||||
{
|
||||
#pragma omp atomic write
|
||||
abort_execution = true;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
#ifdef _OPENMP
|
||||
void handleMultithreadAbort();
|
||||
#else
|
||||
inline void handleMultithreadAbort(){}
|
||||
#endif
|
||||
|
||||
// TODO: remove old code below
|
||||
#ifdef _OPENMP
|
||||
#define MULTITHREAD_FOR_CATCH_EXCEPTION(code) \
|
||||
if (checkMultithreadAbort()) \
|
||||
{ \
|
||||
continue; \
|
||||
} \
|
||||
try \
|
||||
{ \
|
||||
code \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
setMultithreadAbort(); \
|
||||
}
|
||||
#else
|
||||
#define MULTITHREAD_FOR_CATCH_EXCEPTION(code) code
|
||||
#endif
|
||||
|
||||
#ifdef _OPENMP
|
||||
#define MULTITHREAD_TASK_CATCH_EXCEPTION(code) \
|
||||
if (!checkMultithreadAbort()) \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
code \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
setMultithreadAbort(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define MULTITHREAD_TASK_CATCH_EXCEPTION(code) code
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif // MULTITHREAD_OPENMP_H
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../utils/polygonUtils.h"
|
||||
#include "../utils/linearAlg2D.h"
|
||||
#include "../utils/PolygonsPointIndex.h"
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "../utils/SVG.h"
|
||||
@@ -23,7 +22,7 @@ Polygons& Comb::getBoundaryOutside()
|
||||
return *boundary_outside;
|
||||
}
|
||||
|
||||
Comb::Comb(const SliceDataStorage& storage, int layer_nr, const Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, layer_nr(layer_nr)
|
||||
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
|
||||
@@ -32,7 +31,7 @@ Comb::Comb(const SliceDataStorage& storage, int layer_nr, const Polygons& comb_b
|
||||
, offset_from_inside_to_outside(offset_from_outlines + offset_from_outlines_outside)
|
||||
, max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation
|
||||
, avoid_other_parts(travel_avoid_other_parts)
|
||||
, boundary_inside( comb_boundary_inside ) // copy the boundary, because the partsView_inside will reorder the polygons
|
||||
, boundary_inside( comb_boundary_inside )
|
||||
, partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !!
|
||||
, inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset))
|
||||
, boundary_outside(
|
||||
@@ -214,7 +213,7 @@ Comb::Crossing::Crossing(const Point& dest_point, const bool dest_is_inside, con
|
||||
{
|
||||
if (dest_is_inside)
|
||||
{
|
||||
dest_crossing_poly.emplace(boundary_inside[dest_part_boundary_crossing_poly_idx]); // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
|
||||
dest_crossing_poly = boundary_inside[dest_part_boundary_crossing_poly_idx]; // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
const SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
|
||||
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
|
||||
const int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
|
||||
const int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
|
||||
@@ -115,8 +115,8 @@ private:
|
||||
|
||||
const bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
|
||||
|
||||
Polygons boundary_inside; //!< The boundary within which to comb. (Will be reordered by the partsView_inside)
|
||||
const PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
|
||||
Polygons& boundary_inside; //!< The boundary within which to comb.
|
||||
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
|
||||
LocToLineGrid* inside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the inner boundary.
|
||||
LazyInitialization<Polygons> boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
|
||||
LazyInitialization<LocToLineGrid, Comb*, const int64_t> outside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the outside boundary.
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
*/
|
||||
Comb(const SliceDataStorage& storage, int layer_nr, const Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
|
||||
~Comb();
|
||||
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "PathConfigStorage.h"
|
||||
|
||||
#include "settings.h" // MAX_INFILL_COMBINE
|
||||
#include "../sliceDataStorage.h" // SliceDataStorage
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
GCodePathConfig getPerimeterGapConfig(const SliceMeshStorage& mesh, int layer_thickness)
|
||||
{
|
||||
// The perimeter gap config follows the skin config, but has a different line width:
|
||||
// wall_line_width_x divided by two because the gaps are between 0 and 1 times the wall line width
|
||||
const int perimeter_gaps_line_width = mesh.getSettingInMicrons("wall_line_width_x") / 2;
|
||||
double perimeter_gaps_speed = mesh.getSettingInMillimetersPerSecond("speed_topbottom");
|
||||
if (mesh.getSettingBoolean("speed_equalize_flow_enabled"))
|
||||
{
|
||||
perimeter_gaps_speed = perimeter_gaps_speed * mesh.getSettingInMicrons("skin_line_width") / perimeter_gaps_line_width;
|
||||
}
|
||||
return GCodePathConfig(
|
||||
PrintFeatureType::Skin
|
||||
, perimeter_gaps_line_width
|
||||
, layer_thickness
|
||||
, mesh.getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{perimeter_gaps_speed, mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom")}
|
||||
);
|
||||
}
|
||||
|
||||
PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, int layer_thickness)
|
||||
: inset0_config(
|
||||
PrintFeatureType::OuterWall
|
||||
, mesh.getSettingInMicrons("wall_line_width_0")
|
||||
, layer_thickness
|
||||
, mesh.getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_0"), mesh.getSettingInMillimetersPerSecond("jerk_wall_0")}
|
||||
)
|
||||
, insetX_config(
|
||||
PrintFeatureType::InnerWall
|
||||
, mesh.getSettingInMicrons("wall_line_width_x")
|
||||
, layer_thickness
|
||||
, mesh.getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_x"), mesh.getSettingInMillimetersPerSecond("jerk_wall_x")}
|
||||
)
|
||||
, skin_config(
|
||||
PrintFeatureType::Skin
|
||||
, mesh.getSettingInMicrons("skin_line_width")
|
||||
, layer_thickness
|
||||
, mesh.getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom")}
|
||||
)
|
||||
|
||||
, perimeter_gap_config(getPerimeterGapConfig(mesh, layer_thickness))
|
||||
{
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++)
|
||||
{
|
||||
infill_config.emplace_back(
|
||||
PrintFeatureType::Infill
|
||||
, mesh.getSettingInMicrons("infill_line_width") * (combine_idx + 1)
|
||||
, layer_thickness
|
||||
, mesh.getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMillimetersPerSecond("acceleration_infill"), mesh.getSettingInMillimetersPerSecond("jerk_infill")}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, int layer_nr, int layer_thickness)
|
||||
: adhesion_extruder_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")))
|
||||
, support_infill_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr")))
|
||||
, support_interface_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr")))
|
||||
, raft_base_config(
|
||||
PrintFeatureType::SupportInterface
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_base_line_width")
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_base_thickness")
|
||||
, adhesion_extruder_train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_jerk")}
|
||||
)
|
||||
, raft_interface_config(
|
||||
PrintFeatureType::Support
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_interface_line_width")
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_interface_thickness")
|
||||
, adhesion_extruder_train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_jerk")}
|
||||
)
|
||||
, raft_surface_config(
|
||||
PrintFeatureType::SupportInterface
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_surface_line_width")
|
||||
, adhesion_extruder_train->getSettingInMicrons("raft_surface_thickness")
|
||||
, adhesion_extruder_train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_jerk")}
|
||||
)
|
||||
, support_infill_config(
|
||||
PrintFeatureType::Support
|
||||
, support_infill_train->getSettingInMicrons("support_line_width")
|
||||
, layer_thickness
|
||||
, support_infill_train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{support_infill_train->getSettingInMillimetersPerSecond("speed_support_infill"), support_infill_train->getSettingInMillimetersPerSecond("acceleration_support_infill"), support_infill_train->getSettingInMillimetersPerSecond("jerk_support_infill")}
|
||||
)
|
||||
, support_interface_config(
|
||||
PrintFeatureType::SupportInterface
|
||||
, support_interface_train->getSettingInMicrons("support_interface_line_width")
|
||||
, layer_thickness
|
||||
, support_interface_train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{support_interface_train->getSettingInMillimetersPerSecond("speed_support_interface"), support_interface_train->getSettingInMillimetersPerSecond("acceleration_support_interface"), support_interface_train->getSettingInMillimetersPerSecond("jerk_support_interface")}
|
||||
)
|
||||
{
|
||||
int extruder_count = storage.meshgroup->getExtruderCount();
|
||||
travel_config_per_extruder.reserve(extruder_count);
|
||||
skirt_brim_config.reserve(extruder_count);
|
||||
prime_tower_config_per_extruder.reserve(extruder_count);
|
||||
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
|
||||
{
|
||||
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
travel_config_per_extruder.emplace_back(
|
||||
PrintFeatureType::MoveCombing
|
||||
, 0
|
||||
, 0
|
||||
, 0.0
|
||||
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("speed_travel"), train->getSettingInMillimetersPerSecond("acceleration_travel"), train->getSettingInMillimetersPerSecond("jerk_travel")}
|
||||
);
|
||||
skirt_brim_config.emplace_back(
|
||||
PrintFeatureType::SkirtBrim
|
||||
, train->getSettingInMicrons("skirt_brim_line_width")
|
||||
, layer_thickness
|
||||
, train->getSettingInPercentage("material_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("skirt_brim_speed"), train->getSettingInMillimetersPerSecond("acceleration_skirt_brim"), train->getSettingInMillimetersPerSecond("jerk_skirt_brim")}
|
||||
);
|
||||
prime_tower_config_per_extruder.emplace_back(
|
||||
PrintFeatureType::SupportInfill
|
||||
, train->getSettingInMicrons("prime_tower_line_width")
|
||||
, layer_thickness
|
||||
, train->getSettingInPercentage("prime_tower_flow")
|
||||
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMillimetersPerSecond("acceleration_prime_tower"), train->getSettingInMillimetersPerSecond("jerk_prime_tower")}
|
||||
);
|
||||
}
|
||||
|
||||
mesh_configs.reserve(storage.meshes.size());
|
||||
for (const SliceMeshStorage& mesh_storage : storage.meshes)
|
||||
{
|
||||
mesh_configs.emplace_back(mesh_storage, layer_thickness);
|
||||
}
|
||||
|
||||
int initial_speedup_layer_count = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
if (layer_nr < initial_speedup_layer_count)
|
||||
{
|
||||
handleInitialLayerSpeedup(storage, layer_nr, initial_speedup_layer_count);
|
||||
}
|
||||
}
|
||||
|
||||
void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storage, int layer_nr, int initial_speedup_layer_count)
|
||||
{
|
||||
std::vector<GCodePathConfig::SpeedDerivatives> global_first_layer_config_per_extruder;
|
||||
global_first_layer_config_per_extruder.reserve(storage.meshgroup->getExtruderCount());
|
||||
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
|
||||
{
|
||||
const ExtruderTrain* extruder = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
global_first_layer_config_per_extruder.emplace_back(
|
||||
GCodePathConfig::SpeedDerivatives{
|
||||
extruder->getSettingInMillimetersPerSecond("speed_print_layer_0")
|
||||
, extruder->getSettingInMillimetersPerSecond("acceleration_print_layer_0")
|
||||
, extruder->getSettingInMillimetersPerSecond("jerk_print_layer_0")
|
||||
});
|
||||
}
|
||||
|
||||
{ // support
|
||||
if (layer_nr < initial_speedup_layer_count)
|
||||
{
|
||||
int extruder_nr_support_infill = storage.getSettingAsIndex((layer_nr <= 0)? "support_extruder_nr_layer_0" : "support_infill_extruder_nr");
|
||||
GCodePathConfig::SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill];
|
||||
support_infill_config.smoothSpeed(first_layer_config_infill, std::max(0, layer_nr), initial_speedup_layer_count);
|
||||
|
||||
int extruder_nr_support_interface = storage.getSettingAsIndex("support_interface_extruder_nr");
|
||||
GCodePathConfig::SpeedDerivatives& first_layer_config_interface = global_first_layer_config_per_extruder[extruder_nr_support_interface];
|
||||
support_interface_config.smoothSpeed(first_layer_config_interface, std::max(0, layer_nr), initial_speedup_layer_count);
|
||||
}
|
||||
}
|
||||
|
||||
{ // extruder configs: travel, skirt/brim (= shield)
|
||||
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
|
||||
{
|
||||
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
GCodePathConfig::SpeedDerivatives initial_layer_speed_config{
|
||||
train->getSettingInMillimetersPerSecond("speed_travel_layer_0")
|
||||
, train->getSettingInMillimetersPerSecond("acceleration_travel_layer_0")
|
||||
, train->getSettingInMillimetersPerSecond("jerk_travel_layer_0")
|
||||
};
|
||||
GCodePathConfig& travel = travel_config_per_extruder[extruder_nr];
|
||||
|
||||
travel.smoothSpeed(initial_layer_speed_config, std::max(0, layer_nr), initial_speedup_layer_count);
|
||||
|
||||
// don't smooth speed for the skirt/brim!
|
||||
// NOTE: not smoothing skirt/brim means the speeds are also not smoothed for the draft/ooze shield
|
||||
|
||||
GCodePathConfig& prime_tower = prime_tower_config_per_extruder[extruder_nr];
|
||||
|
||||
prime_tower.smoothSpeed(initial_layer_speed_config, std::max(0, layer_nr), initial_speedup_layer_count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{ // meshes
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
const SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
|
||||
GCodePathConfig::SpeedDerivatives initial_layer_speed_config{
|
||||
mesh.getSettingInMillimetersPerSecond("speed_print_layer_0")
|
||||
, mesh.getSettingInMillimetersPerSecond("acceleration_print_layer_0")
|
||||
, mesh.getSettingInMillimetersPerSecond("jerk_print_layer_0")
|
||||
};
|
||||
|
||||
MeshPathConfigs& mesh_config = mesh_configs[mesh_idx];
|
||||
//Outer wall speed (per mesh).
|
||||
mesh_config.inset0_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
|
||||
|
||||
//Inner wall speed (per mesh).
|
||||
mesh_config.insetX_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
|
||||
|
||||
//Skin speed (per mesh).
|
||||
mesh_config.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
|
||||
mesh_config.perimeter_gap_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
|
||||
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
//Infill speed (per combine part per mesh).
|
||||
mesh_config.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -1,63 +0,0 @@
|
||||
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef SETTINGS_PATH_CONFIGS_H
|
||||
#define SETTINGS_PATH_CONFIGS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../utils/intpoint.h" // coord_t
|
||||
#include "../GCodePathConfig.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class SliceDataStorage; // forward decl for SliceDataStorage
|
||||
class SliceMeshStorage; // forward decl for SliceDataStorage
|
||||
class ExtruderTrain; // forward decl for SliceDataStorage
|
||||
|
||||
/*!
|
||||
* A class to represent all configurations for all features types of printed lines in a meshgroup.
|
||||
*/
|
||||
class PathConfigStorage
|
||||
{
|
||||
private:
|
||||
const ExtruderTrain* adhesion_extruder_train;
|
||||
const ExtruderTrain* support_infill_train;
|
||||
const ExtruderTrain* support_interface_train;
|
||||
public:
|
||||
class MeshPathConfigs
|
||||
{
|
||||
public:
|
||||
GCodePathConfig inset0_config;
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
GCodePathConfig perimeter_gap_config;
|
||||
std::vector<GCodePathConfig> infill_config;
|
||||
|
||||
MeshPathConfigs(const SliceMeshStorage& mesh, int layer_thickness);
|
||||
};
|
||||
|
||||
GCodePathConfig raft_base_config;
|
||||
GCodePathConfig raft_interface_config;
|
||||
GCodePathConfig raft_surface_config;
|
||||
|
||||
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
|
||||
std::vector<GCodePathConfig> skirt_brim_config; //!< Configuration for skirt and brim per extruder.
|
||||
std::vector<GCodePathConfig> prime_tower_config_per_extruder; //!< Configuration for the prime tower per extruder.
|
||||
|
||||
GCodePathConfig support_infill_config; //!< The config used to print the normal support, rather than the support interface
|
||||
GCodePathConfig support_interface_config; //!< The config to use to print the dense roofs and bottoms of support
|
||||
|
||||
std::vector<MeshPathConfigs> mesh_configs; //!< For each meash the config for all its feature types
|
||||
|
||||
/*!
|
||||
* \warning Note that the layer_nr might be below zero for raft (filler) layers
|
||||
*/
|
||||
PathConfigStorage(const SliceDataStorage& storage, int layer_nr, int layer_thickness);
|
||||
|
||||
private:
|
||||
void handleInitialLayerSpeedup(const SliceDataStorage& storage, int layer_nr, int initial_speedup_layer_count);
|
||||
};
|
||||
|
||||
}; // namespace cura
|
||||
|
||||
#endif // SETTINGS_PATH_CONFIGS_H
|
||||
@@ -93,23 +93,21 @@ void SettingsBase::setSettingInheritBase(std::string key, const SettingsBaseVirt
|
||||
|
||||
std::string SettingsBase::getSettingString(std::string key) const
|
||||
{
|
||||
auto value_it = setting_values.find(key);
|
||||
if (value_it != setting_values.end())
|
||||
if (setting_values.find(key) != setting_values.end())
|
||||
{
|
||||
return value_it->second;
|
||||
return setting_values.at(key);
|
||||
}
|
||||
auto inherit_override_it = setting_inherit_base.find(key);
|
||||
if (inherit_override_it != setting_inherit_base.end())
|
||||
if (setting_inherit_base.find(key) != setting_inherit_base.end())
|
||||
{
|
||||
return inherit_override_it->second->getSettingString(key);
|
||||
return setting_inherit_base.at(key)->getSettingString(key);
|
||||
}
|
||||
if (parent)
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
|
||||
cura::logError("Trying to retrieve unregistered setting with no value given: '%s'\n", key.c_str());
|
||||
std::exit(-1);
|
||||
const_cast<SettingsBase&>(*this).setting_values[key] = "";
|
||||
cura::logWarning("Unregistered setting %s\n", key.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -428,7 +426,7 @@ FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
|
||||
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key) const
|
||||
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "off")
|
||||
@@ -446,7 +444,7 @@ CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key) const
|
||||
return CombingMode::ALL;
|
||||
}
|
||||
|
||||
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key) const
|
||||
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "xy_overrides_z")
|
||||
|
||||
@@ -251,8 +251,8 @@ public:
|
||||
EZSeamType getSettingAsZSeamType(std::string key) const;
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key) const;
|
||||
CombingMode getSettingAsCombingMode(std::string key) const;
|
||||
SupportDistPriority getSettingAsSupportDistPriority(std::string key) const;
|
||||
CombingMode getSettingAsCombingMode(std::string key);
|
||||
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
|
||||
};
|
||||
|
||||
class SettingRegistry;
|
||||
|
||||
+3
-21
@@ -46,7 +46,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
|
||||
{
|
||||
return;
|
||||
}
|
||||
int min_infill_area = mesh.getSettingInMillimeters("min_infill_area");
|
||||
|
||||
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart& part = layer.parts[partNr];
|
||||
@@ -78,22 +78,12 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
|
||||
{
|
||||
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr - downSkinCount]);
|
||||
if (min_infill_area > 0)
|
||||
{
|
||||
not_air.removeSmallAreas(min_infill_area);
|
||||
}
|
||||
downskin = downskin.difference(not_air); // skin overlaps with the walls
|
||||
downskin = downskin.difference(getInsidePolygons(mesh.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(mesh.layers.size()))
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + upSkinCount]);
|
||||
if (min_infill_area > 0)
|
||||
{
|
||||
not_air.removeSmallAreas(min_infill_area);
|
||||
}
|
||||
upskin = upskin.difference(not_air); // skin overlaps with the walls
|
||||
upskin = upskin.difference(getInsidePolygons(mesh.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -105,10 +95,6 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(mesh.layers[downskin_layer_nr]));
|
||||
}
|
||||
if (min_infill_area > 0)
|
||||
{
|
||||
not_air.removeSmallAreas(min_infill_area);
|
||||
}
|
||||
downskin = downskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
@@ -119,10 +105,6 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(mesh.layers[upskin_layer_nr]));
|
||||
}
|
||||
if (min_infill_area > 0)
|
||||
{
|
||||
not_air.removeSmallAreas(min_infill_area);
|
||||
}
|
||||
upskin = upskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +87,38 @@ std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<GCodePathConfig> SliceDataStorage::initializeTravelConfigs()
|
||||
{
|
||||
std::vector<GCodePathConfig> ret;
|
||||
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
|
||||
{
|
||||
travel_config_per_extruder.emplace_back(PrintFeatureType::MoveCombing);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<GCodePathConfig> SliceDataStorage::initializeSkirtBrimConfigs()
|
||||
{
|
||||
std::vector<GCodePathConfig> ret;
|
||||
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
|
||||
{
|
||||
skirt_brim_config.emplace_back(PrintFeatureType::SkirtBrim);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
|
||||
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
|
||||
print_layer_count(0),
|
||||
retraction_config_per_extruder(initializeRetractionConfigs()),
|
||||
extruder_switch_retraction_config_per_extruder(initializeRetractionConfigs()),
|
||||
travel_config_per_extruder(initializeTravelConfigs()),
|
||||
skirt_brim_config(initializeSkirtBrimConfigs()),
|
||||
raft_base_config(PrintFeatureType::SupportInterface),
|
||||
raft_interface_config(PrintFeatureType::Support),
|
||||
raft_surface_config(PrintFeatureType::SupportInterface),
|
||||
support_config(PrintFeatureType::Support),
|
||||
support_skin_config(PrintFeatureType::SupportInterface),
|
||||
max_print_height_second_to_last_extruder(-1),
|
||||
primeTower(*this)
|
||||
{
|
||||
|
||||
+41
-12
@@ -8,9 +8,10 @@
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "mesh.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "gcodeExport.h" // CoastingConfig
|
||||
#include "GCodePathConfig.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -137,11 +138,7 @@ public:
|
||||
|
||||
std::vector<SupportLayer> supportLayers;
|
||||
|
||||
SupportStorage()
|
||||
: generated(false)
|
||||
, layer_nr_max_filled_layer(-1)
|
||||
{
|
||||
}
|
||||
SupportStorage() : generated(false), layer_nr_max_filled_layer(-1) { }
|
||||
~SupportStorage(){ supportLayers.clear(); }
|
||||
};
|
||||
/******************/
|
||||
@@ -155,14 +152,27 @@ public:
|
||||
|
||||
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content (modified while infill meshes are processed)
|
||||
|
||||
GCodePathConfig inset0_config;
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
GCodePathConfig perimeter_gap_config;
|
||||
std::vector<GCodePathConfig> infill_config;
|
||||
|
||||
SubDivCube* base_subdiv_cube;
|
||||
|
||||
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)
|
||||
, perimeter_gap_config(PrintFeatureType::Skin)
|
||||
, base_subdiv_cube(nullptr)
|
||||
{
|
||||
layers.resize(slice_layer_count);
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(PrintFeatureType::Infill);
|
||||
}
|
||||
|
||||
virtual ~SliceMeshStorage();
|
||||
@@ -181,8 +191,18 @@ public:
|
||||
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
|
||||
|
||||
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
|
||||
|
||||
std::vector<GCodePathConfig> skirt_brim_config; //!< Configuration for skirt and brim 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_skin_config; //!< The config to use to print the dense roofs and bottoms of support
|
||||
|
||||
SupportStorage support;
|
||||
|
||||
Polygons skirt_brim[MAX_EXTRUDERS]; //!< Skirt and brim polygons per extruder, ordered from inner to outer polygons.
|
||||
@@ -197,6 +217,21 @@ public:
|
||||
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.
|
||||
|
||||
/*!
|
||||
* Construct the initial retraction_config_per_extruder
|
||||
*/
|
||||
std::vector<RetractionConfig> initializeRetractionConfigs();
|
||||
|
||||
/*!
|
||||
* Construct the initial travel_config_per_extruder
|
||||
*/
|
||||
std::vector<GCodePathConfig> initializeTravelConfigs();
|
||||
|
||||
/*!
|
||||
* Construct the initial skirt & brim configurations for each extruder.
|
||||
*/
|
||||
std::vector<GCodePathConfig> initializeSkirtBrimConfigs();
|
||||
|
||||
/*!
|
||||
* \brief Creates a new slice data storage that stores the slice data of the
|
||||
* specified mesh group.
|
||||
@@ -246,12 +281,6 @@ public:
|
||||
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
|
||||
*/
|
||||
std::vector<bool> getExtrudersUsed(int layer_nr) const;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Construct the retraction_config_per_extruder
|
||||
*/
|
||||
std::vector<RetractionConfig> initializeRetractionConfigs();
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+13
-11
@@ -8,6 +8,7 @@
|
||||
#include "utils/SparsePointGridInclusive.h"
|
||||
|
||||
#include "slicer.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
|
||||
namespace cura {
|
||||
@@ -589,7 +590,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
{
|
||||
if (best_result.pointIdxA == best_result.pointIdxB)
|
||||
{
|
||||
polygons.add(open_polylines[best_polyline_1_idx]);
|
||||
polygons.add(PolygonRef{open_polylines[best_polyline_1_idx]});
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else if (best_result.AtoB)
|
||||
@@ -604,9 +605,9 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
else
|
||||
{
|
||||
unsigned int n = polygons.size();
|
||||
polygons.add(open_polylines[best_polyline_1_idx]);
|
||||
polygons.add(PolygonRef{open_polylines[best_polyline_1_idx]});
|
||||
for(unsigned int j = best_result.pointIdxB; j != best_result.pointIdxA; j = (j + 1) % polygons[best_result.polygonIdx].size())
|
||||
polygons[n].add(polygons[best_result.polygonIdx][j]);
|
||||
PolygonRef{polygons[n]}.add(polygons[best_result.polygonIdx][j]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
}
|
||||
@@ -615,7 +616,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
if (best_result.pointIdxA == best_result.pointIdxB)
|
||||
{
|
||||
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else if (best_result.AtoB)
|
||||
@@ -624,17 +625,17 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
for(unsigned int n = best_result.pointIdxA; n != best_result.pointIdxB; n = (n + 1) % polygons[best_result.polygonIdx].size())
|
||||
poly.add(polygons[best_result.polygonIdx][n]);
|
||||
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
|
||||
open_polylines[best_polyline_2_idx].add(poly[n]);
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(poly[n]);
|
||||
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int n = best_result.pointIdxB; n != best_result.pointIdxA; n = (n + 1) % polygons[best_result.polygonIdx].size())
|
||||
open_polylines[best_polyline_2_idx].add(polygons[best_result.polygonIdx][n]);
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(polygons[best_result.polygonIdx][n]);
|
||||
for(unsigned int n = open_polylines[best_polyline_1_idx].size() - 1; int(n) >= 0; n--)
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
}
|
||||
@@ -888,12 +889,13 @@ 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
|
||||
auto& layers_ref = layers;
|
||||
#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++)
|
||||
{
|
||||
{ MULTITHREAD_FOR_CATCH_EXCEPTION(
|
||||
layers_ref[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
)}
|
||||
handleMultithreadAbort();
|
||||
|
||||
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
|
||||
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
|
||||
|
||||
+3
-7
@@ -165,7 +165,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
const int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
|
||||
const int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
|
||||
const double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
|
||||
const bool use_towers = mesh.getSettingBoolean("support_use_towers") && supportMinAreaSqrt > 0;
|
||||
|
||||
const int layerThickness = storage.getSettingInMicrons("layer_height");
|
||||
const int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
|
||||
@@ -235,10 +234,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
|
||||
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
|
||||
if (use_towers)
|
||||
{
|
||||
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt);
|
||||
}
|
||||
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt);
|
||||
|
||||
std::deque<std::pair<Polygons, Polygons>> basic_and_full_overhang_above;
|
||||
for (unsigned int layer_idx = support_layer_count - 1; layer_idx != support_layer_count - 1 - layerZdistanceTop ; layer_idx--)
|
||||
@@ -268,7 +264,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
supportLayer_this = supportLayer_this.offset(extension_offset);
|
||||
}
|
||||
|
||||
if (use_towers)
|
||||
if (supportMinAreaSqrt > 0)
|
||||
{
|
||||
// handle straight walls
|
||||
AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter);
|
||||
@@ -512,7 +508,7 @@ void AreaSupport::handleTowers(
|
||||
{
|
||||
supportLayer_this = supportLayer_this.unionPolygons(tower_roof);
|
||||
|
||||
if (tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
|
||||
if (PolygonRef{tower_roof[0]}.area() < supportTowerDiameter * supportTowerDiameter)
|
||||
{
|
||||
tower_roof = tower_roof.offset(towerRoofExpansionDistance);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ AABB::AABB(const Polygons& polys)
|
||||
calculate(polys);
|
||||
}
|
||||
|
||||
AABB::AABB(ConstPolygonRef poly)
|
||||
AABB::AABB(const PolygonRef& poly)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
calculate(poly);
|
||||
@@ -43,7 +43,7 @@ void AABB::calculate(const Polygons& polys)
|
||||
}
|
||||
}
|
||||
|
||||
void AABB::calculate(ConstPolygonRef poly)
|
||||
void AABB::calculate(const PolygonRef& poly)
|
||||
{
|
||||
min = Point(POINT_MAX, POINT_MAX);
|
||||
max = Point(POINT_MIN, POINT_MIN);
|
||||
|
||||
+2
-2
@@ -20,10 +20,10 @@ public:
|
||||
AABB(); //!< initializes with invalid min and max
|
||||
AABB(Point& min, Point& max); //!< initializes with given min and max
|
||||
AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons
|
||||
AABB(ConstPolygonRef poly); //!< Computes the boundary box for the given polygons
|
||||
AABB(const PolygonRef& poly); //!< 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)
|
||||
void calculate(ConstPolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
|
||||
void calculate(const PolygonRef& poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
|
||||
|
||||
/*!
|
||||
* Check whether this aabb overlaps with another.
|
||||
|
||||
@@ -10,16 +10,16 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void ListPolyIt::convertPolygonsToLists(const Polygons& polys, ListPolygons& result)
|
||||
void ListPolyIt::convertPolygonsToLists(Polygons& polys, ListPolygons& result)
|
||||
{
|
||||
for (ConstPolygonRef poly : polys)
|
||||
for (PolygonRef poly : polys)
|
||||
{
|
||||
result.emplace_back();
|
||||
convertPolygonToList(poly, result.back());
|
||||
}
|
||||
}
|
||||
|
||||
void ListPolyIt::convertPolygonToList(ConstPolygonRef poly, ListPolygon& result)
|
||||
void ListPolyIt::convertPolygonToList(const PolygonRef& poly, ListPolygon& result)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Point last = poly.back();
|
||||
@@ -39,7 +39,7 @@ void ListPolyIt::convertPolygonToList(ConstPolygonRef poly, ListPolygon& result)
|
||||
}
|
||||
|
||||
|
||||
void ListPolyIt::convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons)
|
||||
void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons)
|
||||
{
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
@@ -48,9 +48,9 @@ void ListPolyIt::convertListPolygonsToPolygons(const ListPolygons& list_polygons
|
||||
}
|
||||
}
|
||||
|
||||
void ListPolyIt::convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon)
|
||||
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon)
|
||||
{
|
||||
for (const Point& p : list_polygon)
|
||||
for (Point& p : list_polygon)
|
||||
{
|
||||
polygon.add(p);
|
||||
}
|
||||
|
||||
@@ -94,28 +94,28 @@ public:
|
||||
* \param polys The polygons to convert
|
||||
* \param result The converted polygons
|
||||
*/
|
||||
static void convertPolygonsToLists(const Polygons& polys, ListPolygons& result);
|
||||
static void convertPolygonsToLists(Polygons& polys, ListPolygons& result);
|
||||
/*!
|
||||
* Convert Polygons to ListPolygons
|
||||
*
|
||||
* \param polys The polygons to convert
|
||||
* \param result The converted polygons
|
||||
*/
|
||||
static void convertPolygonToList(ConstPolygonRef poly, ListPolygon& result);
|
||||
static void convertPolygonToList(const PolygonRef& poly, ListPolygon& result);
|
||||
/*!
|
||||
* Convert ListPolygons to Polygons
|
||||
*
|
||||
* \param list_polygons The polygons to convert
|
||||
* \param polygons The converted polygons
|
||||
*/
|
||||
static void convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons);
|
||||
static void convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons);
|
||||
/*!
|
||||
* Convert ListPolygons to Polygons
|
||||
*
|
||||
* \param list_polygons The polygons to convert
|
||||
* \param polygons The converted polygons
|
||||
*/
|
||||
static void convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon);
|
||||
static void convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon);
|
||||
|
||||
/*!
|
||||
* Insert a point into a ListPolygon if it's not a duplicate of the point before or the point after.
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
/*!
|
||||
* Get the polygon to which this PolygonsPointIndex refers
|
||||
*/
|
||||
ConstPolygonRef getPolygon() const
|
||||
const ConstPolygonRef getPolygon() const
|
||||
{
|
||||
return (*polygons)[poly_idx];
|
||||
}
|
||||
|
||||
+2
-1
@@ -93,8 +93,9 @@ public:
|
||||
{
|
||||
for(unsigned int j=0;j<parts.size();j++)
|
||||
{
|
||||
Polygon poly = PolygonRef{parts[j]};
|
||||
fprintf(out, "<polygon points=\"");
|
||||
for (Point& p : parts[j])
|
||||
for(Point& p : poly)
|
||||
{
|
||||
Point fp = transform(p);
|
||||
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
|
||||
|
||||
+71
-35
@@ -8,9 +8,26 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
bool ConstPolygonRef::shorterThan(int64_t check_length) const
|
||||
/*
|
||||
* Implementation of offset polygon used by PolygonRef and ConstPolygonRef
|
||||
*
|
||||
* \param ret_paths[out] where the offset polygon is stored.
|
||||
* \param path the path to be offset.
|
||||
* \param distance the distance to offset path.
|
||||
* \param joinType See ClipperLib documentation.
|
||||
* \param miter_limit See ClipperLib documentation.
|
||||
*/
|
||||
inline void PolygonRef_offset_impl(ClipperLib::Paths& ret_path, const ClipperLib::Path& path, int distance, ClipperLib::JoinType join_type, double miter_limit)
|
||||
{
|
||||
const ConstPolygonRef& polygon = *this;
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPath(path, join_type, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret_path, distance);
|
||||
}
|
||||
|
||||
bool PolygonRef::shorterThan(int64_t check_length) const
|
||||
{
|
||||
const PolygonRef& polygon = *this;
|
||||
const Point* p0 = &polygon.back();
|
||||
int64_t length = 0;
|
||||
for (const Point& p1 : polygon)
|
||||
@@ -25,9 +42,9 @@ bool ConstPolygonRef::shorterThan(int64_t check_length) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConstPolygonRef::_inside(Point p, bool border_result) const
|
||||
bool PolygonRef::_inside(Point p, bool border_result) const
|
||||
{
|
||||
const ConstPolygonRef thiss = *this;
|
||||
const PolygonRef& thiss = *this;
|
||||
if (size() < 1)
|
||||
{
|
||||
return false;
|
||||
@@ -200,13 +217,10 @@ Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double m
|
||||
return ret;
|
||||
}
|
||||
|
||||
Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType join_type, double miter_limit) const
|
||||
Polygons PolygonRef::offset(int distance, ClipperLib::JoinType joinType, double miter_limit) const
|
||||
{
|
||||
Polygons ret;
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPath(*path, join_type, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret.paths, distance);
|
||||
PolygonRef_offset_impl(ret.paths, *path, distance, joinType, miter_limit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -521,7 +535,7 @@ void Polygons::removeEmptyHoles_processPolyTreeNode(const ClipperLib::PolyNode&
|
||||
}
|
||||
}
|
||||
|
||||
bool ConstPolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length)
|
||||
bool PolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length)
|
||||
{
|
||||
// walk away from the corner until the shortcut > shortcut_length or it would smooth a piece inward
|
||||
// - walk in both directions untill shortcut > shortcut_length
|
||||
@@ -703,7 +717,7 @@ bool ConstPolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, L
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far)
|
||||
void PolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far)
|
||||
{
|
||||
const bool forward_has_converged = forward_is_blocked || forward_is_too_far;
|
||||
const bool backward_has_converged = backward_is_blocked || backward_is_too_far;
|
||||
@@ -759,7 +773,7 @@ void ConstPolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut
|
||||
}
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle)
|
||||
void PolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle)
|
||||
{
|
||||
// 1----b---->2
|
||||
// ^ /
|
||||
@@ -837,6 +851,7 @@ void ConstPolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, co
|
||||
Point b;
|
||||
bool success = LinearAlg2D::getPointOnLineWithDist(a, p1, p2, shortcut_length, b);
|
||||
// v02 has to be longer than ab!
|
||||
p1_it.remove();
|
||||
if (success)
|
||||
{ // if not success then assume b is negligibly close to 2, but rounding errors caused a problem
|
||||
#ifdef ASSERT_INSANE_OUTPUT
|
||||
@@ -844,12 +859,11 @@ void ConstPolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, co
|
||||
#endif // #ifdef ASSERT_INSANE_OUTPUT
|
||||
ListPolyIt::insertPointNonDuplicate(p1_it, p2_it, b);
|
||||
}
|
||||
p1_it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth_outward(float min_angle, int shortcut_length, PolygonRef result) const
|
||||
void PolygonRef::smooth_outward(float min_angle, int shortcut_length, PolygonRef result) const
|
||||
{
|
||||
// example of smoothed out corner:
|
||||
//
|
||||
@@ -952,8 +966,7 @@ Polygons Polygons::smooth_outward(float max_angle, int shortcut_length)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
inline void PolygonRef_smooth_impl(const ClipperLib::Path& thiss, int remove_length, ClipperLib::Path* poly)
|
||||
{
|
||||
// a typical zigzag with the middle part to be removed by removing (1) :
|
||||
//
|
||||
@@ -968,9 +981,7 @@ void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
// |
|
||||
// |
|
||||
// 0
|
||||
const ConstPolygonRef& thiss = *path;
|
||||
ClipperLib::Path* poly = result.path;
|
||||
if (size() > 0)
|
||||
if (thiss.size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
}
|
||||
@@ -1004,11 +1015,11 @@ void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
Point v02T = turn90CCW(v02);
|
||||
int64_t v02_size = vSize(v02);
|
||||
bool force_push = false;
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
for (unsigned int poly_idx = 1; poly_idx < thiss.size(); poly_idx++)
|
||||
{
|
||||
const Point& p1 = thiss[poly_idx];
|
||||
const Point& p2 = thiss[(poly_idx + 1) % size()];
|
||||
const Point& p3 = thiss[(poly_idx + 2) % size()];
|
||||
const Point& p2 = thiss[(poly_idx + 1) % thiss.size()];
|
||||
const Point& p3 = thiss[(poly_idx + 2) % thiss.size()];
|
||||
// v02 computed in last iteration
|
||||
// v02_size as well
|
||||
const Point v12 = p2 - p1;
|
||||
@@ -1037,6 +1048,16 @@ void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth_impl(*path, remove_length, result.path);
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth_impl(*path, remove_length, &(*result));
|
||||
}
|
||||
|
||||
Polygons Polygons::smooth(int remove_length) const
|
||||
{
|
||||
Polygons ret;
|
||||
@@ -1062,10 +1083,8 @@ Polygons Polygons::smooth(int remove_length) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
inline void PolygonRef_smooth2_impl(const ClipperLib::Path& thiss, int remove_length, ClipperLib::Path* poly)
|
||||
{
|
||||
const ConstPolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.path;
|
||||
if (thiss.size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
@@ -1090,6 +1109,16 @@ void ConstPolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth2_impl(*path, remove_length, result.path);
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth2_impl(*path, remove_length, &(*result));
|
||||
}
|
||||
|
||||
Polygons Polygons::smooth2(int remove_length, int min_area) const
|
||||
{
|
||||
Polygons ret;
|
||||
@@ -1117,6 +1146,13 @@ Polygons Polygons::smooth2(int remove_length, int min_area) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType joinType, double miter_limit) const
|
||||
{
|
||||
Polygons ret;
|
||||
PolygonRef_offset_impl(ret.paths, *path, distance, joinType, miter_limit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<PolygonsPart> Polygons::splitIntoParts(bool unionAll) const
|
||||
{
|
||||
std::vector<PolygonsPart> ret;
|
||||
@@ -1138,24 +1174,24 @@ void Polygons::splitIntoParts_processPolyTreeNode(ClipperLib::PolyNode* node, st
|
||||
{
|
||||
ClipperLib::PolyNode* child = node->Childs[n];
|
||||
PolygonsPart part;
|
||||
part.add(child->Contour);
|
||||
part.add(ConstPolygonRef{child->Contour});
|
||||
for(int i=0; i<child->ChildCount(); i++)
|
||||
{
|
||||
part.add(child->Childs[i]->Contour);
|
||||
part.add(ConstPolygonRef{child->Childs[i]->Contour});
|
||||
splitIntoParts_processPolyTreeNode(child->Childs[i], ret);
|
||||
}
|
||||
ret.push_back(part);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx) const
|
||||
unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
|
||||
{
|
||||
const PartsView& partsView = *this;
|
||||
PartsView& partsView = *this;
|
||||
for (unsigned int part_idx_now = 0; part_idx_now < partsView.size(); part_idx_now++)
|
||||
{
|
||||
const std::vector<unsigned int>& partView = partsView[part_idx_now];
|
||||
std::vector<unsigned int>& partView = partsView[part_idx_now];
|
||||
if (partView.size() == 0) { continue; }
|
||||
std::vector<unsigned int>::const_iterator result = std::find(partView.begin(), partView.end(), poly_idx);
|
||||
std::vector<unsigned int>::iterator result = std::find(partView.begin(), partView.end(), poly_idx);
|
||||
if (result != partView.end())
|
||||
{
|
||||
if (boundary_poly_idx) { *boundary_poly_idx = partView[0]; }
|
||||
@@ -1173,13 +1209,13 @@ PolygonsPart PartsView::assemblePart(unsigned int part_idx) const
|
||||
{
|
||||
for (unsigned int poly_idx_ff : partsView[part_idx])
|
||||
{
|
||||
ret.add(polygons[poly_idx_ff]);
|
||||
ret.add(PolygonRef{polygons[poly_idx_ff]});
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PolygonsPart PartsView::assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx) const
|
||||
PolygonsPart PartsView::assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
|
||||
{
|
||||
PolygonsPart ret;
|
||||
unsigned int part_idx = getPartContaining(poly_idx, boundary_poly_idx);
|
||||
@@ -1216,11 +1252,11 @@ void Polygons::splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Poly
|
||||
partsView.emplace_back();
|
||||
unsigned int pos = partsView.size() - 1;
|
||||
partsView[pos].push_back(reordered.size());
|
||||
reordered.add(child->Contour); //TODO: should this steal the internal representation for speed?
|
||||
reordered.add(ConstPolygonRef{child->Contour}); //TODO: should this steal the internal representation for speed?
|
||||
for(int i = 0; i < child->ChildCount(); i++)
|
||||
{
|
||||
partsView[pos].push_back(reordered.size());
|
||||
reordered.add(child->Childs[i]->Contour);
|
||||
reordered.add(ConstPolygonRef{child->Childs[i]->Contour});
|
||||
splitIntoPartsView_processPolyTreeNode(partsView, reordered, child->Childs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+372
-232
@@ -27,8 +27,6 @@ namespace cura {
|
||||
|
||||
class PartsView;
|
||||
class Polygons;
|
||||
class Polygon;
|
||||
class PolygonRef;
|
||||
|
||||
class ListPolyIt;
|
||||
|
||||
@@ -38,45 +36,280 @@ typedef std::vector<ListPolygon> ListPolygons; //!< Polygons represented by a ve
|
||||
const static int clipper_init = (0);
|
||||
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
|
||||
|
||||
class ConstPolygonRef
|
||||
class PolygonRef
|
||||
{
|
||||
friend class Polygons;
|
||||
friend class Polygon;
|
||||
friend class PolygonRef;
|
||||
protected:
|
||||
ClipperLib::Path* path;
|
||||
ConstPolygonRef()
|
||||
PolygonRef()
|
||||
: path(nullptr)
|
||||
{}
|
||||
public:
|
||||
ConstPolygonRef(const ClipperLib::Path& polygon)
|
||||
: path(const_cast<ClipperLib::Path*>(&polygon))
|
||||
PolygonRef(PolygonRef& polygon)
|
||||
:path{polygon.path}
|
||||
{}
|
||||
|
||||
bool operator==(ConstPolygonRef& other) const =delete;
|
||||
|
||||
// needed in std::optional<ConstPolygonRef>
|
||||
// ConstPolygonRef& operator=(const ConstPolygonRef& other) =delete;
|
||||
ConstPolygonRef& operator=(const ConstPolygonRef& other)
|
||||
{
|
||||
path = other.path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PolygonRef(PolygonRef&& polygon)
|
||||
:path{polygon.path}
|
||||
{}
|
||||
PolygonRef(ClipperLib::Path& polygon)
|
||||
: path(&polygon)
|
||||
{}
|
||||
|
||||
unsigned int size() const
|
||||
{
|
||||
return path->size();
|
||||
}
|
||||
|
||||
const Point& operator[] (unsigned int index) const
|
||||
Point& operator[] (unsigned int index) const
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
return (*path)[index];
|
||||
}
|
||||
|
||||
const ClipperLib::Path& operator*() const
|
||||
void* data()
|
||||
{
|
||||
return *path;
|
||||
return path->data();
|
||||
}
|
||||
|
||||
const void* data() const
|
||||
{
|
||||
return path->data();
|
||||
}
|
||||
|
||||
void add(const Point p)
|
||||
{
|
||||
path->push_back(p);
|
||||
}
|
||||
|
||||
PolygonRef& operator=(const PolygonRef& other) { path = other.path; return *this; }
|
||||
|
||||
bool operator==(const PolygonRef& other) const =delete;
|
||||
|
||||
ClipperLib::Path& operator*() { return *path; }
|
||||
|
||||
const ClipperLib::Path& operator*() const { return *path; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
path->emplace_back(args...);
|
||||
}
|
||||
|
||||
void remove(unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
path->erase(path->begin() + index);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
path->clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* On Y-axis positive upward displays, Orientation will return true if the polygon's orientation is counter-clockwise.
|
||||
*
|
||||
* from http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/Orientation.htm
|
||||
*/
|
||||
bool orientation() const
|
||||
{
|
||||
return ClipperLib::Orientation(*path);
|
||||
}
|
||||
|
||||
void reverse()
|
||||
{
|
||||
ClipperLib::ReversePath(*path);
|
||||
}
|
||||
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const;
|
||||
|
||||
int64_t polygonLength() const
|
||||
{
|
||||
int64_t length = 0;
|
||||
Point p0 = (*path)[path->size()-1];
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
Point p1 = (*path)[n];
|
||||
length += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
bool shorterThan(int64_t check_length) const;
|
||||
|
||||
Point min() const
|
||||
{
|
||||
Point ret = Point(POINT_MAX, POINT_MAX);
|
||||
for(Point p : *path)
|
||||
{
|
||||
ret.X = std::min(ret.X, p.X);
|
||||
ret.Y = std::min(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point max() const
|
||||
{
|
||||
Point ret = Point(POINT_MIN, POINT_MIN);
|
||||
for(Point p : *path)
|
||||
{
|
||||
ret.X = std::max(ret.X, p.X);
|
||||
ret.Y = std::max(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
double area() const
|
||||
{
|
||||
return ClipperLib::Area(*path);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Translate the whole polygon in some direction.
|
||||
*
|
||||
* \param translation The direction in which to move the polygon
|
||||
*/
|
||||
void translate(Point translation)
|
||||
{
|
||||
for (Point& p : *this)
|
||||
{
|
||||
p += translation;
|
||||
}
|
||||
}
|
||||
|
||||
Point centerOfMass() const
|
||||
{
|
||||
double x = 0, y = 0;
|
||||
Point p0 = (*path)[path->size()-1];
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
Point p1 = (*path)[n];
|
||||
double second_factor = (p0.X * p1.Y) - (p1.X * p0.Y);
|
||||
|
||||
x += double(p0.X + p1.X) * second_factor;
|
||||
y += double(p0.Y + p1.Y) * second_factor;
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
double area = Area(*path);
|
||||
|
||||
x = x / 6 / area;
|
||||
y = y / 6 / area;
|
||||
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
Point closestPointTo(Point p) const
|
||||
{
|
||||
Point ret = p;
|
||||
float bestDist = FLT_MAX;
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
float dist = vSize2f(p - (*path)[n]);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
ret = (*path)[n];
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Check if we are inside the polygon. We do this by tracing from the point towards the positive X direction,
|
||||
* every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
|
||||
* Care needs to be taken, if p.Y exactly matches a vertex to the right of p, then we need to count 1 intersect if the
|
||||
* outline passes vertically past; and 0 (or 2) intersections if that point on the outline is a 'top' or 'bottom' vertex.
|
||||
* The easiest way to do this is to break out two cases for increasing and decreasing Y ( from p0 to p1 ).
|
||||
* A segment is tested if pa.Y <= p.Y < pb.Y, where pa and pb are the points (from p0,p1) with smallest & largest Y.
|
||||
* When both have the same Y, no intersections are counted but there is a special test to see if the point falls
|
||||
* exactly on the line.
|
||||
*
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
* \deprecated This function is no longer used, since the Clipper function is used by the function PolygonRef::inside(.)
|
||||
*
|
||||
* \param p The point for which to check if it is inside this polygon
|
||||
* \param border_result What to return when the point is exactly on the border
|
||||
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
|
||||
*/
|
||||
bool _inside(Point p, bool border_result = false) const;
|
||||
|
||||
/*!
|
||||
* Clipper function.
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
* http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/PointInPolygon.htm
|
||||
*/
|
||||
bool inside(Point p, bool border_result = false) const
|
||||
{
|
||||
int res = ClipperLib::PointInPolygon(p, *path);
|
||||
if (res == -1)
|
||||
{
|
||||
return border_result;
|
||||
}
|
||||
return res == 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Smooth out small perpendicular segments and store the result in \p result.
|
||||
* Smoothing is performed by removing the inner most vertex of a line segment smaller than \p remove_length
|
||||
* which has an angle with the next and previous line segment smaller than roughly 150*
|
||||
*
|
||||
* Note that in its current implementation this function doesn't remove line segments with an angle smaller than 30*
|
||||
* Such would be the case for an N shape.
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth(int remove_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
|
||||
*
|
||||
* \param angle The maximum angle of inner corners to be smoothed out
|
||||
* \param shortcut_length The desired length of the shortcut line segment introduced (shorter shortcuts may be unavoidable)
|
||||
* \param result The resulting polygon
|
||||
*/
|
||||
void smooth_outward(float angle, int shortcut_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth2(int remove_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Removes consecutive line segments with same orientation and changes this polygon.
|
||||
*
|
||||
* Removes verts which are connected to line segments which are both too small.
|
||||
* Removes verts which detour from a direct line from the previous and next vert by a too small amount.
|
||||
*
|
||||
* \param smallest_line_segment_squared maximal squared length of removed line segments
|
||||
* \param allowed_error_distance_squared The square of the distance of the middle point to the line segment of the consecutive and previous point for which the middle point is removed
|
||||
*/
|
||||
void simplify(int smallest_line_segment_squared = 100, int allowed_error_distance_squared = 25);
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
path->pop_back();
|
||||
}
|
||||
|
||||
ClipperLib::Path::reference back() const
|
||||
{
|
||||
return path->back();
|
||||
}
|
||||
|
||||
ClipperLib::Path::iterator begin()
|
||||
{
|
||||
return path->begin();
|
||||
}
|
||||
|
||||
ClipperLib::Path::iterator end()
|
||||
{
|
||||
return path->end();
|
||||
}
|
||||
|
||||
ClipperLib::Path::const_iterator begin() const
|
||||
@@ -89,9 +322,90 @@ public:
|
||||
return path->end();
|
||||
}
|
||||
|
||||
ClipperLib::Path::const_reference back() const
|
||||
friend class Polygons;
|
||||
friend class Polygon;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Smooth out a simple corner consisting of two linesegments.
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \param poly The polygon in which to find the corner
|
||||
* \param p0 The point before the corner
|
||||
* \param p1 The corner
|
||||
* \param p2 The point after the corner
|
||||
* \param p0_it Iterator to the point before the corner
|
||||
* \param p1_it Iterator to the corner
|
||||
* \param p2_it Iterator to the point after the corner
|
||||
* \param v10 Vector from \p p1 to \p p0
|
||||
* \param v12 Vector from \p p1 to \p p2
|
||||
* \param v02 Vector from \p p0 to \p p2
|
||||
* \param shortcut_length The desired length ofthe shortcutting line
|
||||
* \param cos_angle The cosine on the angle in L 012
|
||||
*/
|
||||
static void smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle);
|
||||
|
||||
/*!
|
||||
* Smooth out a complex corner where the shortcut bypasses more than two line segments
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \warning This function might try to remove the whole polygon
|
||||
* Error code -1 means the whole polygon should be removed (which means it is a hole polygon)
|
||||
*
|
||||
*
|
||||
* \param poly The polygon in which to find the corner
|
||||
* \param p1 The corner point
|
||||
* \param[in,out] p0_it Iterator to the last point checked before \p p1 to consider cutting off
|
||||
* \param[in,out] p2_it Iterator to the last point checked after \p p1 to consider cutting off
|
||||
* \param shortcut_length The desired length ofthe shortcutting line
|
||||
* \return Whether this whole polygon whould be removed by the smoothing
|
||||
*/
|
||||
static bool smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length);
|
||||
|
||||
/*!
|
||||
* Try to take a step away from the corner point in order to take a bigger shortcut.
|
||||
*
|
||||
* Try to take the shortcut from a place as far away from the corner as the place we are taking the shortcut to.
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \param[in] p1 The corner point
|
||||
* \param[in] shortcut_length2 The square of the desired length ofthe shortcutting line
|
||||
* \param[in,out] p0_it Iterator to the previously checked point somewhere beyond \p p1. Updated for the next iteration.
|
||||
* \param[in,out] p2_it Iterator to the previously checked point somewhere before \p p1. Updated for the next iteration.
|
||||
* \param[in,out] forward_is_blocked Whether trying another step forward is blocked by the smoothing outward condition. Updated for the next iteration.
|
||||
* \param[in,out] backward_is_blocked Whether trying another step backward is blocked by the smoothing outward condition. Updated for the next iteration.
|
||||
* \param[in,out] forward_is_too_far Whether trying another step forward is blocked by the shortcut length condition. Updated for the next iteration.
|
||||
* \param[in,out] backward_is_too_far Whether trying another step backward is blocked by the shortcut length condition. Updated for the next iteration.
|
||||
*/
|
||||
static void smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far);
|
||||
};
|
||||
|
||||
class ConstPolygonRef
|
||||
{
|
||||
const ClipperLib::Path* path;
|
||||
ConstPolygonRef()
|
||||
: path(nullptr)
|
||||
{}
|
||||
public:
|
||||
ConstPolygonRef(const ClipperLib::Path& polygon)
|
||||
: path(&polygon)
|
||||
{}
|
||||
ConstPolygonRef(const PolygonRef& polygon)
|
||||
: path(&(*polygon))
|
||||
{}
|
||||
|
||||
unsigned int size() const
|
||||
{
|
||||
return path->back();
|
||||
return path->size();
|
||||
}
|
||||
|
||||
const Point& operator[] (unsigned int index) const
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
return (*path)[index];
|
||||
}
|
||||
|
||||
const void* data() const
|
||||
@@ -99,6 +413,13 @@ public:
|
||||
return path->data();
|
||||
}
|
||||
|
||||
ConstPolygonRef& operator=(const ConstPolygonRef& other) { path = other.path; return *this; }
|
||||
|
||||
ConstPolygonRef& operator=(const PolygonRef& other) { path = &(*other); return *this; }
|
||||
|
||||
bool operator==(const ConstPolygonRef& other) const =delete;
|
||||
|
||||
const ClipperLib::Path& operator*() const { return *path; }
|
||||
|
||||
/*!
|
||||
* On Y-axis positive upward displays, Orientation will return true if the polygon's orientation is counter-clockwise.
|
||||
@@ -192,30 +513,10 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Check if we are inside the polygon. We do this by tracing from the point towards the positive X direction,
|
||||
* every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
|
||||
* Care needs to be taken, if p.Y exactly matches a vertex to the right of p, then we need to count 1 intersect if the
|
||||
* outline passes vertically past; and 0 (or 2) intersections if that point on the outline is a 'top' or 'bottom' vertex.
|
||||
* The easiest way to do this is to break out two cases for increasing and decreasing Y ( from p0 to p1 ).
|
||||
* A segment is tested if pa.Y <= p.Y < pb.Y, where pa and pb are the points (from p0,p1) with smallest & largest Y.
|
||||
* When both have the same Y, no intersections are counted but there is a special test to see if the point falls
|
||||
* exactly on the line.
|
||||
*
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
* \deprecated This function is no longer used, since the Clipper function is used by the function PolygonRef::inside(.)
|
||||
*
|
||||
* \param p The point for which to check if it is inside this polygon
|
||||
* \param border_result What to return when the point is exactly on the border
|
||||
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
|
||||
*/
|
||||
bool _inside(Point p, bool border_result = false) const;
|
||||
|
||||
/*!
|
||||
* Clipper function.
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
*
|
||||
* http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/PointInPolygon.htm
|
||||
*/
|
||||
bool inside(Point p, bool border_result = false) const
|
||||
@@ -232,197 +533,41 @@ public:
|
||||
* Smooth out small perpendicular segments and store the result in \p result.
|
||||
* Smoothing is performed by removing the inner most vertex of a line segment smaller than \p remove_length
|
||||
* which has an angle with the next and previous line segment smaller than roughly 150*
|
||||
*
|
||||
*
|
||||
* Note that in its current implementation this function doesn't remove line segments with an angle smaller than 30*
|
||||
* Such would be the case for an N shape.
|
||||
*
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth(int remove_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
|
||||
*
|
||||
* \param angle The maximum angle of inner corners to be smoothed out
|
||||
* \param shortcut_length The desired length of the shortcut line segment introduced (shorter shortcuts may be unavoidable)
|
||||
* \param result The resulting polygon
|
||||
*/
|
||||
void smooth_outward(float angle, int shortcut_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
*
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth2(int remove_length, PolygonRef result) const;
|
||||
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Smooth out a simple corner consisting of two linesegments.
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \param poly The polygon in which to find the corner
|
||||
* \param p0 The point before the corner
|
||||
* \param p1 The corner
|
||||
* \param p2 The point after the corner
|
||||
* \param p0_it Iterator to the point before the corner
|
||||
* \param p1_it Iterator to the corner
|
||||
* \param p2_it Iterator to the point after the corner
|
||||
* \param v10 Vector from \p p1 to \p p0
|
||||
* \param v12 Vector from \p p1 to \p p2
|
||||
* \param v02 Vector from \p p0 to \p p2
|
||||
* \param shortcut_length The desired length ofthe shortcutting line
|
||||
* \param cos_angle The cosine on the angle in L 012
|
||||
*/
|
||||
static void smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle);
|
||||
|
||||
/*!
|
||||
* Smooth out a complex corner where the shortcut bypasses more than two line segments
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \warning This function might try to remove the whole polygon
|
||||
* Error code -1 means the whole polygon should be removed (which means it is a hole polygon)
|
||||
*
|
||||
*
|
||||
* \param poly The polygon in which to find the corner
|
||||
* \param p1 The corner point
|
||||
* \param[in,out] p0_it Iterator to the last point checked before \p p1 to consider cutting off
|
||||
* \param[in,out] p2_it Iterator to the last point checked after \p p1 to consider cutting off
|
||||
* \param shortcut_length The desired length ofthe shortcutting line
|
||||
* \return Whether this whole polygon whould be removed by the smoothing
|
||||
*/
|
||||
static bool smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length);
|
||||
|
||||
/*!
|
||||
* Try to take a step away from the corner point in order to take a bigger shortcut.
|
||||
*
|
||||
* Try to take the shortcut from a place as far away from the corner as the place we are taking the shortcut to.
|
||||
*
|
||||
* Auxiliary function for \ref smooth_outward
|
||||
*
|
||||
* \param[in] p1 The corner point
|
||||
* \param[in] shortcut_length2 The square of the desired length ofthe shortcutting line
|
||||
* \param[in,out] p0_it Iterator to the previously checked point somewhere beyond \p p1. Updated for the next iteration.
|
||||
* \param[in,out] p2_it Iterator to the previously checked point somewhere before \p p1. Updated for the next iteration.
|
||||
* \param[in,out] forward_is_blocked Whether trying another step forward is blocked by the smoothing outward condition. Updated for the next iteration.
|
||||
* \param[in,out] backward_is_blocked Whether trying another step backward is blocked by the smoothing outward condition. Updated for the next iteration.
|
||||
* \param[in,out] forward_is_too_far Whether trying another step forward is blocked by the shortcut length condition. Updated for the next iteration.
|
||||
* \param[in,out] backward_is_too_far Whether trying another step backward is blocked by the shortcut length condition. Updated for the next iteration.
|
||||
*/
|
||||
static void smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far);
|
||||
};
|
||||
|
||||
|
||||
class PolygonRef : public ConstPolygonRef
|
||||
{
|
||||
PolygonRef()
|
||||
: ConstPolygonRef()
|
||||
{}
|
||||
public:
|
||||
PolygonRef(ClipperLib::Path& polygon)
|
||||
: ConstPolygonRef(polygon)
|
||||
{}
|
||||
|
||||
PolygonRef& operator=(const PolygonRef& other)
|
||||
{
|
||||
path = other.path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Point& operator[] (unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
return (*path)[index];
|
||||
}
|
||||
|
||||
ClipperLib::Path::iterator begin()
|
||||
{
|
||||
return path->begin();
|
||||
}
|
||||
|
||||
ClipperLib::Path::iterator end()
|
||||
{
|
||||
return path->end();
|
||||
}
|
||||
|
||||
ClipperLib::Path::reference back()
|
||||
ClipperLib::Path::const_reference back() const
|
||||
{
|
||||
return path->back();
|
||||
}
|
||||
|
||||
void* data()
|
||||
ClipperLib::Path::const_iterator begin() const
|
||||
{
|
||||
return path->data();
|
||||
return path->begin();
|
||||
}
|
||||
|
||||
void add(const Point p)
|
||||
ClipperLib::Path::const_iterator end() const
|
||||
{
|
||||
path->push_back(p);
|
||||
return path->end();
|
||||
}
|
||||
|
||||
PolygonRef& operator=(ConstPolygonRef& other) { path = other.path; return *this; }
|
||||
|
||||
ClipperLib::Path& operator*()
|
||||
{
|
||||
return *path;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
path->emplace_back(args...);
|
||||
}
|
||||
|
||||
void remove(unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
path->erase(path->begin() + index);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
path->clear();
|
||||
}
|
||||
|
||||
void reverse()
|
||||
{
|
||||
ClipperLib::ReversePath(*path);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Translate the whole polygon in some direction.
|
||||
*
|
||||
* \param translation The direction in which to move the polygon
|
||||
*/
|
||||
void translate(Point translation)
|
||||
{
|
||||
for (Point& p : *this)
|
||||
{
|
||||
p += translation;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Removes consecutive line segments with same orientation and changes this polygon.
|
||||
*
|
||||
* Removes verts which are connected to line segments which are both too small.
|
||||
* Removes verts which detour from a direct line from the previous and next vert by a too small amount.
|
||||
*
|
||||
* \param smallest_line_segment_squared maximal squared length of removed line segments
|
||||
* \param allowed_error_distance_squared The square of the distance of the middle point to the line segment of the consecutive and previous point for which the middle point is removed
|
||||
*/
|
||||
void simplify(int smallest_line_segment_squared = 100, int allowed_error_distance_squared = 25);
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
path->pop_back();
|
||||
}
|
||||
friend class Polygons;
|
||||
friend class Polygon;
|
||||
};
|
||||
|
||||
class Polygon : public PolygonRef
|
||||
@@ -434,7 +579,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
Polygon(PolygonRef& other)
|
||||
Polygon(const PolygonRef& other)
|
||||
: PolygonRef(poly)
|
||||
{
|
||||
poly = *other.path;
|
||||
@@ -458,14 +603,13 @@ public:
|
||||
|
||||
unsigned int pointCount() const; //!< Return the amount of points in all polygons
|
||||
|
||||
PolygonRef operator[] (unsigned int index)
|
||||
ClipperLib::Path& operator[] (unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
return paths[index];
|
||||
}
|
||||
ConstPolygonRef operator[] (unsigned int index) const
|
||||
{
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
return paths[index];
|
||||
}
|
||||
ClipperLib::Paths::iterator begin()
|
||||
@@ -509,7 +653,7 @@ public:
|
||||
{
|
||||
paths.clear();
|
||||
}
|
||||
void add(ConstPolygonRef& poly)
|
||||
void add(const PolygonRef& poly)
|
||||
{
|
||||
paths.push_back(*poly.path);
|
||||
}
|
||||
@@ -540,18 +684,14 @@ public:
|
||||
paths.emplace_back(args...);
|
||||
}
|
||||
|
||||
PolygonRef newPoly()
|
||||
ClipperLib::Path& newPoly()
|
||||
{
|
||||
paths.emplace_back();
|
||||
return PolygonRef(paths.back());
|
||||
return paths.back();
|
||||
}
|
||||
PolygonRef back()
|
||||
ClipperLib::Path& back()
|
||||
{
|
||||
return PolygonRef(paths.back());
|
||||
}
|
||||
ConstPolygonRef back() const
|
||||
{
|
||||
return ConstPolygonRef(paths.back());
|
||||
return paths.back();
|
||||
}
|
||||
|
||||
Polygons() {}
|
||||
@@ -733,7 +873,7 @@ public:
|
||||
Polygons& thiss = *this;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
thiss[p].simplify(smallest_line_segment_squared, allowed_error_distance_squared);
|
||||
PolygonRef{thiss[p]}.simplify(smallest_line_segment_squared, allowed_error_distance_squared);
|
||||
if (thiss[p].size() < 3)
|
||||
{
|
||||
remove(p);
|
||||
@@ -794,7 +934,7 @@ public:
|
||||
Polygons& thiss = *this;
|
||||
for(unsigned int i=0; i<size(); i++)
|
||||
{
|
||||
double area = INT2MM(INT2MM(fabs(thiss[i].area())));
|
||||
double area = INT2MM(INT2MM(fabs(PolygonRef{thiss[i]}.area())));
|
||||
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
|
||||
{
|
||||
remove(i);
|
||||
@@ -996,11 +1136,11 @@ public:
|
||||
{
|
||||
if (size() < 1)
|
||||
return false;
|
||||
if (!(*this)[0].inside(p))
|
||||
if (!PolygonRef{(*this)[0]}.inside(p))
|
||||
return false;
|
||||
for(unsigned int n=1; n<paths.size(); n++)
|
||||
{
|
||||
if ((*this)[n].inside(p))
|
||||
if (PolygonRef{(*this)[n]}.inside(p))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1022,7 +1162,7 @@ public:
|
||||
* \param boundary_poly_idx Optional output parameter: The index of the boundary polygon of the part in \p polygons
|
||||
* \return The PolygonsPart containing the polygon with index \p poly_idx
|
||||
*/
|
||||
unsigned int getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr) const;
|
||||
unsigned int getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr);
|
||||
/*!
|
||||
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
|
||||
*
|
||||
@@ -1030,7 +1170,7 @@ public:
|
||||
* \param boundary_poly_idx Optional output parameter: The index of the boundary polygon of the part in \p polygons
|
||||
* \return The PolygonsPart containing the polygon with index \p poly_idx
|
||||
*/
|
||||
PolygonsPart assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr) const;
|
||||
PolygonsPart assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr);
|
||||
/*!
|
||||
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
|
||||
*
|
||||
|
||||
@@ -692,7 +692,7 @@ PolygonsPointIndex PolygonUtils::findNearestVert(const Point from, const Polygon
|
||||
return closest_vert;
|
||||
}
|
||||
|
||||
unsigned int PolygonUtils::findNearestVert(const Point from, ConstPolygonRef poly)
|
||||
unsigned int PolygonUtils::findNearestVert(const Point from, const PolygonRef poly)
|
||||
{
|
||||
int64_t best_dist2 = std::numeric_limits<int64_t>::max();
|
||||
unsigned int closest_vert_idx = -1;
|
||||
@@ -947,7 +947,7 @@ bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Po
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& startPoint, const Point& endPoint)
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(PolygonRef poly, const Point& startPoint, const Point& endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ public:
|
||||
* \param poly The polygon in which to search
|
||||
* \return The index to the nearest vertex on the polygon
|
||||
*/
|
||||
static unsigned int findNearestVert(const Point from, ConstPolygonRef poly);
|
||||
static unsigned int findNearestVert(const Point from, const PolygonRef poly);
|
||||
|
||||
/*!
|
||||
* Create a SparsePointGridInclusive mapping from locations to line segments occurring in the \p polygons
|
||||
@@ -449,7 +449,7 @@ public:
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& startPoint, const Point& endPoint);
|
||||
static bool polygonCollidesWithLineSegment(const PolygonRef poly, const Point& startPoint, const Point& endPoint);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//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
|
||||
|
||||
@@ -38,7 +35,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, "%d", int(coord)); // convert int to string
|
||||
int char_count = sprintf(buffer, "%" PRId64, coord); // convert int to string
|
||||
#ifdef DEBUG
|
||||
if (char_count + 1 >= int(buffer_size)) // + 1 for the null character
|
||||
{
|
||||
|
||||
+1
-4
@@ -235,14 +235,11 @@ class Setting:
|
||||
tree = ast.parse(code, "eval")
|
||||
compiled = compile(code, self._key, "eval")
|
||||
except (SyntaxError, TypeError) as e:
|
||||
print("Parse error in function (" + str(code) + ") for setting", self._key + ":", str(e))
|
||||
return None
|
||||
print("Parse error in function (" + code + ") for setting", self._key + ":", str(e))
|
||||
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