Comparar commits
201 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ecd832744c | |||
| 03b654af3e | |||
| c79a7f1819 | |||
| e01f18c7d4 | |||
| 42891874f4 | |||
| 257d6a6635 | |||
| bec8bef455 | |||
| 4efeaa7083 | |||
| 95fd9d6685 | |||
| 2db37c6018 | |||
| 41b0966d26 | |||
| 93485cd0df | |||
| 2d3382874a | |||
| 1e78397e18 | |||
| 6bcdd94f7e | |||
| accd28db64 | |||
| 1c0f4c42d9 | |||
| 5da1632d9f | |||
| cbf1152f56 | |||
| 2273c5aefe | |||
| 46c793e73d | |||
| fd4969887b | |||
| 6620a050a5 | |||
| 6325197fce | |||
| f828d44365 | |||
| 29564a23e0 | |||
| 1fdda3319f | |||
| be113eceb4 | |||
| 0c42ff9bfa | |||
| 168e041c42 | |||
| 670ae6dd8c | |||
| 20adfa751f | |||
| bf8776b112 | |||
| 1d0f3f519a | |||
| 07fef8668c | |||
| 1c06fc49fc | |||
| beb9422d9b | |||
| a8359b9a68 | |||
| 5ccfe2d1aa | |||
| 74577759b4 | |||
| 45eb026777 | |||
| aabb07fd81 | |||
| 6377ec63e1 | |||
| eab2d8e667 | |||
| 8d41003c67 | |||
| ecfae4d75c | |||
| a2208f6b69 | |||
| bacacb01dc | |||
| 94c9399f2c | |||
| 168dc3c12b | |||
| 235af65b00 | |||
| f3f3be74cc | |||
| dca0bc80b5 | |||
| c0e57622d0 | |||
| b7a8fbe798 | |||
| 4353980e78 | |||
| 18ae9cf41d | |||
| 04edf35331 | |||
| 6718a8b2f3 | |||
| 08a5ec7dee | |||
| 3de01763c1 | |||
| bc7ee74d19 | |||
| fc20a6661b | |||
| 81d521a58b | |||
| c4eb1d9f27 | |||
| 32d1bb6d75 | |||
| afdb552f63 | |||
| a400ba28f2 | |||
| e0a7818d9e | |||
| 277b5dce75 | |||
| 462a6e8c16 | |||
| c20d35e293 | |||
| 9e56841cd2 | |||
| c39e43c161 | |||
| 421a6d4095 | |||
| 8497e46542 | |||
| 6510ebbd92 | |||
| c1b4a5398b | |||
| 11c4b9339a | |||
| 75efbac68e | |||
| cd199dc43e | |||
| 2372a78c9b | |||
| 142f4d519f | |||
| 9ea43e7fc1 | |||
| 9b92de9b8b | |||
| 6826581497 | |||
| 4fead4612b | |||
| f763edfb05 | |||
| 0e662d7d67 | |||
| 94d8c3ff32 | |||
| 8e91753afc | |||
| 21e59cc1e2 | |||
| f0f14b0be3 | |||
| a82c00bead | |||
| 14b1c5333a | |||
| bbe809dabc | |||
| ed581a92b2 | |||
| 9b68305851 | |||
| 940d3a86bd | |||
| 55047120d8 | |||
| e50b00fd73 | |||
| 7377f30fd0 | |||
| c9245e0926 | |||
| 7149b7dbf1 | |||
| daa3927a99 | |||
| 074d1125e6 | |||
| a92274678d | |||
| 081393d7f0 | |||
| 30da3097d0 | |||
| d830fc515b | |||
| 5f2d924771 | |||
| b1ab1cae5a | |||
| 1fb6b20d90 | |||
| dfb4c98e35 | |||
| e455d63ad2 | |||
| cb024c73b1 | |||
| d2d25058cb | |||
| e85362370b | |||
| 00c6f5c092 | |||
| 818c7da951 | |||
| 41723c8b38 | |||
| c8d75dd913 | |||
| faa60c408f | |||
| 7e322bc57c | |||
| 888fc54660 | |||
| fd660fcc11 | |||
| e8080422a4 | |||
| 5fecf2cd17 | |||
| b4bf17c6be | |||
| fabd658b53 | |||
| a2050de513 | |||
| 9bfb5b17e9 | |||
| e93d39d6d5 | |||
| 175a65415a | |||
| 74986dd95c | |||
| 55dd35ae5e | |||
| 8caf810642 | |||
| 76efc41407 | |||
| b0a0fe8f30 | |||
| 653b631ffd | |||
| b9a01ed031 | |||
| de9d3bb447 | |||
| 8f5a46f77d | |||
| c22793b5d7 | |||
| 1e684f23a7 | |||
| 7d0025975b | |||
| 5c892b564b | |||
| d5327ec3f1 | |||
| c48104bc86 | |||
| f58f1daec3 | |||
| 669bb523a3 | |||
| fa74f672ac | |||
| e224d9c853 | |||
| 338d80a7a9 | |||
| 87123dac31 | |||
| 73de288f44 | |||
| 9f5b5f405d | |||
| 2850b34227 | |||
| 072d320f1d | |||
| 8e4b61d2a5 | |||
| ba4e26f801 | |||
| cea0a0a98f | |||
| 2b51a11739 | |||
| d32443ca9b | |||
| 792aa9e8e8 | |||
| a63abb4e88 | |||
| 9f82f58f03 | |||
| df04467ab1 | |||
| bbd9412b5f | |||
| c476acd522 | |||
| 278efdeb39 | |||
| bb8a9dacba | |||
| 42420edeed | |||
| be2e96f3fd | |||
| 18adc0bbc9 | |||
| e7824faefe | |||
| 7199be1b4f | |||
| 727c863f1a | |||
| d732c49dd7 | |||
| 781fc5ed7b | |||
| 7243cf6da4 | |||
| 1cdcce4205 | |||
| acf381c008 | |||
| 4b7df9ddc0 | |||
| 7788a4a234 | |||
| 6224713998 | |||
| db8b30d77a | |||
| 050b9c88f2 | |||
| 8ac63fca6e | |||
| 1383882bc5 | |||
| 3a773d3c0f | |||
| 734ddce3c8 | |||
| 0b19936299 | |||
| 691d5de591 | |||
| 490cef1a5c | |||
| b777b55935 | |||
| 0428c08152 | |||
| 20c74dd22d | |||
| 188b190d21 | |||
| 1cd128decd | |||
| 925247a54d |
+15
-17
@@ -2,14 +2,7 @@ project(CuraEngine)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
option (ENABLE_ARCUS
|
||||
"Enable support for ARCUS" ON)
|
||||
|
||||
if (ENABLE_ARCUS)
|
||||
message(STATUS "Building with Arcus")
|
||||
find_package(Arcus REQUIRED)
|
||||
add_definitions(-DARCUS)
|
||||
endif ()
|
||||
find_package(Arcus REQUIRED)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
@@ -17,6 +10,10 @@ 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")
|
||||
@@ -73,7 +70,14 @@ 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
|
||||
@@ -85,17 +89,12 @@ set(engine_TEST
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
# Generating ProtoBuf protocol
|
||||
if (ENABLE_ARCUS)
|
||||
# Generating ProtoBuf protocol.
|
||||
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)
|
||||
if (ENABLE_ARCUS)
|
||||
target_link_libraries(_CuraEngine Arcus)
|
||||
endif ()
|
||||
target_link_libraries(_CuraEngine clipper Arcus)
|
||||
|
||||
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
|
||||
@@ -119,5 +118,4 @@ endif()
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
|
||||
include(CPackConfig.cmake)
|
||||
+6
-25
@@ -2,20 +2,18 @@ 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.
|
||||
@@ -24,28 +22,13 @@ message Object
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
// typeid 3
|
||||
message Progress
|
||||
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;
|
||||
|
||||
@@ -70,20 +53,16 @@ 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;
|
||||
}
|
||||
@@ -94,7 +73,9 @@ message Setting {
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// typeid 7
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message SlicingFinished {
|
||||
}
|
||||
|
||||
+139
-194
@@ -12,7 +12,7 @@ namespace cura
|
||||
|
||||
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
PrimeTower primetower();
|
||||
PrimeTower primetower;
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
@@ -21,8 +21,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (command_socket)
|
||||
command_socket->beginGCode();
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
|
||||
setConfigFanSpeedLayerTime();
|
||||
|
||||
@@ -72,17 +72,11 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
processLayer(storage, layer_nr, total_layers, has_raft);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
|
||||
|
||||
//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();
|
||||
}
|
||||
|
||||
@@ -104,12 +98,9 @@ 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_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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,8 +153,8 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
{ // support
|
||||
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"));
|
||||
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"));
|
||||
|
||||
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
@@ -173,30 +164,22 @@ 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 (!command_socket)
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
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");
|
||||
}
|
||||
std::string prefix = gcode.getFileHeader();
|
||||
gcode.writeCode(prefix.c_str());
|
||||
}
|
||||
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
@@ -248,7 +231,8 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
gcode.resetExtrusionValue();
|
||||
gcode.setZ(max_object_height + 5000);
|
||||
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), 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);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
@@ -273,25 +257,35 @@ 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;
|
||||
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"));
|
||||
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"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
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());
|
||||
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);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -304,17 +298,19 @@ 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;
|
||||
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"));
|
||||
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"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
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());
|
||||
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);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -329,17 +325,21 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
{ // raft surface layers
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
z += layer_height;
|
||||
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"));
|
||||
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"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
|
||||
Polygons raft_lines;
|
||||
int offset_from_poly_outline = 0;
|
||||
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());
|
||||
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);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -351,26 +351,19 @@ 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, command_socket);
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
|
||||
|
||||
int layer_thickness = getSettingInMicrons("layer_height");
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
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"));
|
||||
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"));
|
||||
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
@@ -446,21 +439,20 @@ 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)
|
||||
@@ -508,11 +500,10 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
|
||||
{
|
||||
polygons.add(layer->parts[partNr].outline);
|
||||
}
|
||||
if (mesh->getSettingBoolean("magic_spiralize"))
|
||||
mesh->inset0_config.spiralize = true;
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config);
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, mesh->getSettingBoolean("magic_spiralize"));
|
||||
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
@@ -531,7 +522,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, Slic
|
||||
lines.add(p);
|
||||
}
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config);
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
|
||||
|
||||
}
|
||||
|
||||
@@ -577,28 +568,16 @@ 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);
|
||||
}
|
||||
@@ -612,12 +591,14 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
|
||||
skin_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = 0;
|
||||
int64_t skin_overlap = infill_overlap;
|
||||
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("machine_nozzle_size") * 1);
|
||||
{
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
}
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
@@ -636,13 +617,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++)
|
||||
{
|
||||
Infill infill_comp(mesh->getSettingAsFillMethod("infill_pattern"), part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
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);
|
||||
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]);
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -665,61 +646,12 @@ 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], mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
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)
|
||||
@@ -727,12 +659,17 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
|
||||
if (mesh->getSettingAsCount("wall_line_count") > 0)
|
||||
{
|
||||
bool spiralize = false;
|
||||
if (mesh->getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
if (static_cast<int>(layer_nr) >= mesh->getSettingAsCount("bottom_layers"))
|
||||
mesh->inset0_config.spiralize = true;
|
||||
{
|
||||
spiralize = true;
|
||||
}
|
||||
if (static_cast<int>(layer_nr) == mesh->getSettingAsCount("bottom_layers") && part.insets.size() > 0)
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config);
|
||||
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, nullptr, EZSeamType::SHORTEST, false);
|
||||
}
|
||||
}
|
||||
for(int inset_number=part.insets.size()-1; inset_number>-1; inset_number--)
|
||||
{
|
||||
@@ -740,13 +677,13 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
{
|
||||
if (!compensate_overlap)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type, spiralize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons& outer_wall = part.insets[0];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type);
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, spiralize);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -775,15 +712,10 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
}
|
||||
Polygons* inner_skin_outline = nullptr;
|
||||
int offset_from_inner_skin_outline = 0;
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
}
|
||||
else
|
||||
if (pattern != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
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)
|
||||
@@ -792,7 +724,13 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -806,17 +744,30 @@ 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);
|
||||
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());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
Polygons perimeter_gap_lines;
|
||||
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,9 +777,9 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
return;
|
||||
|
||||
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
|
||||
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
|
||||
bool print_support_before_rest = support_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
|
||||
|
||||
@@ -841,24 +792,24 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
|
||||
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
{
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
@@ -866,17 +817,16 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
{
|
||||
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_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
|
||||
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
|
||||
|
||||
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
|
||||
|
||||
@@ -893,21 +843,25 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
{
|
||||
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;
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, false, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
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);
|
||||
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);
|
||||
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
|
||||
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,7 +893,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;
|
||||
double infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
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);
|
||||
@@ -948,9 +902,7 @@ 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);
|
||||
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
|
||||
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
@@ -987,22 +939,15 @@ 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, command_socket);
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (command_socket)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
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());
|
||||
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix);
|
||||
}
|
||||
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
|
||||
+3
-54
@@ -17,6 +17,7 @@
|
||||
#include "commandSocket.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
@@ -42,7 +43,6 @@ private:
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
GCodeExport gcode;
|
||||
CommandSocket* command_socket;
|
||||
std::ofstream output_file;
|
||||
|
||||
/*!
|
||||
@@ -59,30 +59,18 @@ private:
|
||||
public:
|
||||
FffGcodeWriter(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, layer_plan_buffer(this, command_socket, gcode)
|
||||
, layer_plan_buffer(this, 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);
|
||||
@@ -124,8 +112,6 @@ 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.
|
||||
@@ -242,43 +228,6 @@ 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.
|
||||
@@ -317,7 +266,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 addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
void addSupportLinesToGCode(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.
|
||||
|
||||
+32
-134
@@ -16,6 +16,7 @@
|
||||
#include "raft.h"
|
||||
#include "debug.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -23,9 +24,6 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
if (commandSocket)
|
||||
commandSocket->beginSendSlicedObject();
|
||||
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
@@ -38,7 +36,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, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
|
||||
|
||||
storage.model_min = meshgroup->min();
|
||||
storage.model_max = meshgroup->max();
|
||||
@@ -65,7 +63,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,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
@@ -79,18 +77,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(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
|
||||
}
|
||||
|
||||
log("Layer count: %i\n", layer_count);
|
||||
|
||||
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
@@ -117,7 +115,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
meshStorage.getSettingInMicrons("raft_base_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap");
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap")
|
||||
- meshStorage.getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer.printZ += meshStorage.getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,16 +129,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)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
CommandSocket::getInstance()->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(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processInsets(storage, layer_number);
|
||||
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
|
||||
@@ -163,21 +166,24 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
return;
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
|
||||
AreaSupport::generateSupportAreas(storage, total_layers);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
|
||||
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
if (getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
@@ -192,17 +198,12 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processSkinsAndInfill(storage, layer_number);
|
||||
}
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -222,17 +223,6 @@ 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,16 +241,6 @@ 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)
|
||||
{
|
||||
@@ -273,72 +253,6 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,17 +307,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 = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
|
||||
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
|
||||
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), 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;
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
|
||||
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)
|
||||
{
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
|
||||
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, 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"));
|
||||
@@ -413,20 +327,6 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +406,6 @@ 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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +441,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 = crossZ(p0p1);
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
|
||||
result.add(pa);
|
||||
@@ -567,7 +466,6 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "settings.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -24,26 +25,14 @@ namespace cura
|
||||
*/
|
||||
class FffPolygonGenerator : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
CommandSocket* commandSocket;
|
||||
public:
|
||||
/*!
|
||||
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
|
||||
* Basic constructor
|
||||
*/
|
||||
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.
|
||||
@@ -55,17 +44,6 @@ 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.
|
||||
@@ -101,15 +79,6 @@ 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
|
||||
|
||||
+11
-6
@@ -11,6 +11,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
|
||||
std::stringstream sstream;
|
||||
if (first_meshgroup)
|
||||
{
|
||||
sstream << getAllLocalSettingsString(); // global settings
|
||||
sstream << " -g";
|
||||
}
|
||||
else
|
||||
@@ -66,7 +67,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
|
||||
if (meshgroup->meshes.empty())
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
@@ -78,11 +79,11 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
log("starting Neith Weaver...\n");
|
||||
|
||||
Weaver w(this);
|
||||
w.weave(meshgroup, command_socket);
|
||||
w.weave(meshgroup);
|
||||
|
||||
log("starting Neith Gcode generation...\n");
|
||||
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
|
||||
gcoder.writeGCode(command_socket);
|
||||
gcoder.writeGCode();
|
||||
log("finished Neith Gcode generation...\n");
|
||||
|
||||
} else
|
||||
@@ -93,13 +94,17 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
gcode_writer.setCommandSocket(command_socket);
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
|
||||
gcode_writer.writeGCode(storage, time_keeper);
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
CommandSocket::getInstance()->sendLayerData();
|
||||
}
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
|
||||
@@ -26,7 +26,6 @@ private:
|
||||
, gcode_writer(this)
|
||||
, first_meshgroup(true)
|
||||
{
|
||||
command_socket = NULL;
|
||||
}
|
||||
public:
|
||||
static FffProcessor* getInstance()
|
||||
@@ -37,7 +36,6 @@ public:
|
||||
private:
|
||||
FffPolygonGenerator polygon_generator;
|
||||
FffGcodeWriter gcode_writer;
|
||||
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
@@ -55,13 +53,6 @@ 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);
|
||||
|
||||
@@ -14,12 +14,14 @@ void LayerPlanBuffer::flush()
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
while (!buffer.empty())
|
||||
{
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
@@ -218,10 +220,4 @@ void LayerPlanBuffer::insertPreheatCommands()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -17,8 +17,6 @@ namespace cura
|
||||
|
||||
class LayerPlanBuffer : SettingsMessenger
|
||||
{
|
||||
CommandSocket* command_socket;
|
||||
|
||||
GCodeExport& gcode;
|
||||
|
||||
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
|
||||
@@ -29,9 +27,8 @@ class LayerPlanBuffer : SettingsMessenger
|
||||
public:
|
||||
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, CommandSocket* command_socket, GCodeExport& gcode)
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
, command_socket(command_socket)
|
||||
, gcode(gcode)
|
||||
{ }
|
||||
|
||||
@@ -55,8 +52,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 (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
buffer.pop_front();
|
||||
}
|
||||
return buffer.back();
|
||||
|
||||
+113
-38
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <algorithm> // min
|
||||
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -12,6 +14,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -51,64 +54,136 @@ 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& 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& resulting_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) 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;
|
||||
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();
|
||||
|
||||
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 < 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
|
||||
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
|
||||
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
|
||||
|
||||
// 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!
|
||||
}
|
||||
|
||||
first_middle = (use_second_middle_as_first)?
|
||||
second_middle :
|
||||
(a + b) / 2;
|
||||
second_middle = (c + d) / 2;
|
||||
|
||||
Point dir_vector_perp = crossZ(second_middle - first_middle);
|
||||
Point dir_vector_perp = turn90CCW(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
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,24 +12,42 @@ 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 line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \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(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
|
||||
|
||||
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);
|
||||
|
||||
/*!
|
||||
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
|
||||
*
|
||||
@@ -43,8 +61,8 @@ public:
|
||||
/*!
|
||||
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
|
||||
*/
|
||||
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) { }
|
||||
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) { }
|
||||
|
||||
/*!
|
||||
* Check for lots of small moves and combine them into one large line.
|
||||
@@ -60,6 +78,21 @@ 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
|
||||
|
||||
+40
-7
@@ -64,26 +64,38 @@ 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;
|
||||
}
|
||||
//Read the face count
|
||||
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
|
||||
|
||||
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)
|
||||
{
|
||||
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(faceCount);
|
||||
mesh->vertices.reserve(faceCount);
|
||||
for(unsigned int i=0;i<faceCount;i++)
|
||||
mesh->faces.reserve(face_count);
|
||||
mesh->vertices.reserve(face_count);
|
||||
for (unsigned int i = 0; i < face_count; i++)
|
||||
{
|
||||
if (fread(buffer, 50, 1, f) != 1)
|
||||
{
|
||||
@@ -105,10 +117,31 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
char buffer[6];
|
||||
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 (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
|
||||
+27
-15
@@ -5,6 +5,7 @@
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "infill.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -22,7 +23,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], "SUPPORT");// so that visualization in the old Cura still works (TODO)
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
|
||||
}
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
@@ -55,13 +56,14 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
{ // compute max_object_height_per_extruder
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
|
||||
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
|
||||
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
max_object_height_per_extruder[extr_nr] =
|
||||
std::max( max_object_height_per_extruder[extr_nr]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
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]
|
||||
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]
|
||||
, 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] =
|
||||
@@ -168,7 +170,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
|
||||
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
|
||||
|
||||
generateGroundpoly(storage);
|
||||
|
||||
@@ -179,14 +181,22 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
std::vector<Polygons>& patterns = patterns_per_extruder.back();
|
||||
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
|
||||
{
|
||||
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
@@ -208,10 +218,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, command_socket);
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, 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)
|
||||
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)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
@@ -227,12 +237,14 @@ 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);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
|
||||
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
if (command_socket)
|
||||
command_socket->sendPolygons(SupportType, layer_nr, pattern, config.getLineWidth());
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
|
||||
}
|
||||
|
||||
if (wipe)
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
@@ -240,7 +252,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, CommandSocket* command_socket)
|
||||
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)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
|
||||
+3
-3
@@ -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, 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);
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
+12
-13
@@ -4,24 +4,23 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class EPrintFeature : unsigned int // unused!!
|
||||
{ // TODO: use in gcodePathConfigs ?
|
||||
OUTER_WALL,
|
||||
INNER_WALLS,
|
||||
INFILL,
|
||||
SKIN,
|
||||
HELPERS,
|
||||
UNCLASSIFIED,
|
||||
ENUM_COUNT
|
||||
enum class PrintFeatureType
|
||||
{
|
||||
NoneType, // unused, but libArcus depends on it
|
||||
OuterWall,
|
||||
InnerWall,
|
||||
Skin,
|
||||
Support,
|
||||
Skirt,
|
||||
Infill,
|
||||
SupportInfill,
|
||||
MoveCombing,
|
||||
MoveRetraction
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PRINT_FEATURE
|
||||
+6
-6
@@ -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, CommandSocket* command_socket)
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
|
||||
{
|
||||
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
|
||||
if (command_socket)
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
command_socket->sendProgress(percentage);
|
||||
CommandSocket::getInstance()->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, CommandSocket* command_socket)
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
|
||||
{
|
||||
if (command_socket)
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
command_socket->sendProgressStage(stage);
|
||||
CommandSocket::getInstance()->sendProgressStage(stage);
|
||||
}
|
||||
|
||||
if (time_keeper)
|
||||
|
||||
+3
-5
@@ -52,22 +52,20 @@ private:
|
||||
public:
|
||||
static void init(); //!< Initialize some values needed in a fast computation of the progress
|
||||
/*!
|
||||
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
* Message progress over the 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, CommandSocket* commandSocket);
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
|
||||
/*!
|
||||
* 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, CommandSocket* commandSocket);
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#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
|
||||
+10
-9
@@ -6,11 +6,12 @@
|
||||
|
||||
#include "Progress.h"
|
||||
#include "weaveDataStorage.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
void Weaver::weave(MeshGroup* meshgroup)
|
||||
{
|
||||
wireFrame.meshgroup = meshgroup;
|
||||
|
||||
@@ -52,8 +53,8 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
|
||||
|
||||
if (slicerList.empty()) //Wait, there is nothing to slice.
|
||||
{
|
||||
@@ -70,10 +71,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
else
|
||||
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
|
||||
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
Polygons parts1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
@@ -84,8 +85,8 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
|
||||
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
|
||||
|
||||
if (chainified.size() > 0)
|
||||
{
|
||||
@@ -107,10 +108,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
|
||||
+1
-2
@@ -61,9 +61,8 @@ 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, CommandSocket* commandSocket);
|
||||
void weave(MeshGroup* objects);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
+16
-22
@@ -12,15 +12,15 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
void Wireframe2gcode::writeGCode()
|
||||
{
|
||||
|
||||
gcode.preSetup(wireFrame.meshgroup);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->beginGCode();
|
||||
if (CommandSocket::getInstance())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
|
||||
processStartingCode(commandSocket);
|
||||
processStartingCode();
|
||||
|
||||
int maxObjectHeight;
|
||||
if (wireFrame.layers.empty())
|
||||
@@ -32,7 +32,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
}
|
||||
|
||||
processSkirt(commandSocket);
|
||||
processSkirt();
|
||||
|
||||
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
@@ -76,10 +76,10 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
@@ -166,12 +166,6 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
finalize();
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
commandSocket->sendGCodeLayer();
|
||||
commandSocket->endSendSlicedObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -550,13 +544,13 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
|
||||
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
}
|
||||
|
||||
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
void Wireframe2gcode::processStartingCode()
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (!command_socket)
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
gcode.writeCode(gcode.getFileHeader().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -595,7 +589,7 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
void Wireframe2gcode::processSkirt()
|
||||
{
|
||||
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
|
||||
{
|
||||
@@ -606,14 +600,14 @@ void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
order.addPolygons(skirt);
|
||||
order.optimize();
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
|
||||
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
|
||||
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
|
||||
|
||||
void writeGCode(CommandSocket* commandSocket);
|
||||
void writeGCode();
|
||||
|
||||
|
||||
private:
|
||||
@@ -80,12 +80,12 @@ private:
|
||||
/*!
|
||||
* Startup gcode: nozzle temp up, retraction settings, bed temp
|
||||
*/
|
||||
void processStartingCode(CommandSocket* command_socket);
|
||||
void processStartingCode();
|
||||
|
||||
/*!
|
||||
* Lay down a skirt
|
||||
*/
|
||||
void processSkirt(CommandSocket* commandSocket);
|
||||
void processSkirt();
|
||||
|
||||
/*!
|
||||
* End gcode: nozzle temp down
|
||||
|
||||
@@ -385,6 +385,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
|
||||
}
|
||||
}
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+160
-150
@@ -6,9 +6,9 @@
|
||||
#include <thread>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef ARCUS
|
||||
#include <Arcus/Socket.h>
|
||||
#endif
|
||||
#include <Arcus/SocketListener.h>
|
||||
#include <Arcus/Error.h>
|
||||
|
||||
#include <string> // stoi
|
||||
|
||||
@@ -25,32 +25,50 @@ namespace cura {
|
||||
#define FLOATS_PER_VECTOR 3
|
||||
#define VECTORS_PER_FACE 3
|
||||
|
||||
#ifdef ARCUS
|
||||
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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{ }
|
||||
|
||||
cura::proto::Layer* getLayerById(int id);
|
||||
std::shared_ptr<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;
|
||||
|
||||
@@ -58,74 +76,70 @@ 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()
|
||||
#ifdef ARCUS
|
||||
: d(new Private)
|
||||
#endif
|
||||
: private_data(new Private)
|
||||
{
|
||||
#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)
|
||||
{
|
||||
#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 = new Arcus::Socket();
|
||||
private_data->socket->addListener(new Listener());
|
||||
|
||||
d->socket->connect(ip, port);
|
||||
//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);
|
||||
|
||||
bool slice_another_time = true;
|
||||
|
||||
// Start & continue listening as long as socket is not closed and there is no error.
|
||||
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != 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 = d->socket->takeNextMessage();
|
||||
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
|
||||
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
|
||||
if(setting_list)
|
||||
{
|
||||
@@ -142,33 +156,57 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
if(slice)
|
||||
{
|
||||
// Reset object counts
|
||||
d->object_count = 0;
|
||||
d->object_ids.clear();
|
||||
private_data->object_count = 0;
|
||||
for(auto object : slice->object_lists())
|
||||
{
|
||||
handleObjectList(&object);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
if(!d->socket->errorString().empty())
|
||||
//If there is an object to slice, do so.
|
||||
if(private_data->objects_to_slice.size())
|
||||
{
|
||||
logError("%s\n", d->socket->errorString().data());
|
||||
d->socket->clearError();
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
|
||||
log("Closing connection\n");
|
||||
private_data->socket->close();
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
if(list->objects_size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
//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();
|
||||
//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();
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
@@ -182,6 +220,14 @@ 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())
|
||||
@@ -197,8 +243,6 @@ 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
|
||||
@@ -225,11 +269,10 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
d->object_ids.push_back(object.id());
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
d->object_count++;
|
||||
private_data->object_count++;
|
||||
meshgroup->finalize();
|
||||
}
|
||||
|
||||
@@ -240,54 +283,39 @@ 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)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->current_sliced_object)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->current_sliced_object)
|
||||
return;
|
||||
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
cura::proto::Polygon* p = layer->add_polygons();
|
||||
cura::proto::Polygon* p = proto_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 /= d->object_count;
|
||||
amount += d->sliced_objects * (1. / d->object_count);
|
||||
amount /= private_data->object_count;
|
||||
amount += private_data->sliced_objects * (1. / private_data->object_count);
|
||||
message->set_amount(amount);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
@@ -297,12 +325,10 @@ 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));
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
@@ -314,90 +340,74 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
|
||||
// socket.sendFloat32(print_time);
|
||||
}
|
||||
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
void CommandSocket::sendLayerData()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!d->sliced_object_list)
|
||||
{
|
||||
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
|
||||
}
|
||||
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.");
|
||||
|
||||
d->current_sliced_object = d->sliced_object_list->add_objects();
|
||||
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
|
||||
#endif
|
||||
if(private_data->sliced_objects >= private_data->object_count)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
void CommandSocket::sendFinishedSlicing()
|
||||
{
|
||||
#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
|
||||
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
|
||||
#endif
|
||||
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodeLayer()
|
||||
void CommandSocket::flushGcode()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodeLayer>();
|
||||
message->set_id(d->object_ids[0]);
|
||||
message->set_data(d->gcode_output_stream.str());
|
||||
d->socket->sendMessage(message);
|
||||
message->set_data(private_data->gcode_output_stream.str());
|
||||
private_data->socket->sendMessage(message);
|
||||
|
||||
d->gcode_output_stream.str("");
|
||||
#endif
|
||||
private_data->gcode_output_stream.str("");
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
id += current_layer_offset;
|
||||
|
||||
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; });
|
||||
auto itr = sliced_layers.find(id);
|
||||
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
if(itr != current_sliced_object->mutable_layers()->end())
|
||||
std::shared_ptr<cura::proto::Layer> layer;
|
||||
if(itr != sliced_layers.end())
|
||||
{
|
||||
layer = &(*itr);
|
||||
layer = itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer = std::make_shared<cura::proto::Layer>();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
sliced_layers[id] = layer;
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+44
-17
@@ -5,20 +5,29 @@
|
||||
#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:
|
||||
CommandSocket();
|
||||
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
|
||||
|
||||
/*!
|
||||
* Connect with the GUI
|
||||
* This creates and initialises the arcus socket and then continues listening for messages.
|
||||
@@ -26,8 +35,7 @@ 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
|
||||
@@ -39,18 +47,22 @@ public:
|
||||
* This simply sets all the settings by using key value pair
|
||||
*/
|
||||
void handleSettingList(cura::proto::SettingList* list);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Does nothing at the moment
|
||||
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
|
||||
*/
|
||||
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::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
|
||||
*/
|
||||
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send progress to GUI
|
||||
*/
|
||||
@@ -70,19 +82,34 @@ 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();
|
||||
|
||||
void beginSendSlicedObject();
|
||||
void endSendSlicedObject();
|
||||
/*!
|
||||
* \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 beginGCode();
|
||||
void sendGCodeLayer();
|
||||
|
||||
/*!
|
||||
* Flush the gcode in gcode_output_stream into a message queued in the socket.
|
||||
*/
|
||||
void flushGcode();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
#ifdef ARCUS
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> d;
|
||||
#endif
|
||||
const std::unique_ptr<Private> private_data;
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+147
-68
@@ -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,8 +29,24 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
|
||||
commandSocket = commandSocket_;
|
||||
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_) {
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
|
||||
@@ -40,6 +56,11 @@ 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;
|
||||
@@ -134,6 +155,42 @@ 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)
|
||||
{
|
||||
@@ -177,33 +234,68 @@ void GCodeExport::writeComment(std::string comment)
|
||||
*output_stream << comment[i];
|
||||
}
|
||||
}
|
||||
*output_stream << "\n";
|
||||
*output_stream << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
{
|
||||
*output_stream << ";TYPE:" << type << "\n";
|
||||
*output_stream << ";TYPE:" << type << new_line;
|
||||
}
|
||||
|
||||
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 << "\n";
|
||||
*output_stream << ";LAYER:" << layer_nr << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeLayerCountComment(int layer_count)
|
||||
{
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << "\n";
|
||||
*output_stream << line << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::resetExtrusionValue()
|
||||
{
|
||||
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
|
||||
{
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
|
||||
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)
|
||||
@@ -217,7 +309,7 @@ void GCodeExport::resetExtrusionValue()
|
||||
|
||||
void GCodeExport::writeDelay(double timeAmount)
|
||||
{
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
|
||||
estimateCalculator.addTime(timeAmount);
|
||||
}
|
||||
|
||||
@@ -233,11 +325,7 @@ 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 = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
@@ -254,11 +342,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 << "\r\n";
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
|
||||
currentSpeed = double(rpm);
|
||||
}
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
|
||||
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.
|
||||
@@ -275,17 +363,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\r\n";
|
||||
*output_stream << "M103" << new_line;
|
||||
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 << "\r\n";
|
||||
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
|
||||
@@ -308,11 +396,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
return;
|
||||
}
|
||||
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
@@ -321,30 +405,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) << "\n";
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
|
||||
isZHopped = 0;
|
||||
}
|
||||
double prime_volume = extruder_attr[current_extruder].prime_volume;
|
||||
current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
|
||||
current_e_value += mm3ToE(prime_volume);
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
if (firmware_retract)
|
||||
{ // note that BFB is handled differently
|
||||
*output_stream << "G11\n";
|
||||
*output_stream << "G11" << new_line;
|
||||
//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 << "\n";
|
||||
*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;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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 << "\n";
|
||||
*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;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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.
|
||||
{
|
||||
@@ -354,10 +438,9 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
}
|
||||
else if (prime_volume > 0.0)
|
||||
{
|
||||
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";
|
||||
*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;
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
|
||||
}
|
||||
extruder_attr[current_extruder].prime_volume = 0.0;
|
||||
current_e_value += extrusion_per_mm * diff.vSizeMM();
|
||||
@@ -366,16 +449,16 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
else
|
||||
{
|
||||
*output_stream << "G0";
|
||||
|
||||
if (commandSocket)
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// 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->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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
@@ -391,10 +474,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 << "\n";
|
||||
*output_stream << new_line;
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
@@ -403,7 +486,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -415,7 +498,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
|
||||
@@ -425,13 +508,13 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1
|
||||
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
|
||||
&& 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)
|
||||
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
|
||||
{
|
||||
extruded_volume_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
@@ -439,19 +522,19 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
|
||||
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
|
||||
|
||||
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
double retraction_e_amount = mmToE(config->distance);
|
||||
if (firmware_retract)
|
||||
{
|
||||
*output_stream << "G10\n";
|
||||
*output_stream << "G10" << new_line;
|
||||
//Assume default UM2 retraction settings.
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(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 << "\n";
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
|
||||
}
|
||||
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
|
||||
@@ -460,7 +543,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) << "\n";
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,13 +552,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
*output_stream << "M103\r\n";
|
||||
*output_stream << "M103" << new_line;
|
||||
|
||||
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 = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
double retraction_e_amount = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
|
||||
{
|
||||
return;
|
||||
@@ -491,13 +574,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
|
||||
{
|
||||
return;
|
||||
}
|
||||
*output_stream << "G10 S1\n";
|
||||
*output_stream << "G10 S1" << new_line;
|
||||
}
|
||||
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 << "\n";
|
||||
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
// 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;
|
||||
@@ -525,11 +608,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 << "\n";
|
||||
*output_stream << "M135 T" << current_extruder << new_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
*output_stream << "T" << current_extruder << "\n";
|
||||
*output_stream << "T" << current_extruder << new_line;
|
||||
}
|
||||
writeCode(extruder_attr[new_extruder].start_code.c_str());
|
||||
|
||||
@@ -539,11 +622,7 @@ void GCodeExport::switchExtruder(int new_extruder)
|
||||
|
||||
void GCodeExport::writeCode(const char* str)
|
||||
{
|
||||
*output_stream << str;
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
*output_stream << "\r\n";
|
||||
else
|
||||
*output_stream << "\n";
|
||||
*output_stream << str << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::writeFanCommand(double speed)
|
||||
@@ -553,16 +632,16 @@ void GCodeExport::writeFanCommand(double speed)
|
||||
if (speed > 0)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
else
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M127 T0\n";
|
||||
*output_stream << "M127 T0" << new_line;
|
||||
else
|
||||
*output_stream << "M107\n";
|
||||
*output_stream << "M107" << new_line;
|
||||
}
|
||||
currentFanSpeed = speed;
|
||||
}
|
||||
@@ -578,7 +657,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
|
||||
*output_stream << "M104";
|
||||
if (extruder != current_extruder)
|
||||
*output_stream << " T" << extruder;
|
||||
*output_stream << " S" << temperature << "\n";
|
||||
*output_stream << " S" << temperature << new_line;
|
||||
extruder_attr[extruder].currentTemperature = temperature;
|
||||
}
|
||||
|
||||
@@ -588,7 +667,7 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
|
||||
*output_stream << "M190 S";
|
||||
else
|
||||
*output_stream << "M140 S";
|
||||
*output_stream << temperature << "\n";
|
||||
*output_stream << temperature << new_line;
|
||||
}
|
||||
|
||||
void GCodeExport::finalize(double moveSpeed, const char* endCode)
|
||||
|
||||
+102
-26
@@ -18,13 +18,9 @@ namespace cura {
|
||||
struct CoastingConfig
|
||||
{
|
||||
bool coasting_enable;
|
||||
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;
|
||||
double coasting_volume;
|
||||
double coasting_speed;
|
||||
double coasting_min_volume;
|
||||
};
|
||||
|
||||
class RetractionConfig
|
||||
@@ -44,19 +40,25 @@ public:
|
||||
class GCodePathConfig
|
||||
{
|
||||
private:
|
||||
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.)
|
||||
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
|
||||
double speed; //!< 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:
|
||||
const char* name; //!< name of the feature type
|
||||
bool spiralize;
|
||||
PrintFeatureType type; //!< name of the feature type
|
||||
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, 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) {}
|
||||
|
||||
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type)
|
||||
: speed_iconic(0)
|
||||
, speed(0)
|
||||
, line_width(0)
|
||||
, extrusion_mm3_per_mm(0.0)
|
||||
, type(type)
|
||||
, retraction_config(retraction_config)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize some of the member variables.
|
||||
@@ -65,8 +67,8 @@ public:
|
||||
*/
|
||||
void init(double speed, int line_width, double flow)
|
||||
{
|
||||
speed_base = speed;
|
||||
this->speed_current = speed;
|
||||
speed_iconic = speed;
|
||||
this->speed = speed;
|
||||
this->line_width = line_width;
|
||||
this->flow = flow;
|
||||
}
|
||||
@@ -91,7 +93,15 @@ public:
|
||||
*/
|
||||
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
|
||||
{
|
||||
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/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;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -107,7 +117,7 @@ public:
|
||||
*/
|
||||
double getSpeed()
|
||||
{
|
||||
return speed_current;
|
||||
return speed;
|
||||
}
|
||||
|
||||
int getLineWidth()
|
||||
@@ -120,6 +130,11 @@ public:
|
||||
return line_width == 0;
|
||||
}
|
||||
|
||||
double getFlowPercentage()
|
||||
{
|
||||
return flow;
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateExtrusion()
|
||||
{
|
||||
@@ -134,6 +149,7 @@ 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;
|
||||
@@ -169,13 +185,15 @@ private:
|
||||
, retraction_e_amount_current(0.0)
|
||||
, retraction_e_amount_at_e_start(0.0)
|
||||
, prime_volume(0.0)
|
||||
, last_retraction_prime_speed(1.0)
|
||||
, last_retraction_prime_speed(0.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
|
||||
@@ -191,19 +209,64 @@ 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();
|
||||
|
||||
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* 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 setOutputStream(std::ostream* stream);
|
||||
|
||||
|
||||
int getNozzleSize(int extruder_idx);
|
||||
|
||||
Point getExtruderOffset(int id);
|
||||
|
||||
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
|
||||
@@ -229,7 +292,7 @@ public:
|
||||
void setFilamentDiameter(unsigned int n, int diameter);
|
||||
|
||||
double getCurrentExtrudedVolume();
|
||||
|
||||
|
||||
double getTotalFilamentUsed(int e);
|
||||
|
||||
double getTotalPrintTime();
|
||||
@@ -238,6 +301,7 @@ 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);
|
||||
|
||||
@@ -282,6 +346,7 @@ 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");
|
||||
@@ -290,10 +355,21 @@ 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);
|
||||
|
||||
|
||||
+175
-132
@@ -22,17 +22,19 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
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];
|
||||
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow) // spiralize can only change when a travel path is in between
|
||||
return &paths.back();
|
||||
paths.emplace_back();
|
||||
GCodePath* ret = &paths.back();
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->done = false;
|
||||
ret->flow = flow;
|
||||
ret->spiralize = spiralize;
|
||||
ret->space_fill_type = space_fill_type;
|
||||
if (config != &storage.travel_config)
|
||||
{
|
||||
last_retraction_config = config->retraction_config;
|
||||
@@ -47,9 +49,8 @@ void GCodePlanner::forceNewPathStart()
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
: storage(storage)
|
||||
, commandSocket(commandSocket)
|
||||
, layer_nr(layer_nr)
|
||||
, z(z)
|
||||
, layer_thickness(layer_thickness)
|
||||
@@ -105,7 +106,6 @@ void GCodePlanner::setIsInside(bool _is_inside)
|
||||
is_inside = _is_inside;
|
||||
}
|
||||
|
||||
|
||||
bool GCodePlanner::setExtruder(int extruder)
|
||||
{
|
||||
if (extruder == extruder_plans.back().extruder)
|
||||
@@ -115,6 +115,7 @@ 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,25 +123,18 @@ 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
|
||||
}
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -148,7 +142,6 @@ 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;
|
||||
@@ -158,13 +151,13 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
|
||||
void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
{
|
||||
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.
|
||||
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.
|
||||
// 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_dist) != NO_INDEX)
|
||||
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
|
||||
{
|
||||
//Move inside again, so we move out of tight 90deg corners
|
||||
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
|
||||
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2);
|
||||
if (comb_boundary_inside.inside(p))
|
||||
{
|
||||
addTravel_simple(p);
|
||||
@@ -187,21 +180,29 @@ void GCodePlanner::addTravel(Point p)
|
||||
if (combed)
|
||||
{
|
||||
bool retract = combPaths.size() > 1;
|
||||
if (!retract)
|
||||
{ // check whether we want to retract
|
||||
if (!retract && combPaths.size() == 1 && combPaths[0].throughAir && combPaths[0].size() > 2)
|
||||
{ // retract when avoiding obstacles through air
|
||||
retract = true;
|
||||
}
|
||||
|
||||
for (unsigned int path_idx = 0; path_idx < combPaths.size() && !retract; path_idx++)
|
||||
for (CombPath& combPath : combPaths)
|
||||
{ // retract when path moves through a boundary
|
||||
if (combPaths[path_idx].cross_boundary) { retract = true; }
|
||||
if (combPath.cross_boundary || combPath.throughAir)
|
||||
{
|
||||
retract = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
@@ -215,7 +216,7 @@ void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path->retract = retract;
|
||||
for (Point& combPoint : combPath)
|
||||
{
|
||||
@@ -231,13 +232,13 @@ void GCodePlanner::addTravel(Point p)
|
||||
// no combing? always retract!
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
if (was_inside)
|
||||
{
|
||||
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.
|
||||
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
|
||||
assert (extr != nullptr);
|
||||
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
|
||||
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
}
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
@@ -250,71 +251,81 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
|
||||
{
|
||||
if (path == nullptr)
|
||||
{
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, float flow)
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
{
|
||||
getLatestPathWithConfig(config, flow)->points.push_back(p);
|
||||
getLatestPathWithConfig(config, space_fill_type, flow, spiralize)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation)
|
||||
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, bool spiralize)
|
||||
{
|
||||
Point p0 = polygon[startIdx];
|
||||
addTravel(p0);
|
||||
for(unsigned int i=1; i<polygon.size(); i++)
|
||||
{
|
||||
Point p1 = polygon[(startIdx + i) % polygon.size()];
|
||||
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
|
||||
p0 = p1;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
{
|
||||
Point& p1 = polygon[startIdx];
|
||||
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning("WARNING: line added as polygon! (gcodePlanner)\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type)
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, bool spiralize)
|
||||
{
|
||||
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
if (polygons.size() == 0)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config, wall_overlap_computation);
|
||||
return;
|
||||
}
|
||||
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
orderOptimizer.addPolygon(polygons[poly_idx]);
|
||||
}
|
||||
orderOptimizer.optimize();
|
||||
for (unsigned int poly_idx : orderOptimizer.polyOrder)
|
||||
{
|
||||
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, spiralize);
|
||||
}
|
||||
}
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist)
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
|
||||
{
|
||||
LineOrderOptimizer orderOptimizer(lastPosition);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
// addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
PolygonRef polygon = polygons[nr];
|
||||
int start = orderOptimizer.polyStart[nr];
|
||||
orderOptimizer.addPolygon(polygons[line_idx]);
|
||||
}
|
||||
orderOptimizer.optimize();
|
||||
for (int poly_idx : orderOptimizer.polyOrder)
|
||||
{
|
||||
// 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 end = 1 - start;
|
||||
Point& p0 = polygon[start];
|
||||
addTravel(p0);
|
||||
Point& p1 = polygon[end];
|
||||
addExtrusionMove(p1, config);
|
||||
addExtrusionMove(p1, config, space_fill_type);
|
||||
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, 0.0);
|
||||
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, space_fill_type, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,7 +452,21 @@ 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;
|
||||
@@ -452,8 +477,25 @@ 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
|
||||
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);
|
||||
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;
|
||||
}
|
||||
/*
|
||||
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.
|
||||
@@ -466,7 +508,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
{
|
||||
completeConfigs();
|
||||
|
||||
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
|
||||
gcode.setLayerNr(layer_nr);
|
||||
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
|
||||
@@ -502,7 +544,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->name);
|
||||
gcode.writeTypeComment(path.config->type);
|
||||
last_extrusion_config = path.config;
|
||||
}
|
||||
double speed = path.config->getSpeed();
|
||||
@@ -511,10 +553,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!
|
||||
|
||||
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
|
||||
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 !!
|
||||
{ // !! has effect on path_idx !!
|
||||
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
|
||||
continue;
|
||||
@@ -525,33 +567,27 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
if (point_idx == path.points.size() - 1)
|
||||
{
|
||||
gcode.setZ(z); // go down to extrusion level when we spiralized before on this layer
|
||||
gcode.writeMove(gcode.getPositionXY(), speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool spiralize = path.config->spiralize;
|
||||
if (spiralize)
|
||||
{
|
||||
//Check if we are the last spiralize path in the list, if not, do not spiralize.
|
||||
for(unsigned int m=path_idx+1; m<paths.size(); m++)
|
||||
{
|
||||
if (paths[m].config->spiralize)
|
||||
spiralize = false;
|
||||
}
|
||||
}
|
||||
bool spiralize = path.spiralize;
|
||||
if (!spiralize) // normal (extrusion) move (with coasting
|
||||
{
|
||||
{
|
||||
CoastingConfig& coasting_config = storage.coasting_config[extruder];
|
||||
bool coasting = coasting_config.coasting_enable;
|
||||
if (coasting)
|
||||
{
|
||||
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);
|
||||
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
|
||||
}
|
||||
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
|
||||
{ // normal path to gcode algorithm
|
||||
if ( // change |||||| to /\/\/\/\/ ...
|
||||
if ( // change infill |||||| to /\/\/\/\/ ...
|
||||
false &&
|
||||
path_idx + 2 < paths.size() // has a next move
|
||||
&& paths[path_idx+1].points.size() == 1 // is single extruded line
|
||||
@@ -562,6 +598,7 @@ 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;
|
||||
}
|
||||
@@ -569,6 +606,7 @@ 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());
|
||||
}
|
||||
}
|
||||
@@ -578,25 +616,34 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
{ // SPIRALIZE
|
||||
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
|
||||
float totalLength = 0.0;
|
||||
int z = gcode.getPositionZ();
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path.points.size(); i++)
|
||||
for (unsigned int _path_idx = path_idx; _path_idx < paths.size() && !paths[_path_idx].isTravelPath(); _path_idx++)
|
||||
{
|
||||
Point p1 = path.points[i];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
GCodePath& _path = paths[_path_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < _path.points.size(); point_idx++)
|
||||
{
|
||||
Point p1 = _path.points[point_idx];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
float length = 0.0;
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
Point p1 = path.points[point_idx];
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
for (; path_idx < paths.size() && paths[path_idx].spiralize; path_idx++)
|
||||
{ // handle all consecutive spiralized paths > CHANGES path_idx!
|
||||
GCodePath& path = paths[path_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
Point p1 = path.points[point_idx];
|
||||
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());
|
||||
}
|
||||
}
|
||||
path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,10 +674,9 @@ 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);
|
||||
@@ -645,26 +691,39 @@ void GCodePlanner::completeConfigs()
|
||||
|
||||
void GCodePlanner::processInitialLayersSpeedup()
|
||||
{
|
||||
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
int 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);
|
||||
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
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)
|
||||
@@ -735,8 +794,12 @@ 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_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
|
||||
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)
|
||||
{
|
||||
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()
|
||||
@@ -748,21 +811,6 @@ 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
|
||||
|
||||
@@ -770,7 +818,8 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
|
||||
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) / 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
|
||||
|
||||
|
||||
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...)
|
||||
@@ -823,14 +872,11 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acc_dist_idx_gt_coast_dist == NO_INDEX)
|
||||
{ // something has gone wrong; coasting_min_dist < coasting_dist ?
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
assert (acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size()); // something has gone wrong; coasting_min_dist < coasting_dist ?
|
||||
|
||||
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];
|
||||
@@ -838,27 +884,24 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
|
||||
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());
|
||||
}
|
||||
|
||||
if (path_next.retract)
|
||||
{
|
||||
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
|
||||
}
|
||||
|
||||
|
||||
// write coasting path
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+76
-36
@@ -10,6 +10,7 @@
|
||||
#include "wallOverlap.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "SpaceFillType.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
@@ -150,11 +151,14 @@ 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.
|
||||
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
|
||||
|
||||
|
||||
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
|
||||
|
||||
TimeMaterialEstimates estimates; //!< Naive time and material estimates
|
||||
|
||||
bool isTravelPath()
|
||||
@@ -169,6 +173,15 @@ 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
|
||||
@@ -236,8 +249,6 @@ class GCodePlanner : public NoCopy
|
||||
private:
|
||||
SliceDataStorage& storage;
|
||||
|
||||
CommandSocket* commandSocket;
|
||||
|
||||
int layer_nr;
|
||||
|
||||
int z;
|
||||
@@ -272,10 +283,12 @@ 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
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
* \return A path with the given config which is now the last path in GCodePlanner::paths
|
||||
*/
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
|
||||
@@ -295,7 +308,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(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(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:
|
||||
@@ -316,6 +329,22 @@ 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.
|
||||
*
|
||||
@@ -374,20 +403,53 @@ public:
|
||||
* \param path (optional) The travel path to which to add the point \p p
|
||||
*/
|
||||
void addTravel_simple(Point p, GCodePath* path = nullptr);
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
|
||||
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = 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
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
*/
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST);
|
||||
/*!
|
||||
* Add polygon to the gcode starting at vertex \p startIdx
|
||||
* \param polygon The polygon
|
||||
* \param startIdx The index of the starting vertex of the \p polygon
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
|
||||
*/
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add polygons to the gcode with optimized order.
|
||||
*
|
||||
* When \p spiralize is true, each polygon will gradually increase from a z corresponding to this layer to the z corresponding to the next layer.
|
||||
* Doing this for each polygon means there is a chance for the print head to crash into already printed parts,
|
||||
* but doing it for the last polygon only would mean you are printing half of the layer in non-spiralize mode,
|
||||
* while each layer starts with a different part.
|
||||
* Two towers would result in alternating spiralize and non-spiralize layers.
|
||||
*
|
||||
* \param polygons The polygons
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param z_seam_type The seam type / poly start optimizer
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
|
||||
*/
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* 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, int wipe_dist = 0);
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
|
||||
|
||||
/*!
|
||||
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
|
||||
@@ -438,35 +500,13 @@ 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_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.
|
||||
* \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.
|
||||
* \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_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
|
||||
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);
|
||||
|
||||
/*!
|
||||
* 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
|
||||
|
||||
+177
-345
@@ -2,7 +2,6 @@
|
||||
#include "infill.h"
|
||||
#include "functional"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
@@ -16,36 +15,28 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
switch(pattern)
|
||||
{
|
||||
case EFillMethod::GRID:
|
||||
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
|
||||
generateGridInfill(result_lines);
|
||||
break;
|
||||
case EFillMethod::LINES:
|
||||
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
|
||||
generateLineInfill(result_lines, line_distance, fill_angle);
|
||||
break;
|
||||
case EFillMethod::TRIANGLES:
|
||||
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
|
||||
generateTriangleInfill(result_lines);
|
||||
break;
|
||||
case EFillMethod::CONCENTRIC:
|
||||
if (outlineOffset != 0)
|
||||
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)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
|
||||
outline = &outline_offsetted;
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
|
||||
}
|
||||
if (abs(extrusion_width - line_distance) < 10)
|
||||
{
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
generateConcentricInfill(*outline, result_polygons, line_distance);
|
||||
}
|
||||
break;
|
||||
case EFillMethod::ZIG_ZAG:
|
||||
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);
|
||||
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
|
||||
break;
|
||||
default:
|
||||
logError("Fill pattern has unknown value.\n");
|
||||
@@ -53,9 +44,9 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
|
||||
|
||||
|
||||
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -65,13 +56,13 @@ void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons*
|
||||
result.add(r);
|
||||
}
|
||||
Polygons next_outline;
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
outline = next_outline;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -85,375 +76,216 @@ void generateConcentricInfill(Polygons outline, Polygons& result, int inset_valu
|
||||
}
|
||||
|
||||
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
void Infill::generateGridInfill(Polygons& result)
|
||||
{
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 90);
|
||||
generateLineInfill(result, line_distance * 2, fill_angle);
|
||||
generateLineInfill(result, line_distance * 2, fill_angle + 90);
|
||||
}
|
||||
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
void Infill::generateTriangleInfill(Polygons& result)
|
||||
{
|
||||
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);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 60);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 120);
|
||||
}
|
||||
|
||||
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
|
||||
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
|
||||
{
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
p.add(rotation_matrix.unapply(from));
|
||||
p.add(rotation_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 * lineSpacing; x < boundary.max.X; x += lineSpacing)
|
||||
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
|
||||
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
|
||||
{ // segment is too short to create infill
|
||||
continue;
|
||||
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
|
||||
}
|
||||
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
|
||||
}
|
||||
scanline_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
|
||||
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
|
||||
{
|
||||
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;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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 poly_idx=0; poly_idx < outline.size(); poly_idx++)
|
||||
|
||||
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)
|
||||
{
|
||||
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
|
||||
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
|
||||
if (connected_zigzags)
|
||||
{
|
||||
Point p1 = outline[poly_idx][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
|
||||
|
||||
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);
|
||||
}
|
||||
p0 = p1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
else
|
||||
{
|
||||
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++)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
|
||||
|
||||
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++)
|
||||
{
|
||||
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 p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
lastPoint = p1;
|
||||
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.
|
||||
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 scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
|
||||
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
|
||||
// otherwise only 1 will be created, counting as an actual intersection
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
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)
|
||||
}
|
||||
else
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
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 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (isEvenScanSegment)
|
||||
addLine(lastPoint, p1);
|
||||
else if (connect_zigzags)
|
||||
unevenBoundarySegment.push_back(p1);
|
||||
}
|
||||
|
||||
lastPoint = p1;
|
||||
zigzag_connector_processor.registerVertex(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);
|
||||
}
|
||||
zigzag_connector_processor.registerPolyFinished();
|
||||
}
|
||||
|
||||
|
||||
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++)
|
||||
if (cut_list.size() == 0)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+137
-79
@@ -4,99 +4,141 @@
|
||||
|
||||
#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
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
/*!
|
||||
* 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);
|
||||
|
||||
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);
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
|
||||
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);
|
||||
private:
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
|
||||
* 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);
|
||||
|
||||
/*!
|
||||
* 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
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* \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
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
|
||||
/*!
|
||||
* adapted from generateLineInfill(.)
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
|
||||
* 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]
|
||||
* sigzag:
|
||||
* 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 [connect_zigzags])
|
||||
*
|
||||
* Note that ZigZag consists of 3 types:
|
||||
* - without endpieces
|
||||
* - with disconnected endpieces
|
||||
* - with connected endpieces
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
@@ -106,21 +148,37 @@ namespace cura
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
* ^ ^ no endpieces
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* v disconnected end piece: leave out last line segment
|
||||
* _____
|
||||
* | | | ,
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
|
||||
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
|
||||
};
|
||||
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
}//namespace cura
|
||||
|
||||
#endif//INFILL_H
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,25 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,28 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,154 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,75 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,27 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,79 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,26 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,27 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,32 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,72 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,29 @@
|
||||
/** 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
|
||||
+2
-31
@@ -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 (line_width_0 < nozzle_width)
|
||||
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
|
||||
{
|
||||
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 (line_width_0 < nozzle_width)
|
||||
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
|
||||
{
|
||||
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,33 +71,4 @@ 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
|
||||
|
||||
@@ -36,16 +36,6 @@ 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
|
||||
|
||||
+4
-3
@@ -66,7 +66,7 @@ void print_call(int argc, char **argv)
|
||||
|
||||
void connect(int argc, char **argv)
|
||||
{
|
||||
CommandSocket* commandSocket = new CommandSocket();
|
||||
CommandSocket::instantiate();
|
||||
std::string ip;
|
||||
int port = 49674;
|
||||
|
||||
@@ -107,7 +107,7 @@ void connect(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
commandSocket->connect(ip, port);
|
||||
CommandSocket::getInstance()->connect(ip, port);
|
||||
}
|
||||
|
||||
void slice(int argc, char **argv)
|
||||
@@ -148,12 +148,13 @@ 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);
|
||||
}
|
||||
|
||||
+96
-105
@@ -2,6 +2,7 @@
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/BucketGrid2D.h"
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
#define INLINE static inline
|
||||
|
||||
@@ -15,17 +16,16 @@ void PathOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
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 in polygon
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_point;
|
||||
best = point_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
@@ -37,46 +37,50 @@ void PathOrderOptimizer::optimize()
|
||||
|
||||
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
int best_poly_idx = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
assert (polygons[i_polygon].size() != 2);
|
||||
assert (polygons[poly_idx].size() != 2);
|
||||
|
||||
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
|
||||
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_polygon;
|
||||
best_poly_idx = poly_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() != 2);
|
||||
assert(polygons[best_poly_idx].size() != 2);
|
||||
|
||||
prev_point = polygons[best][polyStart[best]];
|
||||
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_poly_idx] = true;
|
||||
polyOrder.push_back(best_poly_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Failed to find next closest polygon.\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
|
||||
{
|
||||
int poly_idx = polyOrder[n];
|
||||
int poly_idx = polyOrder[order_idx];
|
||||
int point_idx = getPolyStart(prev_point, poly_idx);
|
||||
polyStart[poly_idx] = point_idx;
|
||||
prev_point = polygons[poly_idx][point_idx];
|
||||
@@ -99,22 +103,23 @@ 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 bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
|
||||
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 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)
|
||||
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)
|
||||
{
|
||||
best_point_idx = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_score = dist + is_on_inside_corner_score;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
return best_point_idx;
|
||||
}
|
||||
@@ -152,127 +157,113 @@ void LineOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
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
|
||||
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
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < best_point_dist)
|
||||
{
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_dist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
polyStart.push_back(best_point_idx);
|
||||
|
||||
assert(poly.size() == 2);
|
||||
|
||||
line_bucket_grid.insert(poly[0], i_polygon);
|
||||
line_bucket_grid.insert(poly[1], i_polygon);
|
||||
line_bucket_grid.insert(poly[0], poly_idx);
|
||||
line_bucket_grid.insert(poly[1], poly_idx);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point incoming_perpundicular_normal(0, 0);
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
int best_line_idx = -1;
|
||||
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
|
||||
|
||||
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
{
|
||||
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
|
||||
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
}
|
||||
|
||||
if (best == -1) /// if single-line-polygon hasn't been found yet
|
||||
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
|
||||
{
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
assert(polygons[i_polygon].size() == 2);
|
||||
}
|
||||
assert(polygons[poly_idx].size() == 2);
|
||||
|
||||
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() == 2);
|
||||
PolygonRef best_line = polygons[best_line_idx];
|
||||
assert(best_line.size() == 2);
|
||||
|
||||
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));
|
||||
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));
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_line_idx] = true;
|
||||
polyOrder.push_back(best_line_idx);
|
||||
}
|
||||
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;
|
||||
}
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
polyStart[nr] = best;
|
||||
assert(poly.size() == 2);
|
||||
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
|
||||
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
|
||||
{
|
||||
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 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)
|
||||
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 0;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 0;
|
||||
}
|
||||
}
|
||||
{ /// check distance to second point on line (1)
|
||||
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)
|
||||
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 1;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 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
|
||||
|
||||
@@ -81,8 +81,35 @@ public:
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
|
||||
/*!
|
||||
* 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);
|
||||
|
||||
/*!
|
||||
* 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
-3
@@ -1,4 +1,6 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <clipper/clipper.hpp>
|
||||
|
||||
#include "raft.h"
|
||||
#include "support.h"
|
||||
|
||||
@@ -8,15 +10,15 @@ void generateRaft(SliceDataStorage& storage, int distance)
|
||||
{
|
||||
if (storage.draft_protection_shield.size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,11 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-8
@@ -42,13 +42,16 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
|
||||
if (upSkinCount == 0) upskin = Polygons();
|
||||
|
||||
auto getInsidePolygons = [&part](SliceLayer& layer2)
|
||||
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
result.add(part2.insets.back());
|
||||
{
|
||||
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
|
||||
result.add(part2.insets[wall_idx]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -67,20 +70,20 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer_nr > 0 && downSkinCount > 0)
|
||||
if (layer_nr >= downSkinCount && downSkinCount > 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
|
||||
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
|
||||
for (int downskin_layer_nr = 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()) - 1 && upSkinCount > 0)
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
|
||||
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++)
|
||||
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
|
||||
}
|
||||
@@ -134,7 +137,7 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
|
||||
}
|
||||
}
|
||||
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
@@ -142,9 +145,10 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
|
||||
{
|
||||
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(-extrusionWidth / 2 - infill_skin_overlap);
|
||||
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
|
||||
|
||||
for(SliceLayerPart& part2 : layer.parts)
|
||||
{
|
||||
|
||||
+4
-2
@@ -65,13 +65,15 @@ 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 extrusionWidth width of the wall lines
|
||||
* \param innermost_wall_extrusion_width width of the innermost 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 extrusionWidth, int infill_skin_overlap, int wall_line_count);
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
|
||||
|
||||
/*!
|
||||
* \brief Combines the infill of multiple layers for a specified mesh.
|
||||
|
||||
@@ -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, "MOVE"),
|
||||
travel_config(&retraction_config, PrintFeatureType::MoveCombing),
|
||||
skirt_config(initializeSkirtConfigs()),
|
||||
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"),
|
||||
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),
|
||||
max_object_height_second_to_last_extruder(-1)
|
||||
{
|
||||
}
|
||||
@@ -161,93 +161,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -24,19 +24,6 @@ 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.
|
||||
@@ -50,7 +37,6 @@ 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.
|
||||
};
|
||||
|
||||
@@ -131,14 +117,13 @@ 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, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
|
||||
: 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)
|
||||
{
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(&retraction_config, "FILL");
|
||||
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -188,7 +173,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, "SKIRT");
|
||||
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -227,21 +212,6 @@ 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
|
||||
|
||||
+38
-17
@@ -35,7 +35,7 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
|
||||
return joined;
|
||||
}
|
||||
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
|
||||
{
|
||||
// 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, commandSocket);
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
|
||||
|
||||
if (mesh.getSettingBoolean("support_roof_enable"))
|
||||
{
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
|
||||
}
|
||||
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, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
@@ -94,7 +94,6 @@ 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");
|
||||
@@ -114,21 +113,23 @@ 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") + extrusionWidth / 2;
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
|
||||
|
||||
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");
|
||||
|
||||
// derived settings:
|
||||
if (conical_support_angle == 0)
|
||||
{
|
||||
conical_support = false;
|
||||
}
|
||||
|
||||
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
|
||||
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
|
||||
// derived settings:
|
||||
|
||||
|
||||
int supportLayerThickness = layerThickness;
|
||||
|
||||
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
|
||||
int layerZdistanceTop = std::max(0, 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
|
||||
@@ -136,12 +137,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
int64_t conical_support_offset;
|
||||
if (conical_support_angle > 0)
|
||||
{
|
||||
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{ // outward ==> wider base than overhang
|
||||
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
else
|
||||
{
|
||||
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{ // inward ==> smaller base than overhang
|
||||
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
|
||||
unsigned int support_layer_count = layer_count;
|
||||
@@ -248,11 +249,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 = layer_idx;
|
||||
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)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(), commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
@@ -263,6 +264,26 @@ 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;
|
||||
@@ -409,7 +430,7 @@ void AreaSupport::handleWallStruts(
|
||||
}
|
||||
|
||||
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
|
||||
{
|
||||
int roof_layer_count = support_roof_height / layerThickness;
|
||||
|
||||
|
||||
+3
-6
@@ -15,9 +15,8 @@ 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, CommandSocket* commandSocket);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -28,9 +27,8 @@ 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, CommandSocket* commandSocket);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas);
|
||||
|
||||
|
||||
|
||||
@@ -39,11 +37,10 @@ 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, CommandSocket* commandSocket);
|
||||
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
|
||||
|
||||
/*!
|
||||
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/** 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
|
||||
@@ -202,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
|
||||
return p0 * len / _len;
|
||||
}
|
||||
|
||||
INLINE Point crossZ(const Point& p0)
|
||||
INLINE Point turn90CCW(const Point& p0)
|
||||
{
|
||||
return Point(-p0.Y, p0.X);
|
||||
}
|
||||
|
||||
+40
-12
@@ -146,32 +146,60 @@ 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -162,7 +162,13 @@ void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_d
|
||||
}
|
||||
polygon->erase(polygon->begin() + writing_idx , polygon->end());
|
||||
}
|
||||
|
||||
|
||||
if (size() < 3)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Point* last = &thiss[0];
|
||||
unsigned int writing_idx = 1;
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
|
||||
+23
-22
@@ -21,20 +21,6 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
enum PolygonType
|
||||
{
|
||||
NoneType,
|
||||
Inset0Type,
|
||||
InsetXType,
|
||||
SkinType,
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType,
|
||||
MoveCombingType,
|
||||
MoveRetractionType
|
||||
};
|
||||
|
||||
|
||||
class PartsView;
|
||||
|
||||
@@ -79,7 +65,9 @@ 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>
|
||||
@@ -225,7 +213,7 @@ public:
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing line segments smaller than \p remove_length
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
@@ -235,15 +223,26 @@ 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++)
|
||||
{
|
||||
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
|
||||
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))
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,6 +361,9 @@ 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;
|
||||
@@ -405,13 +407,12 @@ public:
|
||||
clipper.Execute(ClipperLib::ctXor, ret.polygons);
|
||||
return ret;
|
||||
}
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const
|
||||
{
|
||||
Polygons ret;
|
||||
double miterLimit = 1.2;
|
||||
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miterLimit;
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret.polygons, distance);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -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 = 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 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 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_inside = false;
|
||||
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
@@ -125,9 +125,10 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +148,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;
|
||||
|
||||
@@ -159,9 +160,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = crossZ(normal(ab, distance));
|
||||
Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
|
||||
ret = x + inward_dir;
|
||||
is_inside = dot(inward_dir, p - x) >= 0;
|
||||
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +172,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
if (is_inside)
|
||||
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 (bestDist2 < distance * distance)
|
||||
{
|
||||
@@ -191,7 +192,6 @@ 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;
|
||||
|
||||
@@ -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 maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
|
||||
* \param max_dist2 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 maxDist2 = std::numeric_limits<int64_t>::max());
|
||||
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits<int64_t>::max());
|
||||
|
||||
/*!
|
||||
* Find the two points in two polygons with the smallest distance.
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
+10
-1
@@ -74,7 +74,16 @@ class WallOverlapComputation
|
||||
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
|
||||
: poly(poly), it(it) { }
|
||||
Point& p() const { return *it; }
|
||||
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.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;
|
||||
}
|
||||
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
|
||||
//!< move the iterator forward (and wrap around at the end)
|
||||
ListPolyIt& operator++()
|
||||
|
||||
@@ -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;
|
||||
// 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 );
|
||||
// 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 );
|
||||
}
|
||||
|
||||
void GCodePlannerTest::tearDown()
|
||||
@@ -46,7 +46,7 @@ void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
|
||||
|
||||
GCodeExport gcode;
|
||||
GCodePathConfig configuration = storage->travel_config;
|
||||
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
|
||||
gCodePlanner->addExtrusionMove(Point(0, 0), &configuration, SpaceFillType::Lines, 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();
|
||||
|
||||
@@ -215,4 +215,56 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,15 @@ 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:
|
||||
@@ -90,6 +99,15 @@ public:
|
||||
void getDist2FromLineSegmentDiagonal2LargeTest();
|
||||
void getDist2FromLineSegmentZeroNearTest();
|
||||
void getDist2FromLineSegmentZeroOnTest();
|
||||
|
||||
void getAngleStraightTest();
|
||||
void getAngle90CcwTest();
|
||||
void getAngle90CwTest();
|
||||
void getAngle45CcwTest();
|
||||
void getAngleStraightBackTest();
|
||||
void getAngleLeftAABTest();
|
||||
void getAngleLeftABBTest();
|
||||
void getAngleLeftAAATest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -111,6 +129,21 @@ 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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+95
-22
@@ -9,6 +9,7 @@
|
||||
# * Single random value
|
||||
# * All settings random
|
||||
|
||||
import ast #For safe function evaluation.
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
@@ -64,12 +65,6 @@ 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")
|
||||
@@ -93,18 +88,34 @@ class TestResults():
|
||||
|
||||
|
||||
class Setting():
|
||||
def __init__(self, key, data):
|
||||
## 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):
|
||||
self._key = key
|
||||
self._default = data["default"]
|
||||
self._type = data["type"]
|
||||
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._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._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.
|
||||
@@ -163,10 +174,42 @@ 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 = {}
|
||||
@@ -180,7 +223,7 @@ class EngineTest():
|
||||
|
||||
def _flattenSettings(self, settings):
|
||||
for key, setting in settings.items():
|
||||
self._settings[key] = Setting(key, setting)
|
||||
self._settings[key] = Setting(key, setting, self._locals)
|
||||
if "children" in setting:
|
||||
self._flattenSettings(setting["children"])
|
||||
|
||||
@@ -254,9 +297,42 @@ 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")
|
||||
@@ -264,13 +340,10 @@ if __name__ == "__main__":
|
||||
|
||||
et = EngineTest(args.json, args.engine, args.models)
|
||||
if et.testDefaults() == 0:
|
||||
if not args.simple:
|
||||
et.testSingleChanges()
|
||||
if et.testSingleRandom() == 0:
|
||||
et.testDualRandom()
|
||||
if et.testAllRandom(10) == 0:
|
||||
et.testAllRandom(100)
|
||||
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)
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário