Comparar commits

..

37 Commits

Autor SHA1 Mensagem Data
Tim Kuipers f1d59af2a9 Merge branch 'feature_wall_reinforcement' of https://github.com/Ultimaker/CuraEngine into feature_wall_reinforcement 2015-12-03 19:06:26 +01:00
Tim Kuipers 7d354ca587 merge fixes 2015-12-03 19:03:04 +01:00
Tim Kuipers 7560250348 bugfixes wall_reinforcement 2015-12-03 18:57:21 +01:00
Tim Kuipers 9b7a985b54 feature: multple reinforcement walls 2015-12-03 18:57:21 +01:00
Tim Kuipers db0c47e314 doc: wall reinforcement better documentation 2015-12-03 18:57:21 +01:00
Tim Kuipers 4aed36d7d5 bugfixes wall reinforcement: now also works in edge cases and when totally disabled. 2015-12-03 18:57:21 +01:00
Tim Kuipers 44b0522447 wall reinforcement extra walls now have wall_x config 2015-12-03 18:57:21 +01:00
Tim Kuipers 464fb29bca bugfix: wall reinforcement had wrong config 2015-12-03 18:57:21 +01:00
Tim Kuipers fbf989282b refactor: reinforcement_wall ==> wall reinforcement 2015-12-03 18:57:21 +01:00
Tim Kuipers db99b46506 bugfixes for reinforcement wall 2015-12-03 18:57:14 +01:00
Tim Kuipers c7ffd5b23d reinforcement_wall new feature 2015-12-03 18:56:05 +01:00
Tim Kuipers b8d5474811 merge 2015-12-03 14:57:13 +01:00
Ghostkeeper 4c60339695 Merge pull request #281 from Rhoban/master
Adding ENABLE_ARCUS option in CMakeLists

This allows compiling CuraEngine for command-line use only.
2015-12-03 11:01:57 +01:00
Gregwar e1a594ad3e ENABLE_ARCUS: Adding message(STATUS) line 2015-12-03 10:22:28 +01:00
Ghostkeeper 2b3f22872e Fix codestyle and superfluous commented code
Contributes to CURA-359.
2015-12-02 13:32:48 +01:00
Ghostkeeper fa03c3823f Merge branch '2.1'
Merging recent fixes to master, mostly to be able to get Jenkins to report the updated tests.
2015-12-02 12:15:39 +01:00
Tim Kuipers 2cf0d40775 fix: reserve [extruder_count] extruder plans, so that it's quite unlikely for the vector to get resized (CURA-298) 2015-12-01 17:23:16 +01:00
Tim Kuipers d20df39d5f Merge branch '2.1' 2015-12-01 16:31:58 +01:00
Tim Kuipers 57bc411bc6 refactor: speed_iconic ==> speed_base, speed ==> speed_current (CURA-496) 2015-12-01 16:31:34 +01:00
Gregwar 0c30d27892 Adding ENABLE_ARCUS option in CMakeLists 2015-12-01 16:27:14 +01:00
Tim Kuipers 0c9562b273 rename: support_extruder_nr ==> support_infill_extruder_nr 2015-12-01 16:07:04 +01:00
Tim Kuipers 742eedda06 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-12-01 12:30:37 +01:00
Tim Kuipers bc6a139d1f lil optimization (CURA-480) 2015-12-01 10:59:13 +01:00
Tim Kuipers 9770e91961 bugfix: first extruder plan might be empty, so then it should be removed (CURA-480) 2015-12-01 10:57:58 +01:00
daid 9e3dfb5b12 Allow running of test with just the default settings. 2015-11-30 15:22:13 +01:00
Tim Kuipers c05c3ddaed bugfix: always use safest distance for combing when a single distance is used for inside combing (CURA-480) 2015-11-27 16:32:41 +01:00
Tim Kuipers 804c288353 storage.getExtrudersUsed (CURA-480) 2015-11-27 16:30:04 +01:00
Tim Kuipers 56d6d9196f bugfixes wall_reinforcement 2015-11-06 09:26:59 +01:00
Tim Kuipers e9c9157ac8 feature: multple reinforcement walls 2015-11-05 20:59:01 +01:00
Tim Kuipers 8ad0416428 merge fix from master 2015-11-05 19:25:47 +01:00
Tim Kuipers c185c846e6 doc: wall reinforcement better documentation 2015-10-22 21:04:55 +02:00
Tim Kuipers e28cef3272 bugfixes wall reinforcement: now also works in edge cases and when totally disabled. 2015-10-22 21:00:49 +02:00
Tim Kuipers 777f45ac16 wall reinforcement extra walls now have wall_x config 2015-10-22 20:26:16 +02:00
Tim Kuipers a9a3c8a4b7 bugfix: wall reinforcement had wrong config 2015-10-22 20:22:35 +02:00
Tim Kuipers ad2cf3b7a6 refactor: reinforcement_wall ==> wall reinforcement 2015-10-22 20:19:43 +02:00
Tim Kuipers a57ec01cc4 bugfixes for reinforcement wall 2015-10-22 19:45:16 +02:00
Tim Kuipers c787ffad02 reinforcement_wall new feature 2015-10-22 19:22:23 +02:00
68 arquivos alterados com 1817 adições e 2577 exclusões
+17 -15
Ver Arquivo
@@ -2,7 +2,14 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
find_package(Arcus REQUIRED)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -10,10 +17,6 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
@@ -70,14 +73,7 @@ set(engine_SRCS # Except main.cpp.
src/Weaver.cpp
src/Wireframe2gcode.cpp
src/infill/NoZigZagConnectorProcessor.cpp
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/utils/gettime.cpp
src/utils/LinearAlg2D.cpp
src/utils/logoutput.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
@@ -89,12 +85,17 @@ set(engine_TEST
LinearAlg2DTest
)
# Generating ProtoBuf protocol.
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
# Compiling CuraEngine itself.
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
target_link_libraries(_CuraEngine clipper Arcus)
target_link_libraries(_CuraEngine clipper)
if (ENABLE_ARCUS)
target_link_libraries(_CuraEngine Arcus)
endif ()
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
@@ -118,4 +119,5 @@ endif()
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
include(CPackConfig.cmake)
+25 -6
Ver Arquivo
@@ -2,18 +2,20 @@ syntax = "proto3";
package cura.proto;
message ObjectList
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
}
// typeid 1
message Slice
{
repeated ObjectList object_lists = 1;
}
message Object
message Object
{
int64 id = 1;
bytes vertices = 2; //An array of 3 floats.
@@ -22,13 +24,28 @@ message Object
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
}
message Progress
// typeid 3
message Progress
{
float amount = 1;
}
// typeid 2
message SlicedObjectList
{
repeated SlicedObject objects = 1;
}
message SlicedObject
{
int64 id = 1;
repeated Layer layers = 2;
}
message Layer {
int32 id = 1;
float height = 2;
float thickness = 3;
@@ -53,16 +70,20 @@ message Polygon {
float line_width = 3;
}
// typeid 4
message GCodeLayer {
int64 id = 1;
bytes data = 2;
}
// typeid 5
message ObjectPrintTime {
int64 id = 1;
float time = 2;
float material_amount = 3;
}
// typeid 6
message SettingList {
repeated Setting settings = 1;
}
@@ -73,9 +94,7 @@ message Setting {
bytes value = 2;
}
// typeid 7
message GCodePrefix {
bytes data = 2;
}
message SlicingFinished {
}
+185 -126
Ver Arquivo
@@ -21,8 +21,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
gcode.resetTotalPrintTimeAndFilament();
}
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->beginGCode();
if (command_socket)
command_socket->beginGCode();
setConfigFanSpeedLayerTime();
@@ -72,11 +72,17 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
processLayer(storage, layer_nr, total_layers, has_raft);
}
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
max_object_height = std::max(max_object_height, storage.model_max.z);
if (command_socket)
{
command_socket->sendGCodeLayer();
command_socket->endSendSlicedObject();
}
layer_plan_buffer.flush();
}
@@ -98,9 +104,12 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
CoastingConfig& coasting_config = storage.coasting_config.back();
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
coasting_config.coasting_volume = train->getSettingInCubicMillimeters("coasting_volume");
coasting_config.coasting_min_volume = train->getSettingInCubicMillimeters("coasting_min_volume");
coasting_config.coasting_speed = train->getSettingInPercentage("coasting_speed") / 100.0;
coasting_config.coasting_volume_move = train->getSettingInCubicMillimeters("coasting_volume_move");
coasting_config.coasting_min_volume_move = train->getSettingInCubicMillimeters("coasting_min_volume_move");
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move") / 100.0;
coasting_config.coasting_volume_retract = train->getSettingInCubicMillimeters("coasting_volume_retract");
coasting_config.coasting_min_volume_retract = train->getSettingInCubicMillimeters("coasting_min_volume_retract");
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
}
}
@@ -153,8 +162,8 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
}
{ // support
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"));
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_lines"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_infill"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
}
@@ -164,22 +173,30 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
}
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
}
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
}
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
{
if (!CommandSocket::isInstantiated())
if (!command_socket)
{
std::string prefix = gcode.getFileHeader();
gcode.writeCode(prefix.c_str());
std::ostringstream prefix;
prefix << "FLAVOR:" << toString(gcode.getFlavor());
gcode.writeComment(prefix.str().c_str());
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
gcode.writeComment("TIME:666");
gcode.writeComment("MATERIAL:666");
gcode.writeComment("MATERIAL2:-1");
}
}
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
{
@@ -231,8 +248,7 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
gcode.resetExtrusionValue();
gcode.setZ(max_object_height + 5000);
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
gcode.writeMove(last_position_planned, getSettingInMillimetersPerSecond("speed_travel"), 0);
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
}
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
@@ -257,35 +273,25 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
}
// some infill config for all lines infill generation below
Polygons* in_between = nullptr;
int offset_from_poly_outline = 0;
bool avoidOverlappingPerimeters = false;
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
{ // raft base layer
int layer_nr = -n_raft_surface_layers - 2;
int layer_height = getSettingInMicrons("raft_base_thickness");
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
gcode_layer.setExtruder(extruder_nr);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
}
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
Polygons raftLines;
double fill_angle = 0;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines, in_between);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_base_config.getLineWidth());
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -298,19 +304,17 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_nr = -n_raft_surface_layers - 1;
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
Polygons raftLines;
int offset_from_poly_outline = 0;
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines, in_between);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_interface_config.getLineWidth());
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -325,21 +329,17 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
{ // raft surface layers
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
z += layer_height;
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
}
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
Polygons raft_lines;
int offset_from_poly_outline = 0;
double fill_angle = 90 * raftSurfaceLayer;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raft_lines, in_between);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config);
sendPolygons(SupportType, layer_nr, raft_lines, storage.raft_surface_config.getLineWidth());
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -351,19 +351,26 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, command_socket);
int layer_thickness = getSettingInMicrons("layer_height");
if (layer_nr == 0)
{
layer_thickness = getSettingInMicrons("layer_height_0");
}
ExtruderTrain* current_extruder_train = storage.meshgroup->getExtruderTrain(current_extruder_planned);
int64_t comb_offset_from_outlines = current_extruder_train->getSettingInMicrons((current_extruder_train->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 2; // TODO: only used when there is no second wall.
int max_nozzle_size = 0;
std::vector<bool> extruders_used = storage.getExtrudersUsed(layer_nr);
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
{
if (extruders_used[extr_nr])
{
max_nozzle_size = std::max(max_nozzle_size, storage.meshgroup->getExtruderTrain(extr_nr)->getSettingInMicrons("machine_nozzle_size"));
}
}
int64_t comb_offset_from_outlines = max_nozzle_size * 2;// TODO: only used when there is no second wall.
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
if (layer_nr == 0)
{
@@ -439,20 +446,21 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
{
return;
}
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int layer_height_0 = getSettingInMicrons("layer_height_0");
int layer_height = getSettingInMicrons("layer_height");
int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
if (int(layer_nr) > max_screen_layer)
{
return;
}
gcode_layer.setIsInside(false);
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
}
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int current_extruder)
@@ -523,7 +531,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, Slic
lines.add(p);
}
}
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config);
}
@@ -569,16 +577,28 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
int wall_reinforcement_line_distance = mesh->getSettingInMicrons("wall_reinforcement_line_distance");
int wall_reinforcement_line_width = mesh->wall_reinforcement_config.getLineWidth();
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
for (unsigned int wall_idx = part.reinforcement_walls.size() - 1; int(wall_idx) >= 0; wall_idx--)
{
ReinforcementWall& reinforcement_wall = part.reinforcement_walls[wall_idx];
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, true);
}
}
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
for (ReinforcementWall& reinforcement_wall : part.reinforcement_walls)
{
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, false);
}
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
}
@@ -592,14 +612,12 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
skin_angle -= 45;
int64_t skin_overlap = infill_overlap;
int64_t skin_overlap = 0;
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
//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"))
{
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons("machine_nozzle_size") * 1);
}
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -618,13 +636,13 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
for(unsigned int n=1; n<part.infill_area.size(); n++)
{
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
Infill infill_comp(infill_pattern, part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
Infill infill_comp(mesh->getSettingAsFillMethod("infill_pattern"), part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
Polygons infill_polygons;
Polygons infill_lines;
infill_comp.generate(infill_polygons, infill_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n]);
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
}
}
}
@@ -647,12 +665,61 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
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->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], mesh->getSettingInMicrons("infill_wipe_dist"));
}
else
{
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0]);
}
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
}
void FffGcodeWriter::processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out)
{
if (wall_reinforcement_line_distance == 0 || (reinforcement_wall.wall_reinforcement_area.size() == 0 && reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0) )
{
return;
}
if (inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
processWallReinforcement_infill(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width);
if (!inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
}
void FffGcodeWriter::processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out)
{
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
for(int inset_number=reinforcement_wall.wall_reinforcement_axtra_walls.size()-1; inset_number>-1; inset_number--)
{
gcode_layer.addPolygonsByOptimizer(reinforcement_wall.wall_reinforcement_axtra_walls[inset_number], &mesh->insetX_config);
}
}
}
void FffGcodeWriter::processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width)
{
if (reinforcement_wall.wall_reinforcement_area.size() == 0)
{
return;
}
Polygons infill_polygons;
Polygons infill_lines;
EFillMethod pattern = mesh->getSettingAsFillMethod("wall_reinforcement_pattern");
Infill infill_comp(pattern, reinforcement_wall.wall_reinforcement_area, 0, false, wall_reinforcement_line_width, wall_reinforcement_line_distance, infill_overlap, infill_angle, false, false);
infill_comp.generate(infill_polygons, infill_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->wall_reinforcement_config);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->wall_reinforcement_config);
sendPolygons(SupportInfillType, layer_nr, infill_lines, wall_reinforcement_line_width);
}
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
@@ -708,10 +775,15 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
}
Polygons* inner_skin_outline = nullptr;
int offset_from_inner_skin_outline = 0;
if (pattern != EFillMethod::CONCENTRIC)
if (pattern == EFillMethod::CONCENTRIC)
{
offset_from_inner_skin_outline = -extrusion_width/2;
}
else
{
for (Polygons& skin_perimeter : skin_part.insets)
{
sendPolygons(SkinType, layer_nr, skin_perimeter, mesh->skin_config.getLineWidth());
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
}
if (skin_part.insets.size() > 0)
@@ -720,13 +792,7 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
offset_from_inner_skin_outline = -extrusion_width/2;
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int line_distance = extrusion_width;
int outline_offset = 0;
Infill infill_comp(EFillMethod::LINES, skin_part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
infill_comp.generate(result_polygons, skin_lines, in_between);
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
}
}
}
@@ -740,30 +806,17 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
}
else
{
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config);
sendPolygons(SkinType, layer_nr, skin_polygons, mesh->skin_config.getLineWidth());
sendPolygons(SkinType, layer_nr, skin_lines, mesh->skin_config.getLineWidth());
}
// handle gaps between perimeters etc.
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
Polygons perimeter_gap_lines;
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int line_distance = extrusion_width;
int outline_offset = 0;
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
}
}
@@ -773,9 +826,9 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
return;
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
|| support_roof_extruder_nr == extruder_nr_before;
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
@@ -788,24 +841,24 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
{
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
{
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
}
else
{
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
}
}
else
{
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
}
}
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
{
if (!storage.support.generated
|| layer_nr > storage.support.layer_nr_max_filled_layer
@@ -813,16 +866,17 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
{
return;
}
int support_line_distance = getSettingInMicrons("support_line_distance");
int extrusion_width = storage.support_config.getLineWidth();
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
@@ -839,25 +893,21 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
{
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
double infill_overlap = 0; // support infill should not be expanded outward
int offset_from_outline = 0;
bool remove_overlapping_perimeters = false;
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
{
Polygons boundary;
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
offset_from_outline = -extrusion_width;
infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
}
Infill infill_comp(support_pattern, island, offset_from_outline, remove_overlapping_perimeters, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
Infill infill_comp(support_pattern, island, offset_from_outline, false, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
Polygons support_polygons;
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines, nullptr);
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addPolygonsByOptimizer(island, &storage.support_config);
sendPolygons(SupportType, layer_nr, island, storage.support_config.getLineWidth());
}
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);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
}
}
@@ -889,7 +939,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
{
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
}
double infill_overlap = 0; // the roofs should never be expanded outwards
double infill_overlap = 0;
int outline_offset = 0;
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
@@ -898,7 +948,9 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
infill_comp.generate(support_polygons, support_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config);
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
}
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
@@ -935,15 +987,22 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
}
void FffGcodeWriter::finalize()
{
if (CommandSocket::isInstantiated())
if (command_socket)
{
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
CommandSocket::getInstance()->sendGCodePrefix(prefix);
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
}
command_socket->sendGCodePrefix(prefix.str());
}
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
+54 -3
Ver Arquivo
@@ -17,7 +17,6 @@
#include "commandSocket.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "PrintFeature.h"
#include "LayerPlanBuffer.h"
@@ -43,6 +42,7 @@ private:
LayerPlanBuffer layer_plan_buffer;
GCodeExport gcode;
CommandSocket* command_socket;
std::ofstream output_file;
/*!
@@ -59,18 +59,30 @@ private:
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, layer_plan_buffer(this, gcode)
, layer_plan_buffer(this, command_socket, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // TODO: make configurable
{
meshgroup_number = 1;
max_object_height = 0;
command_socket = NULL;
}
void resetFileNumber()
{
meshgroup_number = 1;
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
}
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (command_socket)
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
}
bool setTargetFile(const char* filename)
{
output_file.open(filename);
@@ -112,6 +124,8 @@ private:
*/
void initConfigs(SliceDataStorage& storage);
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
/*!
* Set temperatures and perform initial priming.
* \param storage Input: where the slice data is stored.
@@ -228,6 +242,43 @@ private:
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Add wall reinforcement 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 gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
* \param inside_out Whether to print from inside outward or other way around
*/
void processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the inner extra walls of the wall reinforcement 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 gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the infill of the wall reinforcement 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 gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
@@ -266,7 +317,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 addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support roofs to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
+133 -26
Ver Arquivo
@@ -16,7 +16,6 @@
#include "raft.h"
#include "debug.h"
#include "Progress.h"
#include "PrintFeature.h"
namespace cura
{
@@ -24,6 +23,9 @@ namespace cura
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (commandSocket)
commandSocket->beginSendSlicedObject();
if (!sliceModel(meshgroup, timeKeeper, storage))
{
return false;
@@ -36,7 +38,7 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
@@ -63,7 +65,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
return true; //This is NOT an error state!
}
@@ -77,18 +79,18 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
@@ -124,16 +126,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (CommandSocket::isInstantiated())
if (commandSocket)
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
}
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
return true;
}
@@ -150,7 +152,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processInsets(storage, layer_number);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
}
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
@@ -161,24 +163,21 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
AreaSupport::generateSupportAreas(storage, total_layers);
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
}
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
int mesh_max_bottom_layer_count = 0;
if (getSettingBoolean("magic_spiralize"))
{
@@ -193,12 +192,17 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processSkinsAndInfill(storage, layer_number);
}
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
processWallReinforcement(storage, layer_number);
}
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
for(SliceMeshStorage& mesh : storage.meshes)
{
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
}
@@ -218,6 +222,17 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processFuzzyWalls(mesh);
}
else if (mesh.getSettingAsCount("wall_line_count") > 0)
{ // only send polygon data
for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
{
SliceLayer* layer = &mesh.layers[layer_nr];
for(SliceLayerPart& part : layer->parts)
{
sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
}
@@ -236,6 +251,16 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() > 0)
{
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin
for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++)
sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x);
}
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -248,6 +273,72 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
}
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
void FffPolygonGenerator::processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
{ // generate infill area
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
continue;
}
if (mesh.getSettingInMicrons("wall_reinforcement_thickness") == 0.0 && mesh.getSettingAsCount("wall_reinforcement_line_count") == 0)
{
return;
}
int inset_count = mesh.getSettingAsCount("wall_reinforcement_line_count");
int wall_line_width = mesh.getSettingInMicrons("wall_line_width_x");
SliceLayer* layer = &mesh.layers[layer_nr];
for (SliceLayerPart& part : layer->parts)
{
if (part.infill_area.size() == 0)
{
continue;
}
int wall_reinforcement_count = mesh.getSettingAsCount("wall_reinforcement_count");
part.reinforcement_walls.reserve(wall_reinforcement_count);
for (unsigned int wall_idx = 0; int(wall_idx) < wall_reinforcement_count; wall_idx++)
{
part.reinforcement_walls.emplace_back();
ReinforcementWall& reinforcement_wall = part.reinforcement_walls.back();
Polygons outer_wall_reinforcement_edge = part.infill_area[0].offset(-mesh.getSettingInMicrons("wall_reinforcement_thickness"));
reinforcement_wall.wall_reinforcement_area = part.infill_area[0].difference(outer_wall_reinforcement_edge);
if (mesh.getSettingAsCount("wall_reinforcement_line_count") > 0)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(outer_wall_reinforcement_edge.offset(-wall_line_width/2));
}
else
{
part.infill_area[0] = outer_wall_reinforcement_edge.offset(-wall_line_width/2);
}
// generate reinforcement wall extra walls
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0)
{
continue;
}
generateWallReinforcementWallExtraWalls(&part, reinforcement_wall, wall_line_width, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
part.infill_area[0] = reinforcement_wall.wall_reinforcement_axtra_walls.back().offset(-wall_line_width/2); // update the infill area to one reinforcement wall insetted (updated each time a reinforcement wall is generated)
}
if (part.insets.size() > 0)
{
for(Polygons& polys : reinforcement_wall.wall_reinforcement_axtra_walls)
sendPolygons(SupportType, layer_nr, polys, wall_line_width);
}
}
}
}
@@ -302,17 +393,17 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
int innermost_wall_extrusion_width = mesh.getSettingInMicrons("wall_line_width_x");
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
{
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
{
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
@@ -322,6 +413,20 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
}
bool frontend_can_show_polygon_as_filled_polygon = false;
if (frontend_can_show_polygon_as_filled_polygon)
{
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
}
}
}
}
}
@@ -401,6 +506,7 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
}
@@ -436,7 +542,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
{
int r = rand() % (fuzziness * 2) - fuzziness;
Point perp_to_p0p1 = turn90CCW(p0p1);
Point perp_to_p0p1 = crossZ(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
result.add(pa);
@@ -461,6 +567,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
}
}
skin = results;
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
+33 -2
Ver Arquivo
@@ -9,7 +9,6 @@
#include "settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
#include "PrintFeature.h"
namespace cura
{
@@ -25,14 +24,26 @@ namespace cura
*/
class FffPolygonGenerator : public SettingsMessenger, NoCopy
{
private:
CommandSocket* commandSocket;
public:
/*!
* Basic constructor
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
*/
FffPolygonGenerator(SettingsBase* settings_)
: SettingsMessenger(settings_)
, commandSocket(nullptr)
{
}
/*!
* Set the FffAreaGenerator::commandSocket
*/
void setCommandSocket(CommandSocket* socket)
{
commandSocket = socket;
}
/*!
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
@@ -44,6 +55,17 @@ public:
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* Send polygons over the command socket, if there is one.
* \param type The type of polygon to send
* \param layer_nr The layer number at which the polygons occur
* \param polygons The polygons to be sent
*/
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (commandSocket)
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
}
/*!
* Slice the \p object and store the outlines in the \p storage.
@@ -79,6 +101,15 @@ private:
*/
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Generate the wall reinforcement extra wall polygons and its infill area which form the reinforcement.
*
* Also redefines the infill area;
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
void processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
+6 -11
Ver Arquivo
@@ -11,7 +11,6 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
std::stringstream sstream;
if (first_meshgroup)
{
sstream << getAllLocalSettingsString(); // global settings
sstream << " -g";
}
else
@@ -67,7 +66,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
if (meshgroup->meshes.empty())
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
@@ -79,11 +78,11 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
log("starting Neith Weaver...\n");
Weaver w(this);
w.weave(meshgroup);
w.weave(meshgroup, command_socket);
log("starting Neith Gcode generation...\n");
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
gcoder.writeGCode();
gcoder.writeGCode(command_socket);
log("finished Neith Gcode generation...\n");
} else
@@ -94,17 +93,13 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
return false;
}
gcode_writer.setCommandSocket(command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
gcode_writer.writeGCode(storage, time_keeper);
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->sendLayerData();
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
+9
Ver Arquivo
@@ -26,6 +26,7 @@ private:
, gcode_writer(this)
, first_meshgroup(true)
{
command_socket = NULL;
}
public:
static FffProcessor* getInstance()
@@ -36,6 +37,7 @@ public:
private:
FffPolygonGenerator polygon_generator;
FffGcodeWriter gcode_writer;
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
bool first_meshgroup;
@@ -53,6 +55,13 @@ public:
gcode_writer.resetFileNumber();
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
gcode_writer.setCommandSocket(socket);
polygon_generator.setCommandSocket(socket);
}
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
+10 -6
Ver Arquivo
@@ -14,14 +14,12 @@ void LayerPlanBuffer::flush()
{
insertPreheatCommands(); // insert preheat commands of the very last layer
}
while (!buffer.empty())
for (GCodePlanner& layer_plan : buffer)
{
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->flushGcode();
buffer.pop_front();
layer_plan.writeGCode(gcode, getSettingBoolean("cool_lift_head"), layer_plan.getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
}
}
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
@@ -220,4 +218,10 @@ void LayerPlanBuffer::insertPreheatCommands()
}
}
} // namespace cura
+6 -3
Ver Arquivo
@@ -17,6 +17,8 @@ namespace cura
class LayerPlanBuffer : SettingsMessenger
{
CommandSocket* command_socket;
GCodeExport& gcode;
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
@@ -27,8 +29,9 @@ class LayerPlanBuffer : SettingsMessenger
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
LayerPlanBuffer(SettingsBaseVirtual* settings, CommandSocket* command_socket, GCodeExport& gcode)
: SettingsMessenger(settings)
, command_socket(command_socket)
, gcode(gcode)
{ }
@@ -52,8 +55,8 @@ public:
if (buffer.size() > buffer_size)
{
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->flushGcode();
if (command_socket)
command_socket->sendGCodeLayer();
buffer.pop_front();
}
return buffer.back();
+40 -115
Ver Arquivo
@@ -2,8 +2,6 @@
#include <algorithm> // min
#include "utils/linearAlg2D.h"
namespace cura
{
@@ -14,7 +12,6 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
double speed_mod = old_line_width / new_line_width_mm;
double extrusion_mod = new_line_width_mm / old_line_width;
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
@@ -54,136 +51,64 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
return false;
};
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
{
int64_t max_line_width = nozzle_size * 3 / 2;
unsigned int idx = path_idx_first_move;
if (idx + 3 > paths.size()-1)
{
return false;
}
if ( paths[idx+0].config != &travelConfig // must be travel
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|| paths[idx+1].config == &travelConfig // must be extrusion
// || paths[idx+2].points.size() > 1 // travel must be direct
|| paths[idx+2].config != &travelConfig // must be travel
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|| paths[idx+3].config == &travelConfig // must be extrusion
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
)
{
return false;
}
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
return false;
}
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
{ // both extrusion moves must be of lines space filling type!
return false;
}
int64_t line_width = paths[idx+1].config->getLineWidth();
if (idx + 3 > paths.size()-1) return false;
if (paths[idx+0].config != &travelConfig) return false;
if (paths[idx+1].points.size() > 1) return false;
if (paths[idx+1].config == &travelConfig) return false;
// if (paths[idx+2].points.size() > 1) return false;
if (paths[idx+2].config != &travelConfig) return false;
if (paths[idx+3].points.size() > 1) return false;
if (paths[idx+3].config == &travelConfig) return false;
Point& a = paths[idx+0].points.back(); // first extruded line from
Point& b = paths[idx+1].points.back(); // first extruded line to
Point& c = paths[idx+2].points.back(); // second extruded line from
Point& d = paths[idx+3].points.back(); // second extruded line to
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
}
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
use_second_middle_as_first = false;
int64_t max_line_width = nozzle_size * 3 / 2;
Point ab = b - a;
Point cd = d - c;
if (b == c)
{
return false; // the line segments are connected!
}
int64_t ab_size = vSize(ab);
int64_t cd_size = vSize(cd);
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
{
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
}
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
{
return false; // extrusion moves not in the same or opposite diraction
}
// make lines in the same direction by flipping one
if (prod < 0)
{
ab = ab * -1;
}
else if (prod == 0)
{
return false; // lines are orthogonal!
}
else if (b == d || a == c)
{
return false; // the line segments are connected!
}
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
return false; // extrusion moves not in the same or opposite diraction
if (prod < 0) { ab = ab * -1; }
Point infill_vector = (cd + ab) / 2;
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
first_middle = (use_second_middle_as_first)?
second_middle :
(a + b) / 2;
second_middle = (c + d) / 2;
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
Point dir_vector_perp = crossZ(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0)
{
return false;
}
if (dir_vector_perp_length > 5 * nozzle_size)
{
return false; // infill lines too far apart
}
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
// compute the resulting line width
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (resulting_line_width > max_line_width)
{
return false; // combined lines would be too wide
}
if (resulting_line_width == 0)
{
return false; // dot is zero, so lines are in each others extension, not next to eachother
}
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
Point ac = c - first_middle;
Point infill_vector_perp = turn90CCW(infill_vector);
int64_t perp_proj = dot(ac, infill_vector_perp);
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
{
return false; // lines are too far apart or too close together
if (dir_vector_perp_length == 0) return false;
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (line_width > max_line_width) return false; // combined lines would be too wide
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
{ // check whether the two lines are adjacent
Point ca = first_middle - c;
double ca_size = vSizeMM(ca);
double cd_size = vSizeMM(cd);
double prod = INT2MM(dot(ca, cd));
double fraction = prod / ( ca_size * cd_size );
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
}
// check whether the two line segments are adjacent.
// full infill in a narrow area might result in line segments with arbitrary distance between them
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
{
return false;
}
return true;
};
+7 -40
Ver Arquivo
@@ -12,42 +12,24 @@ class MergeInfillLines
{
// void merge(Point& from, Point& p0, Point& p1);
GCodeExport& gcode; //!< Where to write the combined line to
int layer_nr; //!< The current layer number
std::vector<GCodePath>& paths; //!< The paths currently under consideration
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
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
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
*
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
*
* \param a first from
* \param b first to
* \param c second from
* \param d second to
* \param line_width The line width of the moves
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
/*!
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
*
@@ -61,8 +43,8 @@ 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, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
/*!
* Check for lots of small moves and combine them into one large line.
@@ -78,21 +60,6 @@ public:
*/
bool mergeInfillLines(double speed, unsigned int& path_idx);
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
}
};
}//namespace cura
+9 -42
Ver Arquivo
@@ -64,38 +64,26 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
fseek(f, 0L, SEEK_END);
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
rewind(f); //Seek back to start.
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return false;
}
uint32_t reported_face_count;
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return false;
}
if (reported_face_count != face_count)
{
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
mesh->faces.reserve(face_count);
mesh->vertices.reserve(face_count);
for (unsigned int i = 0; i < face_count; i++)
mesh->faces.reserve(faceCount);
mesh->vertices.reserve(faceCount);
for(unsigned int i=0;i<faceCount;i++)
{
if (fread(buffer, 50, 1, f) != 1)
{
@@ -117,31 +105,10 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
if (f == nullptr)
{
return false;
}
//Skip any whitespace at the beginning of the file.
unsigned long long num_whitespace = 0; //Number of whitespace characters.
unsigned char whitespace;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
while(isspace(whitespace))
{
num_whitespace++;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
}
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
char buffer[6];
if (f == nullptr)
return false;
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
+15 -27
Ver Arquivo
@@ -5,7 +5,6 @@
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "infill.h"
#include "PrintFeature.h"
namespace cura
{
@@ -23,7 +22,7 @@ void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>
for (int extr = 0; extr < extruder_count; extr++)
{
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "SUPPORT");// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
@@ -56,14 +55,13 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{ // compute max_object_height_per_extruder
for (SliceMeshStorage& mesh : storage.meshes)
{
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_object_height_per_extruder[extr_nr] =
std::max( max_object_height_per_extruder[extr_nr]
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
, mesh.layer_nr_max_filled_layer );
}
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_extruder_nr] =
std::max( max_object_height_per_extruder[support_extruder_nr]
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_infill_extruder_nr] =
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
max_object_height_per_extruder[support_roof_extruder_nr] =
@@ -170,7 +168,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
double infill_overlap = 15; // so that it can't be zero
generateGroundpoly(storage);
@@ -181,22 +179,14 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
std::vector<Polygons>& patterns = patterns_per_extruder.back();
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int outline_offset = -line_width/2;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
infill_comp.generate(result_polygons, result_lines, in_between);
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
}
}
}
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
@@ -218,10 +208,10 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
{
wipe = false;
}
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
}
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
@@ -237,14 +227,12 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
gcodeLayer.addLinesByOptimizer(pattern, &config);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
}
if (command_socket)
command_socket->sendPolygons(SupportType, layer_nr, pattern, config.getLineWidth());
if (wipe)
{ //Make sure we wipe the old extruder on the prime tower.
@@ -252,7 +240,7 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
}
}
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
+3 -3
Ver Arquivo
@@ -54,9 +54,9 @@ public:
PrimeTower();
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
};
+13 -12
Ver Arquivo
@@ -4,23 +4,24 @@
namespace cura
{
enum class PrintFeatureType
{
NoneType, // unused, but libArcus depends on it
OuterWall,
InnerWall,
Skin,
Support,
Skirt,
Infill,
SupportInfill,
MoveCombing,
MoveRetraction
enum class EPrintFeature : unsigned int // unused!!
{ // TODO: use in gcodePathConfigs ?
OUTER_WALL,
INNER_WALLS,
INFILL,
SKIN,
HELPERS,
UNCLASSIFIED,
ENUM_COUNT
};
} // namespace cura
#endif // PRINT_FEATURE
+6 -6
Ver Arquivo
@@ -64,22 +64,22 @@ void Progress::init()
total_timing = accumulated_time;
}
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
{
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
if (CommandSocket::getInstance())
if (command_socket)
{
CommandSocket::getInstance()->sendProgress(percentage);
command_socket->sendProgress(percentage);
}
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
}
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
{
if (CommandSocket::getInstance())
if (command_socket)
{
CommandSocket::getInstance()->sendProgressStage(stage);
command_socket->sendProgressStage(stage);
}
if (time_keeper)
+5 -3
Ver Arquivo
@@ -52,20 +52,22 @@ private:
public:
static void init(); //!< Initialize some values needed in a fast computation of the progress
/*!
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
*
* \param stage The current stage of processing
* \param progress_in_stage Any number giving the progress within the stage
* \param progress_in_stage_max The maximal value of \p progress_in_stage
* \param commandSocket The command socket over which to communicate the progress.
*/
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
/*!
* Message the progress stage over the command socket.
*
* \param stage The current stage
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
* \param commandSocket The command socket over which to communicate (optional)
*/
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
};
-25
Ver Arquivo
@@ -1,25 +0,0 @@
#ifndef SPACE_FILL_TYPE
#define SPACE_FILL_TYPE
namespace cura
{
/*!
* Enum class enumerating the strategies with which an area can be occupied with filament
*
* The walls/perimeters are Polygons
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
* Grid, Triangles and lines infill is Lines
*/
enum class SpaceFillType
{
None,
Polygons,
PolyLines,
Lines
};
} // namespace cura
#endif // SPACE_FILL_TYPE
+9 -10
Ver Arquivo
@@ -6,12 +6,11 @@
#include "Progress.h"
#include "weaveDataStorage.h"
#include "PrintFeature.h"
namespace cura
{
void Weaver::weave(MeshGroup* meshgroup)
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{
wireFrame.meshgroup = meshgroup;
@@ -53,8 +52,8 @@ void Weaver::weave(MeshGroup* meshgroup)
for (cura::Slicer* slicer : slicerList)
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
if (slicerList.empty()) //Wait, there is nothing to slice.
{
@@ -71,10 +70,10 @@ void Weaver::weave(MeshGroup* meshgroup)
else
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
@@ -85,8 +84,8 @@ void Weaver::weave(MeshGroup* meshgroup)
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
if (chainified.size() > 0)
{
@@ -108,10 +107,10 @@ void Weaver::weave(MeshGroup* meshgroup)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
+2 -1
Ver Arquivo
@@ -61,8 +61,9 @@ public:
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
*
* \param objects The objects for which to create a wireframe print
* \param commandSocket the commandSocket
*/
void weave(MeshGroup* objects);
void weave(MeshGroup* objects, CommandSocket* commandSocket);
private:
+22 -16
Ver Arquivo
@@ -12,15 +12,15 @@ namespace cura
{
void Wireframe2gcode::writeGCode()
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
{
gcode.preSetup(wireFrame.meshgroup);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
if (commandSocket)
commandSocket->beginGCode();
processStartingCode();
processStartingCode(commandSocket);
int maxObjectHeight;
if (wireFrame.layers.empty())
@@ -32,7 +32,7 @@ void Wireframe2gcode::writeGCode()
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt();
processSkirt(commandSocket);
unsigned int total_layers = wireFrame.layers.size();
@@ -76,10 +76,10 @@ void Wireframe2gcode::writeGCode()
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_nr];
@@ -166,6 +166,12 @@ void Wireframe2gcode::writeGCode()
gcode.writeFanCommand(0);
finalize();
if (commandSocket)
{
commandSocket->sendGCodeLayer();
commandSocket->endSendSlicedObject();
}
}
@@ -544,13 +550,13 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode()
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
if (!CommandSocket::isInstantiated())
if (!command_socket)
{
gcode.writeCode(gcode.getFileHeader().c_str());
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
}
}
else
@@ -589,7 +595,7 @@ void Wireframe2gcode::processStartingCode()
}
void Wireframe2gcode::processSkirt()
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
{
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
{
@@ -600,14 +606,14 @@ void Wireframe2gcode::processSkirt()
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
{
unsigned int poly_idx = order.polyOrder[poly_order_idx];
PolygonRef poly = skirt[poly_idx];
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
PolygonRef poly = skirt[actual_poly_idx];
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
}
}
+3 -3
Ver Arquivo
@@ -71,7 +71,7 @@ public:
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
void writeGCode();
void writeGCode(CommandSocket* commandSocket);
private:
@@ -80,12 +80,12 @@ private:
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
*/
void processStartingCode();
void processStartingCode(CommandSocket* command_socket);
/*!
* Lay down a skirt
*/
void processSkirt();
void processSkirt(CommandSocket* commandSocket);
/*!
* End gcode: nozzle temp down
-1
Ver Arquivo
@@ -385,7 +385,6 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
}
}
}
optimized_comb_path.push_back(comb_path.back());
return true;
}
+150 -160
Ver Arquivo
@@ -6,9 +6,9 @@
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#include <Arcus/SocketListener.h>
#include <Arcus/Error.h>
#endif
#include <string> // stoi
@@ -25,50 +25,32 @@ namespace cura {
#define FLOATS_PER_VECTOR 3
#define VECTORS_PER_FACE 3
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
class Listener : public Arcus::SocketListener
{
public:
void stateChanged(Arcus::SocketState::SocketState newState) override
{
}
void messageReceived() override
{
}
void error(const Arcus::Error & error) override
{
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
{
log("%s\n", error.toString().c_str());
}
else
{
logError("%s\n", error.toString().c_str());
}
}
};
#ifdef ARCUS
class CommandSocket::Private
{
public:
Private()
: socket(nullptr)
, object_count(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
cura::proto::Layer* getLayerById(int id);
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
// Message that holds a list of sliced objects
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
// Message that holds the currently sliced object (to be added to sliced_object_list)
cura::proto::SlicedObject* current_sliced_object;
// Number of sliced objects for this sliced object list
int sliced_objects;
@@ -76,70 +58,74 @@ public:
// Used for incrementing the current layer in one at a time mode
int current_layer_count;
int current_layer_offset;
// Ids of the sliced objects
std::vector<int64_t> object_ids;
std::string temp_gcode_file;
std::ostringstream gcode_output_stream;
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
};
#endif
CommandSocket::CommandSocket()
: private_data(new Private)
#ifdef ARCUS
: d(new Private)
#endif
{
#ifdef ARCUS
FffProcessor::getInstance()->setCommandSocket(this);
#endif
}
CommandSocket* CommandSocket::getInstance()
{
return instance;
}
void CommandSocket::instantiate()
{
instance = new CommandSocket();
}
bool CommandSocket::isInstantiated()
{
return instance != nullptr;
}
void CommandSocket::connect(const std::string& ip, int port)
{
private_data->socket = new Arcus::Socket();
private_data->socket->addListener(new Listener());
#ifdef ARCUS
d->socket = new Arcus::Socket();
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
private_data->socket->connect(ip, port);
log("Connecting to %s:%i\n", ip.c_str(), port);
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
log("Connected to %s:%i\n", ip.c_str(), port);
d->socket->connect(ip, port);
bool slice_another_time = true;
// Start & continue listening as long as socket is not closed and there is no error.
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
{
//If there is an object to slice, do so.
if(d->objects_to_slice.size())
{
FffProcessor::getInstance()->resetFileNumber();
for(auto object : d->objects_to_slice)
{
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
d->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
sendGCodeLayer();
sendPrintTime();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//d->processor->processModel(d->object_to_slice.get());
//d->object_to_slice.reset();
//d->processor->resetFileNumber();
//sendPrintTime();
}
// Actually start handling messages.
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
Arcus::MessagePtr message = d->socket->takeNextMessage();
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if(setting_list)
{
@@ -156,57 +142,33 @@ void CommandSocket::connect(const std::string& ip, int port)
if(slice)
{
// Reset object counts
private_data->object_count = 0;
d->object_count = 0;
d->object_ids.clear();
for(auto object : slice->object_lists())
{
handleObjectList(&object);
}
}
//If there is an object to slice, do so.
if(private_data->objects_to_slice.size())
{
FffProcessor::getInstance()->resetFileNumber();
for(auto object : private_data->objects_to_slice)
{
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
private_data->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
flushGcode();
sendPrintTime();
sendFinishedSlicing();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//private_data->processor->processModel(private_data->object_to_slice.get());
//private_data->object_to_slice.reset();
//private_data->processor->resetFileNumber();
//sendPrintTime();
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if(!d->socket->errorString().empty())
{
logError("%s\n", d->socket->errorString().data());
d->socket->clearError();
}
}
log("Closing connection\n");
private_data->socket->close();
#endif
}
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
{
if(list->objects_size() <= 0)
{
return;
}
FMatrix3x3 matrix;
//private_data->object_count = 0;
//private_data->object_ids.clear();
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
//d->object_count = 0;
//d->object_ids.clear();
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = d->objects_to_slice.back().get();
for(auto setting : list->settings())
{
@@ -220,14 +182,6 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
for(auto object : list->objects())
{
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
if(face_count <= 0)
{
logWarning("Got an empty mesh, ignoring it!");
continue;
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
for(auto setting : object.settings())
@@ -243,6 +197,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = meshgroup->meshes.back();
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
for(int i = 0; i < face_count; ++i)
{
//TODO: Apply matrix
@@ -269,10 +225,11 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
mesh.setSetting(setting.name(), setting.value());
}
d->object_ids.push_back(object.id());
mesh.finish();
}
private_data->object_count++;
d->object_count++;
meshgroup->finalize();
}
@@ -283,39 +240,54 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
#ifdef ARCUS
if(!d->current_sliced_object)
{
return;
}
cura::proto::Layer* layer = d->getLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
#endif
}
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
#ifdef ARCUS
if(!d->current_sliced_object)
return;
if (polygons.size() == 0)
return;
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
cura::proto::Layer* layer = d->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
cura::proto::Polygon* p = proto_layer->add_polygons();
cura::proto::Polygon* p = layer->add_polygons();
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
std::string polydata;
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
p->set_points(polydata);
p->set_line_width(line_width);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::Progress>();
amount /= private_data->object_count;
amount += private_data->sliced_objects * (1. / private_data->object_count);
amount /= d->object_count;
amount += d->sliced_objects * (1. / d->object_count);
message->set_amount(amount);
private_data->socket->sendMessage(message);
d->socket->sendMessage(message);
#endif
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
@@ -325,10 +297,12 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
void CommandSocket::sendPrintTime()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
private_data->socket->sendMessage(message);
d->socket->sendMessage(message);
#endif
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -340,74 +314,90 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
// socket.sendFloat32(print_time);
}
void CommandSocket::sendLayerData()
void CommandSocket::beginSendSlicedObject()
{
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
if(private_data->sliced_objects >= private_data->object_count)
#ifdef ARCUS
if(!d->sliced_object_list)
{
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
private_data->sliced_objects = 0;
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_layers.clear();
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
}
d->current_sliced_object = d->sliced_object_list->add_objects();
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
#endif
}
void CommandSocket::sendFinishedSlicing()
void CommandSocket::endSendSlicedObject()
{
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
#ifdef ARCUS
d->sliced_objects++;
d->current_layer_offset = d->current_layer_count;
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
std::cout << "current layer count" << d->current_layer_count << std::endl;
std::cout << "current layer offset" << d->current_layer_offset << std::endl;
if(d->sliced_objects >= d->object_count)
{
d->socket->sendMessage(d->sliced_object_list);
d->sliced_objects = 0;
d->current_layer_count = 0;
d->current_layer_offset = 0;
d->sliced_object_list.reset();
d->current_sliced_object = nullptr;
}
#endif
}
void CommandSocket::beginGCode()
{
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
#endif
}
void CommandSocket::flushGcode()
void CommandSocket::sendGCodeLayer()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodeLayer>();
message->set_data(private_data->gcode_output_stream.str());
private_data->socket->sendMessage(message);
message->set_id(d->object_ids[0]);
message->set_data(d->gcode_output_stream.str());
d->socket->sendMessage(message);
private_data->gcode_output_stream.str("");
d->gcode_output_stream.str("");
#endif
}
void CommandSocket::sendGCodePrefix(std::string prefix)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
message->set_data(prefix);
private_data->socket->sendMessage(message);
d->socket->sendMessage(message);
#endif
}
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
#ifdef ARCUS
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
auto itr = sliced_layers.find(id);
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
std::shared_ptr<cura::proto::Layer> layer;
if(itr != sliced_layers.end())
cura::proto::Layer* layer = nullptr;
if(itr != current_sliced_object->mutable_layers()->end())
{
layer = itr->second;
layer = &(*itr);
}
else
{
layer = std::make_shared<cura::proto::Layer>();
layer = current_sliced_object->add_layers();
layer->set_id(id);
current_layer_count++;
sliced_layers[id] = layer;
}
return layer;
}
#endif
}//namespace cura
+17 -44
Ver Arquivo
@@ -5,29 +5,20 @@
#include "utils/polygon.h"
#include "settings.h"
#include "Progress.h"
#include "PrintFeature.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura {
namespace cura
{
class CommandSocket
{
private:
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
public:
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
static void instantiate(); //!< Instantiate the CommandSocket.
static bool isInstantiated(); //!< Check whether the singleton is instantiated
CommandSocket();
/*!
* Connect with the GUI
* This creates and initialises the arcus socket and then continues listening for messages.
@@ -35,7 +26,8 @@ public:
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
@@ -47,22 +39,18 @@ public:
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
#endif
/*!
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
* Does nothing at the moment
*/
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the engine. This is used for the layerview in the GUI
*/
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
*/
@@ -82,34 +70,19 @@ public:
* Does nothing at the moment
*/
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
/*!
* Send the sliced layer data to the GUI.
*
* The GUI may use this to visualise the g-code, so that the user can
* inspect the result of slicing.
*/
void sendLayerData();
/*!
* \brief Sends a message to indicate that all the slicing is done.
*
* This should indicate that no more data (g-code, prefix/postfix, metadata
* or otherwise) should be sent any more regarding the latest slice job.
*/
void sendFinishedSlicing();
void beginSendSlicedObject();
void endSendSlicedObject();
void beginGCode();
/*!
* Flush the gcode in gcode_output_stream into a message queued in the socket.
*/
void flushGcode();
void sendGCodeLayer();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> private_data;
const std::unique_ptr<Private> d;
#endif
};
}//namespace cura
+68 -147
Ver Arquivo
@@ -5,13 +5,13 @@
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "PrintFeature.h"
namespace cura {
GCodeExport::GCodeExport()
: output_stream(&std::cout)
, currentPosition(0,0,MM2INT(20))
, commandSocket(nullptr)
, layer_nr(0)
{
current_e_value = 0;
@@ -29,24 +29,8 @@ GCodeExport::~GCodeExport()
{
}
std::string GCodeExport::getFileHeader(double print_time, int filament_used_0, int filament_used_1)
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";TIME:" << int(print_time) << new_line;
if (flavor == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << int(filament_used_0) << new_line;
prefix << ";MATERIAL2:" << int(filament_used_1) << new_line;
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(1))) << new_line; // TODO: the second nozzle size isn't always initiated!
}
return prefix.str();
}
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
commandSocket = commandSocket_;
layer_nr = layer_nr_;
}
@@ -56,11 +40,6 @@ void GCodeExport::setOutputStream(std::ostream* stream)
*output_stream << std::fixed;
}
int GCodeExport::getNozzleSize(int extruder_idx)
{
return extruder_attr[extruder_idx].nozzle_size;
}
Point GCodeExport::getExtruderOffset(int id)
{
return extruder_attr[id].nozzle_offset;
@@ -155,42 +134,6 @@ double GCodeExport::getCurrentExtrudedVolume()
}
}
double GCodeExport::eToMm(double e)
{
if (is_volumatric)
{
return e / extruder_attr[current_extruder].filament_area;
}
else
{
return e;
}
}
double GCodeExport::mm3ToE(double mm3)
{
if (is_volumatric)
{
return mm3;
}
else
{
return mm3 / extruder_attr[current_extruder].filament_area;
}
}
double GCodeExport::mmToE(double mm)
{
if (is_volumatric)
{
return mm * extruder_attr[current_extruder].filament_area;
}
else
{
return mm;
}
}
double GCodeExport::getTotalFilamentUsed(int e)
{
@@ -234,68 +177,33 @@ void GCodeExport::writeComment(std::string comment)
*output_stream << comment[i];
}
}
*output_stream << new_line;
*output_stream << "\n";
}
void GCodeExport::writeTypeComment(const char* type)
{
*output_stream << ";TYPE:" << type << new_line;
*output_stream << ";TYPE:" << type << "\n";
}
void GCodeExport::writeTypeComment(PrintFeatureType type)
{
switch (type)
{
case PrintFeatureType::OuterWall:
*output_stream << ";TYPE:WALL-OUTER" << new_line;
break;
case PrintFeatureType::InnerWall:
*output_stream << ";TYPE:WALL-INNER" << new_line;
break;
case PrintFeatureType::Skin:
*output_stream << ";TYPE:SKIN" << new_line;
break;
case PrintFeatureType::Support:
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::Skirt:
*output_stream << ";TYPE:SKIRT" << new_line;
break;
case PrintFeatureType::Infill:
*output_stream << ";TYPE:FILL" << new_line;
break;
case PrintFeatureType::SupportInfill:
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::MoveCombing:
case PrintFeatureType::MoveRetraction:
default:
// do nothing
break;
}
}
void GCodeExport::writeLayerComment(int layer_nr)
{
*output_stream << ";LAYER:" << layer_nr << new_line;
*output_stream << ";LAYER:" << layer_nr << "\n";
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << new_line;
*output_stream << line << "\n";
}
void GCodeExport::resetExtrusionValue()
{
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
double current_extruded_volume = getCurrentExtrudedVolume();
extruder_attr[current_extruder].totalFilament += current_extruded_volume;
for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
@@ -309,7 +217,7 @@ void GCodeExport::resetExtrusionValue()
void GCodeExport::writeDelay(double timeAmount)
{
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
estimateCalculator.addTime(timeAmount);
}
@@ -325,7 +233,11 @@ void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
Point gcode_pos = getGcodePos(x,y, current_extruder);
@@ -342,11 +254,11 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
@@ -363,17 +275,17 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!extruder_attr[current_extruder].retraction_e_amount_current)
{
*output_stream << "M103" << new_line;
*output_stream << "M103\r\n";
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
}
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
}
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
@@ -396,7 +308,11 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
return;
}
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
Point gcode_pos = getGcodePos(x,y, current_extruder);
@@ -405,30 +321,30 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
isZHopped = 0;
}
double prime_volume = extruder_attr[current_extruder].prime_volume;
current_e_value += mm3ToE(prime_volume);
current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
if (firmware_retract)
{ // note that BFB is handled differently
*output_stream << "G11" << new_line;
*output_stream << "G11\n";
//Assume default UM2 retraction settings.
if (prime_volume > 0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0);
}
else
{
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
}
if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
{
@@ -438,9 +354,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
}
else if (prime_volume > 0.0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
current_e_value += prime_volume;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
}
extruder_attr[current_extruder].prime_volume = 0.0;
current_e_value += extrusion_per_mm * diff.vSizeMM();
@@ -449,16 +366,16 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
else
{
*output_stream << "G0";
if (CommandSocket::isInstantiated())
if (commandSocket)
{
// we should send this travel as a non-retraction move
cura::Polygons travelPoly;
PolygonRef travel = travelPoly.newPoly();
travel.add(Point(currentPosition.x, currentPosition.y));
travel.add(Point(x, y));
CommandSocket::getInstance()->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
commandSocket->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
}
if (currentSpeed != speed)
@@ -474,10 +391,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
*output_stream << " Z" << INT2MM(z + isZHopped);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
*output_stream << new_line;
*output_stream << "\n";
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
@@ -486,7 +403,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
return;
}
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
{
return;
}
@@ -498,7 +415,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
while (int(extruded_volume_at_previous_n_retractions.size()) >= config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
{
// extruder switch could have introduced data which falls outside the retraction window
// also the retraction_count_max could have changed between the last retraction and this
@@ -508,13 +425,13 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
return;
}
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].filament_area)
{
return;
}
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
@@ -522,19 +439,19 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
double retraction_e_amount = mmToE(config->distance);
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
if (firmware_retract)
{
*output_stream << "G10" << new_line;
*output_stream << "G10\n";
//Assume default UM2 retraction settings.
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value - retraction_e_amount)), 25); // TODO: hardcoded values!
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
@@ -543,7 +460,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
if (config->zHop > 0)
{
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
}
}
@@ -552,13 +469,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
if (flavor == EGCodeFlavor::BFB)
{
if (!extruder_attr[current_extruder].retraction_e_amount_current)
*output_stream << "M103" << new_line;
*output_stream << "M103\r\n";
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
return;
}
double retraction_e_amount = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
double retraction_e_amount = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
{
return;
@@ -574,13 +491,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
{
return;
}
*output_stream << "G10 S1" << new_line;
*output_stream << "G10 S1\n";
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
@@ -608,11 +525,11 @@ void GCodeExport::switchExtruder(int new_extruder)
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
{
*output_stream << "M135 T" << current_extruder << new_line;
*output_stream << "M135 T" << current_extruder << "\n";
}
else
{
*output_stream << "T" << current_extruder << new_line;
*output_stream << "T" << current_extruder << "\n";
}
writeCode(extruder_attr[new_extruder].start_code.c_str());
@@ -622,7 +539,11 @@ void GCodeExport::switchExtruder(int new_extruder)
void GCodeExport::writeCode(const char* str)
{
*output_stream << str << new_line;
*output_stream << str;
if (flavor == EGCodeFlavor::BFB)
*output_stream << "\r\n";
else
*output_stream << "\n";
}
void GCodeExport::writeFanCommand(double speed)
@@ -632,16 +553,16 @@ void GCodeExport::writeFanCommand(double speed)
if (speed > 0)
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
}
else
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M127 T0" << new_line;
*output_stream << "M127 T0\n";
else
*output_stream << "M107" << new_line;
*output_stream << "M107\n";
}
currentFanSpeed = speed;
}
@@ -657,7 +578,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
*output_stream << "M104";
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << new_line;
*output_stream << " S" << temperature << "\n";
extruder_attr[extruder].currentTemperature = temperature;
}
@@ -667,7 +588,7 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << new_line;
*output_stream << temperature << "\n";
}
void GCodeExport::finalize(double moveSpeed, const char* endCode)
+23 -93
Ver Arquivo
@@ -18,9 +18,13 @@ namespace cura {
struct CoastingConfig
{
bool coasting_enable;
double coasting_volume;
double coasting_speed;
double coasting_min_volume;
double coasting_volume_move;
double coasting_speed_move;
double coasting_min_volume_move;
double coasting_volume_retract;
double coasting_speed_retract;
double coasting_min_volume_retract;
};
class RetractionConfig
@@ -40,19 +44,19 @@ public:
class GCodePathConfig
{
private:
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
double speed; //!< current movement speed (mm/s) (modified by layer_nr etc.)
double speed_base; //!< movement speed (mm/s) specific to this print feature
double speed_current; //!< current movement speed (mm/s) (modified by layer_nr etc.)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow in %
int layer_thickness; //!< layer height
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
public:
PrintFeatureType type; //!< name of the feature type
const char* name; //!< name of the feature type
bool spiralize;
RetractionConfig *const retraction_config;
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type) : speed_iconic(0), speed(0), line_width(0), extrusion_mm3_per_mm(0.0), type(type), spiralize(false), retraction_config(retraction_config) {}
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
/*!
* Initialize some of the member variables.
@@ -61,8 +65,8 @@ public:
*/
void init(double speed, int line_width, double flow)
{
speed_iconic = speed;
this->speed = speed;
speed_base = speed;
this->speed_current = speed;
this->line_width = line_width;
this->flow = flow;
}
@@ -87,15 +91,7 @@ public:
*/
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
{
speed = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
* Set the speed to the iconic speed, i.e. the normal speed of the feature type for which this is a config.
*/
void setSpeedIconic()
{
speed = speed_iconic;
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
@@ -111,7 +107,7 @@ public:
*/
double getSpeed()
{
return speed;
return speed_current;
}
int getLineWidth()
@@ -124,11 +120,6 @@ public:
return line_width == 0;
}
double getFlowPercentage()
{
return flow;
}
private:
void calculateExtrusion()
{
@@ -143,7 +134,6 @@ class GCodeExport : public NoCopy
private:
struct ExtruderTrainAttributes
{
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string start_code;
@@ -179,15 +169,13 @@ private:
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
, last_retraction_prime_speed(0.0)
, last_retraction_prime_speed(1.0)
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
bool use_extruder_offset_to_offset_coords;
std::ostream* output_stream;
std::string new_line;
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
Point3 currentPosition;
double currentSpeed; //!< The current speed (F values / 60) in mm/s
@@ -203,64 +191,19 @@ private:
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
CommandSocket* commandSocket; //!< for sending travel data
unsigned int layer_nr; //!< for sending travel data
protected:
/*!
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param e the value to convert
* \return the value converted to mm
*/
double eToMm(double e);
/*!
* Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm3 the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mm3ToE(double mm3);
/*!
* Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mmToE(double mm);
public:
GCodeExport();
~GCodeExport();
/*!
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
*
* \param print_time The total print time of the whole file (if known)
* \param filament_used_0 The total mm^3 filament used for the primary extruder (if known)
* \param filament_used_1 The total mm^3 filament used for the secondary extruder (if used and if known)
* \return The string representing the file header
*/
std::string getFileHeader(double print_time = 666, int filament_used_0 = 666, int filament_used_1 = 0);
void setLayerNr(unsigned int layer_nr);
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
int getNozzleSize(int extruder_idx);
Point getExtruderOffset(int id);
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
@@ -286,7 +229,7 @@ public:
void setFilamentDiameter(unsigned int n, int diameter);
double getCurrentExtrudedVolume();
double getTotalFilamentUsed(int e);
double getTotalPrintTime();
@@ -295,7 +238,6 @@ public:
void writeComment(std::string comment);
void writeTypeComment(const char* type);
void writeTypeComment(PrintFeatureType type);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
@@ -340,7 +282,6 @@ public:
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
@@ -349,21 +290,10 @@ public:
extruder_attr[n].extruder_switch_retraction_distance = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
}
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
}
else
{
new_line = "\n";
}
}
void finalize(double moveSpeed, const char* endCode);
+104 -142
Ver Arquivo
@@ -22,18 +22,17 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
return *this;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow)
return &paths.back();
paths.emplace_back();
GCodePath* ret = &paths.back();
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done && paths[paths.size()-1].flow == flow)
return &paths[paths.size()-1];
paths.push_back(GCodePath());
GCodePath* ret = &paths[paths.size()-1];
ret->retract = false;
ret->config = config;
ret->done = false;
ret->flow = flow;
ret->space_fill_type = space_fill_type;
if (config != &storage.travel_config)
{
last_retraction_config = config->retraction_config;
@@ -48,8 +47,9 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, commandSocket(commandSocket)
, layer_nr(layer_nr)
, z(z)
, layer_thickness(layer_thickness)
@@ -105,6 +105,7 @@ void GCodePlanner::setIsInside(bool _is_inside)
is_inside = _is_inside;
}
bool GCodePlanner::setExtruder(int extruder)
{
if (extruder == extruder_plans.back().extruder)
@@ -114,7 +115,6 @@ bool GCodePlanner::setExtruder(int extruder)
{ // handle end position of the prev extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
if (!end_pos_absolute)
{
@@ -122,18 +122,25 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
end_pos += extruder_offset; // absolute end pos is given as a head position
}
addTravel(end_pos); // + extruder_offset cause it
}
extruder_plans.emplace_back(extruder);
if (extruder_plans.back().paths.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;
}
else
{
extruder_plans.emplace_back(extruder);
}
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
{ // handle starting pos of the new extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
if (!start_pos_absolute)
{
@@ -141,6 +148,7 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
start_pos += extruder_offset; // absolute start pos is given as a head position
}
lastPosition = start_pos;
@@ -150,13 +158,13 @@ bool GCodePlanner::setExtruder(int extruder)
void GCodePlanner::moveInsideCombBoundary(int distance)
{
int max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
int max_dist = MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
// this function is to be used to move from the boudary of a part to inside the part
Point p = lastPosition; // copy, since we are going to move p
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist) != NO_INDEX)
{
//Move inside again, so we move out of tight 90deg corners
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2);
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
if (comb_boundary_inside.inside(p))
{
addTravel_simple(p);
@@ -179,29 +187,21 @@ void GCodePlanner::addTravel(Point p)
if (combed)
{
bool retract = combPaths.size() > 1;
if (!retract)
{ // check whether we want to retract
for (CombPath& combPath : combPaths)
{ // retract when path moves through a boundary
if (combPath.cross_boundary || combPath.throughAir)
{
retract = true;
break;
}
if (!retract && combPaths.size() == 1 && combPaths[0].throughAir && combPaths[0].size() > 2)
{ // retract when avoiding obstacles through air
retract = true;
}
if (combPaths.size() == 1)
{
CombPath path = combPaths[0];
if (path.throughAir && !path.cross_boundary && path.size() == 2 && path[0] == lastPosition && path[1] == p)
{ // limit the retractions from support to support, which didn't cross anything
retract = false;
}
for (unsigned int path_idx = 0; path_idx < combPaths.size() && !retract; path_idx++)
{ // retract when path moves through a boundary
if (combPaths[path_idx].cross_boundary) { retract = true; }
}
}
if (retract && last_retraction_config->zHop > 0)
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&storage.travel_config);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
@@ -215,7 +215,7 @@ void GCodePlanner::addTravel(Point p)
{
continue;
}
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&storage.travel_config);
path->retract = retract;
for (Point& combPoint : combPath)
{
@@ -231,13 +231,13 @@ void GCodePlanner::addTravel(Point p)
// no combing? always retract!
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc)
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
if (was_inside)
{
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
}
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&storage.travel_config);
path->retract = true;
}
}
@@ -250,16 +250,16 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&storage.travel_config);
}
path->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, float flow)
{
getLatestPathWithConfig(config, space_fill_type, flow)->points.push_back(p);
getLatestPathWithConfig(config, flow)->points.push_back(p);
lastPosition = p;
}
@@ -270,61 +270,51 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[startIdx];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
}
else
{
logWarning("WARNING: line added as polygon! (gcodePlanner)\n");
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type)
{
if (polygons.size() == 0)
{
return;
}
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
orderOptimizer.addPolygon(polygons[poly_idx]);
}
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for (int poly_idx : orderOptimizer.polyOrder)
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
{
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation);
int nr = orderOptimizer.polyOrder[i];
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config, wall_overlap_computation);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist)
{
LineOrderOptimizer orderOptimizer(lastPosition);
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
{
orderOptimizer.addPolygon(polygons[line_idx]);
}
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for (int poly_idx : orderOptimizer.polyOrder)
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
{
// addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config); // adds line as polygon; old code
PolygonRef polygon = polygons[poly_idx];
int start = orderOptimizer.polyStart[poly_idx];
int nr = orderOptimizer.polyOrder[i];
// addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
PolygonRef polygon = polygons[nr];
int start = orderOptimizer.polyStart[nr];
int end = 1 - start;
Point& p0 = polygon[start];
addTravel(p0);
Point& p1 = polygon[end];
addExtrusionMove(p1, config, space_fill_type);
addExtrusionMove(p1, config);
if (wipe_dist != 0)
{
int line_width = config->getLineWidth();
if (vSize2(p1-p0) > line_width * line_width * 4)
{ // otherwise line will get optimized by combining multiple into a single extrusion move
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, space_fill_type, 0.0);
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, 0.0);
}
}
}
@@ -451,21 +441,7 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
/*
min layer time
:
: min layer time fan speed min
| : :
^ max..|__: :
| \ :
fan | \ :
speed min..|... \:___________
|________________
layer time >
*/
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
fan_speed = fsml.cool_fan_speed_min;
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
@@ -476,25 +452,8 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
double fan_speed_diff = fsml.cool_fan_speed_max - fsml.cool_fan_speed_min;
double layer_time_diff = fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time;
double fraction_of_slope = (totalLayerTime - fsml.cool_min_layer_time) / layer_time_diff;
fan_speed = fsml.cool_fan_speed_max - fan_speed_diff * fraction_of_slope;
fan_speed = fsml.cool_fan_speed_max - (fsml.cool_fan_speed_max-fsml.cool_fan_speed_min) * (totalLayerTime - fsml.cool_min_layer_time) / (fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time);
}
/*
Supposing no influence of minimal layer time; i.e. layer time > min layer time fan speed min:
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
zero..|/__________________
layer nr >
*/
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
@@ -507,7 +466,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{
completeConfigs();
gcode.setLayerNr(layer_nr);
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
gcode.writeLayerComment(layer_nr);
@@ -543,7 +502,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
}
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
{
gcode.writeTypeComment(path.config->type);
gcode.writeTypeComment(path.config->name);
last_extrusion_config = path.config;
}
double speed = path.config->getSpeed();
@@ -552,10 +511,10 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
speed *= getTravelSpeedFactor();
else
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
int64_t nozzle_size = 400; // TODO
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, 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;
@@ -586,11 +545,13 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
}
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
{ // normal path to gcode algorithm
if ( // change infill |||||| to /\/\/\/\/ ...
if ( // change |||||| to /\/\/\/\/ ...
false &&
path_idx + 2 < paths.size() // has a next move
&& paths[path_idx+1].points.size() == 1 // is single extruded line
@@ -601,7 +562,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
)
{
sendPolygon(paths[path_idx+2].config->type, gcode.getPositionXY(), paths[path_idx+2].points.back(), paths[path_idx+2].getLineWidth());
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
path_idx += 2;
}
@@ -609,7 +569,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
@@ -636,7 +595,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
@@ -669,9 +627,10 @@ void GCodePlanner::completeConfigs()
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setLayerHeight(layer_thickness);
mesh.insetX_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
mesh.skin_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setLayerHeight(layer_thickness);
@@ -686,39 +645,26 @@ void GCodePlanner::completeConfigs()
void GCodePlanner::processInitialLayersSpeedup()
{
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
if (static_cast<int>(layer_nr) < initial_speedup_layers)
{
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for (SliceMeshStorage& mesh : storage.meshes)
for(SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
}
}
}
else if (static_cast<int>(layer_nr) == initial_speedup_layers)
{
storage.support_config.setSpeedIconic();
storage.support_roof_config.setSpeedIconic();
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setSpeedIconic();
mesh.insetX_config.setSpeedIconic();
mesh.skin_config.setSpeedIconic();
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setSpeedIconic();
}
}
}
}
void GCodePlanner::writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after)
@@ -789,12 +735,8 @@ bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int ext
}
}
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume)
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
{
if (coasting_volume <= 0)
{
return false;
}
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
GCodePath& path = paths[path_idx];
if (path_idx + 1 >= paths.size()
@@ -806,6 +748,21 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
{
return false;
}
GCodePath& path_next = paths[path_idx + 1];
if (path_next.retract)
{
if (coasting_volume_retract <= 0) { return false; }
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx));
}
else
{
if (coasting_volume_move <= 0) { return false; }
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
}
}
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
{
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
@@ -813,8 +770,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed
int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
// /\ the minimal distance when coasting will coast the full coasting volume instead of linearly less with linearly smaller paths
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
std::vector<int64_t> accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...)
@@ -867,11 +823,14 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
}
}
}
assert (acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size()); // something has gone wrong; coasting_min_dist < coasting_dist ?
if (acc_dist_idx_gt_coast_dist == NO_INDEX)
{ // something has gone wrong; coasting_min_dist < coasting_dist ?
return false;
}
unsigned int point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist;
Point start;
{ // computation of begin point of coasting
int64_t residual_dist = actual_coasting_dist - accumulated_dist_per_point[acc_dist_idx_gt_coast_dist - 1];
@@ -879,24 +838,27 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
Point& b = path.points[point_idx_before_start + 1];
start = b + normal(a-b, residual_dist);
}
{ // write normal extrude path:
for(unsigned int point_idx = 0; point_idx <= point_idx_before_start; point_idx++)
{
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM());
}
sendPolygon(path.config->type, gcode.getPositionXY(), start, path.getLineWidth());
gcode.writeMove(start, extrude_speed, path.getExtrusionMM3perMM());
}
// write coasting path
if (path_next.retract)
{
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
}
for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++)
{
gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0);
}
gcode.addLastCoastedVolume(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
return true;
}
+33 -46
Ver Arquivo
@@ -10,7 +10,6 @@
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
namespace cura
@@ -151,7 +150,6 @@ class GCodePath
{
public:
GCodePathConfig* config; //!< The configuration settings of the path.
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
std::vector<Point> points; //!< The points constituting this path.
@@ -171,15 +169,6 @@ public:
{
return flow * config->getExtrusionMM3perMM();
}
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
};
class ExtruderPlan
@@ -247,6 +236,8 @@ class GCodePlanner : public NoCopy
private:
SliceDataStorage& storage;
CommandSocket* commandSocket;
int layer_nr;
int z;
@@ -281,11 +272,10 @@ private:
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
*
* \param config The config used for the path returned
* \param space_fill_type The type of space filling which this path employs
* \param flow (optional) A ratio for the extrusion speed
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
@@ -305,7 +295,7 @@ public:
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
*/
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
private:
@@ -326,22 +316,6 @@ public:
return lastPosition;
}
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
}
/*!
* Set whether the next destination is inside a layer part or not.
*
@@ -400,16 +374,8 @@ public:
* \param path (optional) The travel path to which to add the point \p p
*/
void addTravel_simple(Point p, GCodePath* path = nullptr);
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
*
* \param p The point to extrude to
* \param config The config with which to extrude
* \param space_fill_type Of what space filling type this extrusion move is a part
* \param flow A modifier of the extrusion width which would follow from the \p config
*/
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
@@ -419,10 +385,9 @@ public:
* Add lines to the gcode with optimized order.
* \param polygons The lines
* \param config The config of the lines
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
@@ -473,13 +438,35 @@ public:
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked during a normal move.
* \param coasting_speed The speed at which to move during move-coasting.
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
* \param coasting_volume_move The volume otherwise leaked during a normal move.
* \param coasting_speed_move The speed at which to move during move-coasting.
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
* \param coasting_speed_retract The speed at which to move during retract-coasting.
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
*
* \param gcode The gcode to write the planned paths to
* \param path The extrusion path to be written to GCode.
* \param path_next The next travel path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked.
* \param coasting_speed The speed at which to move during coasting.
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
* \param gcode The gcode to write the planned paths to
+348 -180
Ver Arquivo
@@ -2,6 +2,7 @@
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/AABB.h"
#include "utils/logoutput.h"
namespace cura {
@@ -15,28 +16,36 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
switch(pattern)
{
case EFillMethod::GRID:
generateGridInfill(result_lines);
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
break;
case EFillMethod::LINES:
generateLineInfill(result_lines, line_distance, fill_angle);
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(result_lines);
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
break;
case EFillMethod::CONCENTRIC:
PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
outline = &outline_offsetted;
if (abs(infill_line_width - line_distance) < 10)
if (outlineOffset != 0)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
else
if (abs(extrusion_width - line_distance) < 10)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
}
else
{
generateConcentricInfill(*outline, result_polygons, line_distance);
}
break;
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
break;
default:
logError("Fill pattern has unknown value.\n");
@@ -44,9 +53,9 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
}
}
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
{
while(outline.size() > 0)
{
@@ -56,13 +65,13 @@ void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, P
result.add(r);
}
Polygons next_outline;
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
}
}
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
@@ -76,216 +85,375 @@ void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int in
}
void Infill::generateGridInfill(Polygons& result)
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
{
generateLineInfill(result, line_distance * 2, fill_angle);
generateLineInfill(result, line_distance * 2, fill_angle + 90);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 90);
}
void Infill::generateTriangleInfill(Polygons& result)
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
{
generateLineInfill(result, line_distance * 3, fill_angle);
generateLineInfill(result, line_distance * 3, fill_angle + 60);
generateLineInfill(result, line_distance * 3, fill_angle + 120);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 60);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 120);
}
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
{
auto addLine = [&](Point from, Point to)
{
{
PolygonRef p = result.newPoly();
p.add(rotation_matrix.unapply(from));
p.add(rotation_matrix.unapply(to));
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0)
{
return -1;
}
if (n > 0)
{
return 1;
}
if (n < 0) return -1;
if (n > 0) return 1;
return 0;
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
{
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
{
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
{ // segment is too short to create infill
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
continue;
}
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
}
scanline_idx += 1;
}
}
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
bool safe_outline_offset = false;
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
bool safe_outline_offset = true;
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
{
if (connected_zigzags)
{
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
else
{
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
/*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* rough explanation of the zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
*
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
*/
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
if (line_distance == 0)
{
return;
}
if (in_outline.size() == 0)
{
return;
}
if (lineSpacing == 0) return;
if (in_outline.size() == 0) return;
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
if (outline.size() == 0) return;
Polygons outline;
if (outline_offset != 0)
{
PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
}
else
{
outline = in_outline;
}
PointMatrix matrix(rotation);
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
outline.applyMatrix(matrix);
if (outline.size() == 0)
{
return;
}
outline.applyMatrix(rotation_matrix);
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / line_distance;
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
{
cut_list.push_back(std::vector<int64_t>());
}
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
{
PolygonRef poly = outline[poly_idx];
Point p0 = poly.back();
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
{
Point p1 = poly[point_idx];
if (p1.X == p0.X)
{
zigzag_connector_processor.registerVertex(p1);
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
Point p1 = outline[poly_idx][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
}
else
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
{
int x = scanline_idx * line_distance;
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
cutList[scanline_idx - scanline_min_idx].push_back(y);
}
zigzag_connector_processor.registerVertex(p1);
p0 = p1;
}
zigzag_connector_processor.registerPolyFinished();
}
if (cut_list.size() == 0)
{
return;
}
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
{
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
{
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
}
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
{
// if (in_outline.size() == 0) return;
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
Polygons empty;
Polygons outline = in_outline.difference(empty); // copy
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven = false;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
Point lastPoint = p0;
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
lastPoint = p1;
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
addLine(lastPoint, Point(x,y));
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
{ // add whole unevenBoundarySegment (including the just obtained point)
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
{
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
}
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
unevenBoundarySegment.clear();
}
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
unevenBoundarySegment.push_back(Point(x,y));
else
unevenBoundarySegment.clear();
}
lastPoint = Point(x,y);
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
}
}
if (!isFirstBoundarySegment)
{
if (isEvenScanSegment)
addLine(lastPoint, p1);
else if (connect_zigzags)
unevenBoundarySegment.push_back(p1);
}
lastPoint = p1;
p0 = p1;
}
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
{
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
else if (!firstBoundarySegmentEndsInEven)
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
}
if (cutList.size() == 0) return;
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven = true;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
else boundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && !isEvenScanSegment)
{ // add whole boundarySegment (including the just obtained point)
for (unsigned int p = 1; p < boundarySegment.size(); p++)
{
addLine(boundarySegment[p-1], boundarySegment[p]);
}
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
boundarySegment.clear();
}
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
{
boundarySegment.clear();
boundarySegment.emplace_back(x,y);
} else
boundarySegment.clear();
}
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
boundarySegment.emplace_back(x,y);
}
}
if (!isFirstBoundarySegment && isEvenScanSegment)
boundarySegment.push_back(p1);
p0 = p1;
}
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
}//namespace cura
+76 -134
Ver Arquivo
@@ -4,141 +4,99 @@
#include "utils/polygon.h"
#include "settings.h"
// #include "ZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessor.h"
#include "infill/NoZigZagConnectorProcessor.h"
#include "infill/ActualZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
namespace cura
{
class Infill
{
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
int infill_line_width; //!< The line width of the infill lines to generate
int line_distance; //!< The distance between two infill lines / polygons
double infill_overlap; //!< the percentage (of infill_line_width) to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, bool remove_overlapping_perimeters, int infill_line_width, int line_distance, double infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
, remove_overlapping_perimeters(remove_overlapping_perimeters)
, infill_line_width(infill_line_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
class Infill
{
}
/*!
* Generate the infill.
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \param result_lines (output) The resulting line segments (from linear infill types)
* \param in_between (optional output) The areas in between two concecutive concentric infill polygons
*/
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
EFillMethod pattern;
const Polygons& in_outline;
int outlineOffset;
bool avoidOverlappingPerimeters;
int extrusion_width;
int line_distance;
double infill_overlap;
double fill_angle;
bool connect_zigzags;
bool use_endPieces;
private:
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
: pattern(pattern)
, in_outline(in_outline)
, outlineOffset(outlineOffset)
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
, extrusion_width(extrusion_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connect_zigzags(connect_zigzags)
, use_endPieces(use_endPieces)
{
}
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
};
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
/*!
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
/*!
* Generate dense concentric infill (100%)
*
* \param outline The actual outline of the area within which to generate infill
* \param result (output) The resulting polygons
* \param in_between (output) The areas in between each two consecutive polygons
* \param remove_overlapping_perimeters Whether to remove overlapping perimeter parts
*/
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool remove_overlapping_perimeters);
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
/*!
* Generate a rectangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateTriangleInfill(Polygons& result);
/*!
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
* \param result (output) The resulting lines
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
*
* algorithm:
* 1) for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2) for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
*/
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
*
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
*
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
*/
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
/*!
* adapted from generateLineInfill(.)
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
* sigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connect_zigzags])
*
*
* <--
* ___
@@ -148,37 +106,21 @@ private:
* -->
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
*
*
* v disconnected end piece: leave out last line segment
* _____
* | | | \ .
* | | | ,
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
*
*
* v connected end piece
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
* ^ disconnected end piece
*/
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
};
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
}//namespace cura
#endif//INFILL_H
-47
Ver Arquivo
@@ -1,47 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
/*!
* In contrast to NoZigZagConnectorProcessor
*/
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
{
protected:
/*!
* The line segments belonging the zigzag connector to which the very first vertex belongs.
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
*
* Because the boundary polygon may start in in the middle of a zigzag connector,
*/
std::vector<Point> first_zigzag_connector;
/*!
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
*/
std::vector<Point> zigzag_connector;
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
, is_first_zigzag_connector(true)
, first_zigzag_connector_ends_in_even_scanline(true)
, last_scanline_is_even(false)
{
}
};
} // namespace cura
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
-25
Ver Arquivo
@@ -1,25 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "NoZigZagConnectorProcessor.h"
namespace cura
{
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
{
}
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
}
void NoZigZagConnectorProcessor::registerPolyFinished()
{
}
} // namespace cura
-28
Ver Arquivo
@@ -1,28 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
namespace cura
{
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
{
public:
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
-154
Ver Arquivo
@@ -1,154 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
namespace cura
{
/*!
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
*
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
* at the same time as the lines are created.
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connected_zigzags])
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
* The inheritance structure is as such:
* ZigzagConnectorProcessor
* / \ .
* / \ .
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
* / \ for lines infill .
* / \ .
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
* / \ for zigzag infill (without end pieces) .
* / \ .
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
*
* v v zigzag connectors
* <--
* :___: : < scanlines
* | | |
* | | | < infill lines along scanlines
* | |___|
* : : :
* --> winding order of polygon
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
*
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
* ^ connected end piece
* include a boundary segment also if it starts in an odd scanline and ends odd,
* or starts in an even scanline and ends in an even scanline,
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
*
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
*/
class ZigzagConnectorProcessor
{
protected:
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
Polygons& result; //!< The result of the computation
virtual ~ZigzagConnectorProcessor()
{}
/*!
* Add a line to the result bu unapplying the rotation rotation_matrix.
*
* \param from The one end of the line segment
* \param to The other end of the line segment
*/
void addLine(Point from, Point to)
{
PolygonRef line_poly = result.newPoly();
line_poly.add(rotation_matrix.unapply(from));
line_poly.add(rotation_matrix.unapply(to));
}
/*!
* Basic constructor. Inheriting children should call this constructor.
*
* \param rotation_matrix The rotation matrix used to enforce the infill angle
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
*/
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: rotation_matrix(rotation_matrix)
, result(result)
{}
public:
/*!
* Handle the next vertex on the outer boundary.
* \param vertex The vertex
*/
virtual void registerVertex(const Point& vertex) = 0;
/*!
* Handle the next intersection between a scanline and the outer boundary.
*
* \param intersection The intersection
* \param scanline_is_even Whether the scanline was even
*/
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
/*!
* Handle the end of a polygon and prepare for the next.
* This function should reset all member variables.
*/
virtual void registerPolyFinished() = 0;
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
@@ -1,75 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even)
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|| is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,27 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
@@ -1,79 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole oddBoundarySegment (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
// skip the last segment to the [intersection]
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// write very last line segment if needed
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only add last element if boundary segment ends in odd scanline
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,26 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
@@ -1,27 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
addLine(last_connector_point, vertex);
}
else
{ // it's yet unclear whether the line segment should be included, so we store it until we know
zigzag_connector.push_back(vertex);
}
last_connector_point = vertex;
}
} // namespace cura
@@ -1,32 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
namespace cura
{
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
{
protected:
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
, last_connector_point(0,0)
{
}
public:
void registerVertex(const Point& vertex);
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
@@ -1,72 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorNoEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{
zigzag_connector.push_back(vertex);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
{
zigzag_connector.push_back(intersection);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
{
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,29 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
{
public:
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
+31 -2
Ver Arquivo
@@ -16,7 +16,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
part->insets.push_back(Polygons());
if (i == 0)
{
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
if (line_width_0 < nozzle_width)
{
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
@@ -26,7 +26,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
}
} else if (i == 1)
{
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
if (line_width_0 < nozzle_width)
{
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
@@ -71,4 +71,33 @@ void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int l
}
}
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
{
// optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[0].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
}
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() > 0)
{
for(int i=1; i<insetCount; i++)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(Polygons());
PolygonUtils::offsetExtrusionWidth(reinforcement_wall.wall_reinforcement_axtra_walls[i-1], true, line_width_x, reinforcement_wall.wall_reinforcement_axtra_walls[i], &part->perimeterGaps, avoidOverlappingPerimeters);
//Finally optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[i].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[i].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
break;
}
}
}
}
}//namespace cura
+10
Ver Arquivo
@@ -36,6 +36,16 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
*/
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the wall reinforcement extra walls for a single layer part.
*
* \param part The part for which to generate the extra walls.
* \param line_width_x line width of the walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+3 -4
Ver Arquivo
@@ -66,7 +66,7 @@ void print_call(int argc, char **argv)
void connect(int argc, char **argv)
{
CommandSocket::instantiate();
CommandSocket* commandSocket = new CommandSocket();
std::string ip;
int port = 49674;
@@ -107,7 +107,7 @@ void connect(int argc, char **argv)
}
}
CommandSocket::getInstance()->connect(ip, port);
commandSocket->connect(ip, port);
}
void slice(int argc, char **argv)
@@ -148,13 +148,12 @@ void slice(int argc, char **argv)
FffProcessor::getInstance()->time_keeper.restart();
delete meshgroup;
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_extruder_train = meshgroup->createExtruderTrain(0);
last_settings_object = meshgroup;
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
+105 -96
Ver Arquivo
@@ -2,7 +2,6 @@
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/linearAlg2D.h"
#define INLINE static inline
@@ -16,16 +15,17 @@ void PathOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
{
float dist = vSize2f(poly[point_idx] - startPoint);
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
{
best = point_idx;
best = i_point;
bestDist = dist;
}
}
@@ -37,50 +37,46 @@ void PathOrderOptimizer::optimize()
Point prev_point = startPoint;
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
{
int best_poly_idx = -1;
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
{
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
continue;
}
assert (polygons[poly_idx].size() != 2);
assert (polygons[i_polygon].size() != 2);
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
if (dist < bestDist)
{
best_poly_idx = poly_idx;
best = i_polygon;
bestDist = dist;
}
}
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
{
assert(polygons[best_poly_idx].size() != 2);
assert(polygons[best].size() != 2);
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
prev_point = polygons[best][polyStart[best]];
picked[best_poly_idx] = true;
polyOrder.push_back(best_poly_idx);
picked[best] = true;
polyOrder.push_back(best);
}
else
{
logError("Failed to find next closest polygon.\n");
}
}
prev_point = startPoint;
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int poly_idx = polyOrder[order_idx];
int poly_idx = polyOrder[n];
int point_idx = getPolyStart(prev_point, poly_idx);
polyStart[poly_idx] = point_idx;
prev_point = polygons[poly_idx][point_idx];
@@ -103,23 +99,22 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float best_point_score = std::numeric_limits<float>::infinity();
Point p0 = poly.back();
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
{
Point& p1 = poly[point_idx];
Point& p2 = poly[(point_idx + 1) % poly.size()];
int64_t dist = vSize2(p1 - prev_point);
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
// this score is in the order of 5 mm
if (dist + is_on_inside_corner_score < best_point_score)
float dist = vSize2f(poly[i_point] - prev_point);
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
{
best_point_idx = point_idx;
best_point_score = dist + is_on_inside_corner_score;
best_point_idx = i_point;
bestDist = dist;
}
p0 = p1;
}
return best_point_idx;
}
@@ -157,113 +152,127 @@ void LineOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
{
int best_point_idx = -1;
float best_point_dist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
{
float dist = vSize2f(poly[point_idx] - startPoint);
if (dist < best_point_dist)
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
{
best_point_idx = point_idx;
best_point_dist = dist;
best = i_point;
bestDist = dist;
}
}
polyStart.push_back(best_point_idx);
polyStart.push_back(best);
assert(poly.size() == 2);
line_bucket_grid.insert(poly[0], poly_idx);
line_bucket_grid.insert(poly[1], poly_idx);
line_bucket_grid.insert(poly[0], i_polygon);
line_bucket_grid.insert(poly[1], i_polygon);
}
Point incoming_perpundicular_normal(0, 0);
Point incommingPerpundicularNormal(0, 0);
Point prev_point = startPoint;
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
{
int best_line_idx = -1;
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
{
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
{
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
continue;
}
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
}
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
if (best == -1) /// if single-line-polygon hasn't been found yet
{
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
{
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
continue;
}
assert(polygons[poly_idx].size() == 2);
assert(polygons[i_polygon].size() == 2);
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
}
}
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
{
PolygonRef best_line = polygons[best_line_idx];
assert(best_line.size() == 2);
assert(polygons[best].size() == 2);
int line_start_point_idx = polyStart[best_line_idx];
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
Point& line_start = best_line[line_start_point_idx];
Point& line_end = best_line[line_end_point_idx];
prev_point = line_end;
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
prev_point = polygons[best][endIdx];
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
picked[best_line_idx] = true;
polyOrder.push_back(best_line_idx);
picked[best] = true;
polyOrder.push_back(best);
}
else
{
logError("Failed to find next closest line.\n");
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int nr = polyOrder[n];
PolygonRef poly = polygons[nr];
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i=0;i<poly.size(); i++)
{
float dist = vSize2f(polygons[nr][i] - prev_point);
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
{
best = i;
bestDist = dist + dot_score;
}
}
polyStart[nr] = best;
assert(poly.size() == 2);
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
}
}
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
{
Point& p0 = polygons[poly_idx][0];
Point& p1 = polygons[poly_idx][1];
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
{ /// check distance to first point on line (0)
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
{
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 0;
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 0;
}
}
{ /// check distance to second point on line (1)
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
{
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 1;
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 1;
}
}
}
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
{
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
}
}//namespace cura
+1 -28
Ver Arquivo
@@ -81,35 +81,8 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
/*!
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
*
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
*
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
* in which case the travel move might involve combing, which makes it rather longer.
*
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
* \param best[in, out] The index of current best line
* \param best_score[in, out] The distance score for the current best line
* \param prev_point[in] The previous point from which to find the next best line
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
*/
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
/*!
* Get a score to modify the distance score for measuring how good two lines follow each other.
*
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
*
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
* \param from The one end of the next line
* \param to The other end of the next line
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
*
*/
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
};
}//namespace cura
-5
Ver Arquivo
@@ -236,11 +236,6 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
if (!conf) //Setting could not be found.
{
logWarning("Trying to override unknown setting %s.", setting.c_str());
continue;
}
_loadSettingValues(conf, override_iterator, false);
}
}
+8 -12
Ver Arquivo
@@ -42,16 +42,13 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
if (upSkinCount == 0) upskin = Polygons();
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
auto getInsidePolygons = [&part](SliceLayer& layer2)
{
Polygons result;
for(SliceLayerPart& part2 : layer2.parts)
{
if (part.boundaryBox.hit(part2.boundaryBox))
{
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
result.add(part2.insets[wall_idx]);
}
result.add(part2.insets.back());
}
return result;
};
@@ -70,20 +67,20 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
}
else
{
if (layer_nr >= downSkinCount && downSkinCount > 0)
if (layer_nr > 0 && downSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
}
@@ -137,7 +134,7 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = storage.layers[layerNr];
@@ -145,10 +142,9 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_e
{
if (int(part.insets.size()) < wall_line_count)
{
part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
+2 -4
Ver Arquivo
@@ -65,15 +65,13 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
*
* The walls should already be generated.
*
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
*
* \param layerNr The index of the layer for which to generate the infill
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
* \param innermost_wall_extrusion_width width of the innermost wall lines
* \param extrusionWidth width of the wall lines
* \param infill_skin_overlap overlap distance between infill and skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
*/
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count);
/*!
* \brief Combines the infill of multiple layers for a specified mesh.
+93 -6
Ver Arquivo
@@ -59,13 +59,13 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config(&retraction_config, PrintFeatureType::MoveCombing),
travel_config(&retraction_config, "MOVE"),
skirt_config(initializeSkirtConfigs()),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_extruder_nr")], PrintFeatureType::Support),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], "SUPPORT"),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN"),
max_object_height_second_to_last_extruder(-1)
{
}
@@ -161,6 +161,93 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
}
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr)
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
if (layer_nr < 0)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true; // raft
}
else
{
if (layer_nr == 0)
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield
// support
if (support.supportLayers[layer_nr].supportAreas.size() > 0)
{
if (layer_nr == 0)
{
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
}
else
{
ret[getSettingAsIndex("support_extruder_nr")] = true;
}
}
if (support.supportLayers[layer_nr].roofs.size() > 0)
{
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
}
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
int extr_nr = mesh.getSettingAsIndex("extruder_nr");
if (layer.parts.size() > 0)
{
ret[extr_nr] = true;
}
}
}
return ret;
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield ..?
// support
// support is presupposed to be present...
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
ret[getSettingAsIndex("support_extruder_nr")] = true;
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
return ret;
}
+33 -3
Ver Arquivo
@@ -24,6 +24,19 @@ public:
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
/*!
* A ReinforcementWall is like an insulated wall behind the outer walls.
* It consists of an area with (generally more dense) infill and perimeters on the inside.
* On the outside it has the outer walls, or the inner walls of another ReinforcementWall.
*/
class ReinforcementWall
{
public:
Polygons wall_reinforcement_area; //!< The infill of the reinforced wall
std::vector<Polygons> wall_reinforcement_axtra_walls; //!< The extra walls on the inside of the reinforcement infill
};
/*!
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
@@ -37,6 +50,7 @@ public:
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
std::vector<ReinforcementWall> reinforcement_walls; //!< The reinforcement walls for this part. Order: from outter to inner reinforcement wall.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
@@ -117,13 +131,14 @@ public:
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
GCodePathConfig wall_reinforcement_config;
SliceMeshStorage(SettingsBaseVirtual* settings)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
{
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
infill_config.emplace_back(&retraction_config, "FILL");
}
};
@@ -173,7 +188,7 @@ public:
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
}
return ret;
}
@@ -212,6 +227,21 @@ public:
* \param include_helper_parts whether to include support and prime tower
*/
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts);
/*!
* Get the extruder numbers of all extruders used in a given layer.
*
* \param layer_nr the index of the layer for which to get the extruders used (negative layer numbers indicate the raft)
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed(int layer_nr);
/*!
* Get the extruders used.
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
};
}//namespace cura
+17 -38
Ver Arquivo
@@ -35,7 +35,7 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
return joined;
}
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
{
// initialization of supportAreasPerLayer
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
@@ -46,11 +46,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
if (mesh.getSettingBoolean("support_roof_enable"))
{
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
}
else
{
@@ -80,7 +80,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
*
* for support buildplate only: purge all support not connected to buildplate
*/
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
@@ -94,6 +94,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
int join_distance = mesh.getSettingInMicrons("support_join_distance");
@@ -113,23 +114,21 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int layerThickness = mesh.getSettingInMicrons("layer_height");
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
if (conical_support_angle == 0)
{
conical_support = false;
}
// derived settings:
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
int supportLayerThickness = layerThickness;
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
@@ -137,12 +136,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int64_t conical_support_offset;
if (conical_support_angle > 0)
{ // outward ==> wider base than overhang
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
{
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
}
else
{ // inward ==> smaller base than overhang
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
{
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
}
unsigned int support_layer_count = layer_count;
@@ -249,11 +248,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
storage.support.layer_nr_max_filled_layer = layer_idx;
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
}
// do stuff for when support on buildplate only
@@ -264,26 +263,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
{
Polygons& supportLayer = supportAreas[layer_idx];
if (conical_support)
{ // with conical support the next layer is allowed to be larger than the previous
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
//
// conical support can make
// layer above layer below
// v v
// | : |
// | ==> : |__
// |____ :....
//
// a miter limit would result in
// | : : |
// | :.. <== : |__
// .\___ :....
//
}
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
supportAreas[layer_idx] = touching_buildplate;
@@ -430,7 +409,7 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
{
int roof_layer_count = support_roof_height / layerThickness;
+6 -3
Ver Arquivo
@@ -15,8 +15,9 @@ public:
* Generate the support areas and support roof areas for all models.
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
private:
/*!
@@ -27,8 +28,9 @@ private:
* \param storage data storage containing the input layer outline data
* \param mesh_idx The index of the object for which to generate support areas
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
@@ -37,10 +39,11 @@ private:
*
* \param storage Output storage: support area + support roof area output
* \param supportAreas The basic support areas for the current mesh
* \param commandSocket Socket over which to report the progress
* \param layerThickness The layer height
* \param support_roof_height The thickness of the hammock in z directiontt
*/
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
-34
Ver Arquivo
@@ -1,34 +0,0 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "linearAlg2D.h"
#include <cmath> // atan2
#include "intpoint.h" // dot
namespace cura
{
float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
{
Point ba = a - b;
Point bc = c - b;
int64_t dott = dot(ba, bc); // dot product
int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
float angle = -atan2(det, dott); // from -pi to pi
if (angle >= 0 )
{
return angle;
}
else
{
return M_PI * 2 + angle;
}
// Point ba = a - b;
// Point bc = c - b;
// int64_t dott = dot(ba, bc); // dot product
// int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
// return -atan2(det, dott); // from -pi to pi
}
} // namespace cura
+1 -1
Ver Arquivo
@@ -202,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
return p0 * len / _len;
}
INLINE Point turn90CCW(const Point& p0)
INLINE Point crossZ(const Point& p0)
{
return Point(-p0.Y, p0.X);
}
+12 -40
Ver Arquivo
@@ -146,60 +146,32 @@ public:
if (ax_size < 0)
{// b is 'before' segment ac
if (b_is_beyond_ac) { *b_is_beyond_ac = -1; }
if (b_is_beyond_ac)
{
*b_is_beyond_ac = -1;
}
return vSize2(ab);
}
if (ax_size > ac_size)
{// b is 'after' segment ac
if (b_is_beyond_ac) { *b_is_beyond_ac = 1; }
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 1;
}
return vSize2(b - c);
}
if (b_is_beyond_ac) { *b_is_beyond_ac = 0; }
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 0;
}
Point ax = ac * ax_size / ac_size;
Point bx = ab - ax;
return vSize2(bx);
// return vSize2(ab) - ax_size*ax_size; // less accurate
}
/*!
* Checks whether the minimal distance between two line segments is at most \p max_dist
* The first line semgent is given by end points \p a and \p b, the second by \p c and \p d.
*
* \param a One end point of the first line segment
* \param b Another end point of the first line segment
* \param c One end point of the second line segment
* \param d Another end point of the second line segment
* \param max_dist The maximal distance between the two line segments for which this function will return true.
*/
static bool lineSegmentsAreCloserThan(const Point& a, const Point& b, const Point& c, const Point& d, int64_t max_dist)
{
int64_t max_dist2 = max_dist * max_dist;
return getDist2FromLineSegment(a, c, b) <= max_dist2
|| getDist2FromLineSegment(a, d, b) <= max_dist2
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
}
/*!
* Compute the angle between two consecutive line segments.
*
* The angle is computed from the left side of b when looking from a.
*
* c
* \ .
* \ b
* angle|
* |
* a
*
* \param a start of first line segment
* \param b end of first segment and start of second line segment
* \param c end of second line segment
* \return the angle in radians between 0 and 2 * pi of the corner in \p b
*/
static float getAngleLeft(const Point& a, const Point& b, const Point& c);
};
+22 -23
Ver Arquivo
@@ -21,6 +21,20 @@
namespace cura {
enum PolygonType
{
NoneType,
Inset0Type,
InsetXType,
SkinType,
SupportType,
SkirtType,
InfillType,
SupportInfillType,
MoveCombingType,
MoveRetractionType
};
class PartsView;
@@ -65,9 +79,7 @@ public:
}
PolygonRef& operator=(const PolygonRef& other) { polygon = other.polygon; return *this; }
bool operator==(const PolygonRef& other) const =delete;
ClipperLib::Path& operator*() { return *polygon; }
template <typename... Args>
@@ -213,7 +225,7 @@ public:
/*!
* 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
* Smoothing is performed by removing line segments 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
@@ -223,26 +235,15 @@ public:
PolygonRef& thiss = *this;
ClipperLib::Path* poly = result.polygon;
if (size() > 0)
{
poly->push_back(thiss[0]);
}
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
{
Point& last = thiss[poly_idx - 1];
Point& now = thiss[poly_idx];
Point& next = thiss[(poly_idx + 1) % size()];
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
{
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
if (poly_idx < size())
{
poly->push_back(thiss[poly_idx]);
}
}
else
{
poly->push_back(thiss[poly_idx]);
}
} else poly->push_back(thiss[poly_idx]);
}
}
@@ -361,9 +362,6 @@ public:
Polygons(const Polygons& other) { polygons = other.polygons; }
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
bool operator==(const Polygons& other) const =delete;
Polygons difference(const Polygons& other) const
{
Polygons ret;
@@ -407,12 +405,13 @@ public:
clipper.Execute(ClipperLib::ctXor, ret.polygons);
return ret;
}
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
{
Polygons ret;
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
double miterLimit = 1.2;
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
clipper.MiterLimit = miter_limit;
clipper.MiterLimit = miterLimit;
clipper.Execute(ret.polygons, distance);
return ret;
}
+11 -11
Ver Arquivo
@@ -70,8 +70,8 @@ Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int poi
Point p1 = poly[point_idx];
Point p2 = poly[(point_idx < (poly.size() - 1)) ? (point_idx + 1) : 0];
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
Point n = normal(off0 + off1, -offset);
return p1 + n;
@@ -85,7 +85,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
unsigned int bestPoly = NO_INDEX;
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
bool is_inside = false;
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
PolygonRef poly = polygons[poly_idx];
@@ -125,10 +125,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
// MM2INT(10.0) to retain precision for the eventual normalization
ret = x + normal(inward_dir, distance);
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
is_inside = dot(inward_dir, p - x) >= 0;
}
}
}
@@ -148,7 +147,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
continue;
}
else
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
{
projected_p_beyond_prev_segment = false;
Point x = a + ab * ax_length / ab_length;
@@ -160,9 +159,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
Point inward_dir = crossZ(normal(ab, distance));
ret = x + inward_dir;
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
is_inside = dot(inward_dir, p - x) >= 0;
}
}
}
@@ -172,7 +171,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
p1 = p2;
}
}
if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
if (is_inside)
{
if (bestDist2 < distance * distance)
{
@@ -192,6 +191,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
return NO_INDEX;
}
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
{
PolygonRef poly1 = poly1_result.poly;
+2 -2
Ver Arquivo
@@ -66,10 +66,10 @@ public:
* \param polygons The polygons onto which to move the point
* \param from The point to move.
* \param distance The distance by which to move the point.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
* \return The index to the polygon onto which we have moved the point.
*/
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits<int64_t>::max());
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max());
/*!
* Find the two points in two polygons with the smallest distance.
+2 -2
Ver Arquivo
@@ -79,7 +79,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
Point& last_point = *last_it;
Point& point = *it;
if (&from_it.poly == &to_list_poly
if ( from_it.poly == to_list_poly
&& (
(from_it.it == last_it || from_it.it == it) // we currently consider a linesegment directly connected to [from]
|| (from_it.prev().it == it || from_it.next().it == last_it) // line segment from [last_point] to [point] is connected to line segment of which [from] is the other end
@@ -94,7 +94,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
int64_t dist2 = vSize2(closest - from);
if (dist2 > line_width * line_width
|| (&from_it.poly == &to_list_poly
|| ( from_it.poly == to_list_poly
&& dot(from_it.next().p() - from, point - last_point) > 0
&& dot(from - from_it.prev().p(), point - last_point) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
)
+1 -10
Ver Arquivo
@@ -74,16 +74,7 @@ class WallOverlapComputation
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
: poly(poly), it(it) { }
Point& p() const { return *it; }
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
* \param other The ListPolyIt to test for equality
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
*/
bool operator==(const ListPolyIt& other) const
{
return &poly == &other.poly && it == other.it;
}
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.it; }
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
//!< move the iterator forward (and wrap around at the end)
ListPolyIt& operator++()
+4 -4
Ver Arquivo
@@ -25,9 +25,9 @@ void GCodePlannerTest::setUp()
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
fan_speed_layer_time_settings.cool_min_speed = 0.5;
// Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
// storage nr height position extruder time settings combing offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
// Command Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
// socket storage nr height position extruder time settings combing offset avoid distance
gCodePlanner = new GCodePlanner(nullptr, *storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
}
void GCodePlannerTest::tearDown()
@@ -46,7 +46,7 @@ void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
GCodeExport gcode;
GCodePathConfig configuration = storage->travel_config;
gCodePlanner->addExtrusionMove(Point(0, 0), &configuration, SpaceFillType::Lines, 1.0f); //Need to have at least one path to have a configuration.
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
TimeMaterialEstimates before_retract = gCodePlanner->computeNaiveTimeEstimates();
gCodePlanner->writeRetraction(gcode,(unsigned int)0,(unsigned int)0); //Make a retract.
TimeMaterialEstimates after_retract = gCodePlanner->computeNaiveTimeEstimates();
-52
Ver Arquivo
@@ -215,56 +215,4 @@ void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_
}
}
void LinearAlg2DTest::getAngleStraightTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(100, 1), 1.0);
}
void LinearAlg2DTest::getAngle45CcwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -100), 1.75);
}
void LinearAlg2DTest::getAngle90CcwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, -100), 1.5);
}
void LinearAlg2DTest::getAngle90CwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, 100), .5);
}
void LinearAlg2DTest::getAngleStraightBackTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, 1), 0.0);
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -1), 2.0);
}
void LinearAlg2DTest::getAngleLeftAABTest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(100, 0)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleLeftABBTest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(100, 0), Point(100, 100)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleLeftAAATest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(0, 0)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds)
{
float actual_angle = actual_angle_in_half_rounds * M_PI;
float supposed_angle = LinearAlg2D::getAngleLeft(a, b, c);
std::stringstream ss;
ss << "Corner in " << a << "-" << b << "-" << c << " was computed to have an angle of " << supposed_angle << " instead of " << actual_angle << ".";
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::fabs(actual_angle - supposed_angle) <= maximum_error_angle);
}
}
-33
Ver Arquivo
@@ -42,15 +42,6 @@ class LinearAlg2DTest : public CppUnit::TestFixture
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
CPPUNIT_TEST(getAngleStraightTest);
CPPUNIT_TEST(getAngle90CcwTest);
CPPUNIT_TEST(getAngle90CwTest);
CPPUNIT_TEST(getAngle45CcwTest);
CPPUNIT_TEST(getAngleStraightBackTest);
CPPUNIT_TEST(getAngleLeftAABTest);
CPPUNIT_TEST(getAngleLeftABBTest);
CPPUNIT_TEST(getAngleLeftAAATest);
CPPUNIT_TEST_SUITE_END();
public:
@@ -99,15 +90,6 @@ public:
void getDist2FromLineSegmentDiagonal2LargeTest();
void getDist2FromLineSegmentZeroNearTest();
void getDist2FromLineSegmentZeroOnTest();
void getAngleStraightTest();
void getAngle90CcwTest();
void getAngle90CwTest();
void getAngle45CcwTest();
void getAngleStraightBackTest();
void getAngleLeftAABTest();
void getAngleLeftABBTest();
void getAngleLeftAAATest();
private:
/*!
@@ -129,21 +111,6 @@ private:
* \param actual_is_beyond Whether the point is actually beyond the line.
*/
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
/*!
* \brief The maximum allowed error in angle measurements.
*/
static constexpr float maximum_error_angle = 1.0;
/*!
* Performs the assertion of the getAngle tests
*
* \param a the a parameter of getAngle
* \param b the b parameter of getAngle
* \param c the c parameter of getAngle
* \param actual_angle_in_half_rounds the actual angle where 0.5 equals ???
*/
void getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds);
};
}
+22 -95
Ver Arquivo
@@ -9,7 +9,6 @@
# * Single random value
# * All settings random
import ast #For safe function evaluation.
import sys
import subprocess
import os
@@ -65,6 +64,12 @@ class TestResults():
self._testsuites.append(suite)
return suite
def getFailureCount(self):
result = 0
for testsuite in self._testsuites:
result += testsuite.getFailureCount()
return result
## Save the test results to the file given in the filename.
def saveXML(self, filename):
xml = ElementTree.Element("testsuites")
@@ -88,34 +93,18 @@ class TestResults():
class Setting():
## Creates a new setting from a JSON node.
#
# Some parts of the setting may have to be evaluated as functions. For
# these, the default values of all settings are added as local variables.
#
# \param key The name of the setting.
# \param data The JSON node of the setting, containing the default value,
# the setting type, the minimum value, maximum value, the minimum warning
# value, the maximum warning value and (for enum types) the options.
# \param locals The local variables for eventual function evaluation.
def __init__(self, key, data, locals):
def __init__(self, key, data):
self._key = key
self._default = data["default"]
self._type = data["type"]
self._min_value = self._evaluateFunction(data.get("min_value", None), locals)
self._max_value = self._evaluateFunction(data.get("max_value", None), locals)
self._min_value_warning = self._evaluateFunction(data.get("min_value_warning", None), locals)
self._max_value_warning = self._evaluateFunction(data.get("max_value_warning", None), locals)
self._min_value = data.get("min_value", None)
self._max_value = data.get("max_value", None)
self._min_value_warning = data.get("min_value_warning", None)
self._max_value_warning = data.get("max_value_warning", None)
self._options = data.get("options", None)
if self._options is not None:
self._options = list(self._options.keys())
## Gets the default value of this setting, according to the source JSON.
#
# \return The default value.
def getDefault(self):
return self._default
## Return a list of possible values for this setting. This list depends on the setting type.
# For number values it contains the minimal and maximal values.
# For enums and booleans it will contain the exact possible values.
@@ -174,42 +163,10 @@ class Setting():
return random.uniform(float(min), float(max))
return random.choice(self.getSettingValues())
## Evaluates a setting value that is described as a function.
#
# Note that this function should behave EXACTLY the same as it does in
# UM/Settings/Setting.py:_createFunction. The only differences should be
# that this evaluation always uses the default values instead of the
# current profile values, and that this function directly evaluates the
# setting instead of returning a function with which to evaluate the
# setting. Also, this function doesn't need to compile the list of
# settings that this setting depends on.
#
# \param code The string to evaluate as a function of default values of
# other settings.
# \param locals The default values of other settings, as dictionary keyed
# by the setting names.
# \return The evaluated value of the setting, or None if \p code was None.
def _evaluateFunction(self, code, locals):
if not code: #The input was None. This setting value doesn't exist in the JSON.
return None
try:
tree = ast.parse(code, "eval")
compiled = compile(code, self._key, "eval")
except (SyntaxError, TypeError) as e:
print("Parse error in function (" + code + ") for setting", self._key + ":", str(e))
except IllegalMethodError as e:
print("Use of illegal method", str(e), "in function (" + code + ") for setting", self._key)
except Exception as e:
print("Exception in function (" + code + ") for setting", self._key + ":", str(e))
return eval(compiled, globals(), locals)
class EngineTest():
def __init__(self, json_filename, engine_filename, models):
self._json_filename = json_filename
self._json = json.load(open(json_filename, "r"))
self._locals = {}
self._addAllLocals() #Fills the _locals dictionary.
self._engine = engine_filename
self._models = models
self._settings = {}
@@ -223,7 +180,7 @@ class EngineTest():
def _flattenSettings(self, settings):
for key, setting in settings.items():
self._settings[key] = Setting(key, setting, self._locals)
self._settings[key] = Setting(key, setting)
if "children" in setting:
self._flattenSettings(setting["children"])
@@ -297,42 +254,9 @@ class EngineTest():
def getResults(self):
return self._test_results
## Adds all default values for all settings to the locals.
#
# The results are stored in self._locals, keyed by the setting name.
def _addAllLocals(self):
for key, data in self._json["categories"].items():
self._addLocals(data["settings"])
self._addLocals(self._json["machine_settings"])
## Adds the default values in a node of the setting tree to the locals.
#
# The results are stored in self._locals, keyed by the setting name.
#
# \param settings The JSON node of which to add the default values.
def _addLocals(self, settings):
for key, setting in settings.items():
self._locals[key] = setting["default"]
if "children" in setting:
self._addLocals(setting["children"]) #Recursively go down the tree.
def main(engine, model_path):
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
filenames = list(filter(lambda filename: filename.lower().endswith(".stl"), filenames))
for filename in filenames:
print("Slicing: %s (%d/%d)" % (filename, filenames.index(filename), len(filenames)))
t = time.time()
p = subprocess.Popen([engine, "-vv", os.path.join(model_path, filename)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
print ("Engine failed to report success on test object: %s" % (filename))
print(stderr.decode("utf-8", "replace").split("\n")[-5:])
sys.exit(1)
else:
print("Slicing took: %f" % (time.time() - t))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CuraEngine testing script")
parser.add_argument("--simple", action="store_true", help="Only run the single test, exit")
parser.add_argument("json", type=str, help="Machine JSON file to use")
parser.add_argument("engine", type=str, help="Engine executable")
parser.add_argument("models", type=str, nargs="+", help="List of models to use for testing")
@@ -340,10 +264,13 @@ if __name__ == "__main__":
et = EngineTest(args.json, args.engine, args.models)
if et.testDefaults() == 0:
et.testSingleChanges()
if et.testSingleRandom() == 0:
et.testDualRandom()
if et.testAllRandom(10) == 0:
et.testAllRandom(100)
if not args.simple:
et.testSingleChanges()
if et.testSingleRandom() == 0:
et.testDualRandom()
if et.testAllRandom(10) == 0:
et.testAllRandom(100)
et.getResults().saveXML("output.xml")
if args.simple:
if et.getResults().getFailureCount() > 0:
sys.exit(1)