Comparar commits
119 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| a119be9021 | |||
| 68602df6ee | |||
| 92f5311aaf | |||
| 4f9f55ab06 | |||
| 2bfdb2d612 | |||
| 026f82540f | |||
| d87072f761 | |||
| f89a747836 | |||
| 767803d2bf | |||
| a808e9e8ed | |||
| 087efefc16 | |||
| 09afe2264a | |||
| 5ede92fb1b | |||
| 321ef643a4 | |||
| c43c876cbc | |||
| 3d596f1765 | |||
| ff747fa88f | |||
| 41a1db1e55 | |||
| 0717594a15 | |||
| c72b05463a | |||
| eb1acd02a2 | |||
| 63eda27a50 | |||
| 5b1eeecf76 | |||
| 41ede0727b | |||
| 7ffec2e617 | |||
| 57ebcf0163 | |||
| ad29876617 | |||
| 07481a548c | |||
| 7d70768178 | |||
| 9c201e3396 | |||
| f00c091ed1 | |||
| 4574e0917e | |||
| 4ae1f5e6bf | |||
| ae02a91a5d | |||
| 0781a8f4ef | |||
| c6e201e307 | |||
| 1d073600e4 | |||
| 8f6cb82421 | |||
| 1023102e3a | |||
| 0ac334c849 | |||
| 696e0132f4 | |||
| fb284397c2 | |||
| 71e486d007 | |||
| eb2ae0d699 | |||
| bcee6c7cc2 | |||
| 3a28f5d547 | |||
| 4b11b5946e | |||
| d558e24b43 | |||
| 5d6f45a71d | |||
| d827cc0f92 | |||
| 263eb370e9 | |||
| c0d22dcf7b | |||
| 01d0a3b8d6 | |||
| 1dd7313332 | |||
| 7a7d1ebd77 | |||
| 3dfb84b9a1 | |||
| 80896e3f4d | |||
| e8cb1028e4 | |||
| d4adbb97bf | |||
| c8488f6d37 | |||
| 7524df2868 | |||
| 28ac5dc2c9 | |||
| f5db2ee8b4 | |||
| 8081237298 | |||
| 61915fc322 | |||
| aa1998dbd8 | |||
| 859a106ec2 | |||
| ff38534644 | |||
| 2eb81d216f | |||
| 2785659fe6 | |||
| 4bc86ad192 | |||
| a7b5e05505 | |||
| 0fd130cfd3 | |||
| ccae7d611e | |||
| 5dcce74efe | |||
| f78c3ed4da | |||
| 79b16e406a | |||
| 8219f32d96 | |||
| 05cfe62927 | |||
| dc87dda346 | |||
| f249796f3d | |||
| 3a8a5844d4 | |||
| 7b5bb1e367 | |||
| 017dc5be4c | |||
| 60c9e83da9 | |||
| 51927938b0 | |||
| d4b9f7c0bd | |||
| 0b78099587 | |||
| a61d5385ea | |||
| 24c836f87a | |||
| 8a2ca62305 | |||
| b0ae307006 | |||
| 29dd62345a | |||
| b3f555c78f | |||
| 0b2f64c6a7 | |||
| d501fe67d5 | |||
| 7180e77699 | |||
| 9c36fdf513 | |||
| bc8a167e7b | |||
| bd90dd060f | |||
| 5ac62a96a8 | |||
| 7010689448 | |||
| 869975be6b | |||
| da3bf8da32 | |||
| 360e3d7e24 | |||
| c267492a48 | |||
| 52258e2d3a | |||
| 92c6edca86 | |||
| 9c26c820a9 | |||
| b7993441ff | |||
| 10ebc96323 | |||
| 13af50f34d | |||
| 0563c6eacc | |||
| 6f95a78f3c | |||
| ae79d232f3 | |||
| 11786ada66 | |||
| 648bc1f3f3 | |||
| 84be436eb9 | |||
| e04965187a |
+37
-3
@@ -10,6 +10,16 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
|
||||
# Add a compiler flag to check the output for insane values if we are in debug mode.
|
||||
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message("Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
endif()
|
||||
|
||||
# Add warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
@@ -21,7 +31,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
|
||||
|
||||
add_library(clipper STATIC libs/clipper/clipper.cpp)
|
||||
|
||||
set(engine_SRCS
|
||||
set(engine_SRCS # Except main.cpp.
|
||||
src/bridge.cpp
|
||||
src/comb.cpp
|
||||
src/commandSocket.cpp
|
||||
@@ -33,7 +43,6 @@ set(engine_SRCS
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/main.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
@@ -60,15 +69,40 @@ set(engine_SRCS
|
||||
src/utils/polygon.cpp
|
||||
)
|
||||
|
||||
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
|
||||
set(engine_TEST
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
# Generating ProtoBuf protocol.
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
|
||||
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
|
||||
# Compiling CuraEngine itself.
|
||||
add_executable(CuraEngine ${engine_SRCS} src/main.cpp ${engine_PB_SRCS})
|
||||
target_link_libraries(CuraEngine clipper Arcus)
|
||||
|
||||
set_target_properties(CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(CuraEngine pthread)
|
||||
endif()
|
||||
|
||||
# Compiling the test environment.
|
||||
if (BUILD_TESTS)
|
||||
add_library(CuraEngineLibrary ${engine_SRCS} ${engine_PB_SRCS})
|
||||
if (UNIX)
|
||||
target_link_libraries(CuraEngineLibrary pthread)
|
||||
endif()
|
||||
enable_testing()
|
||||
foreach (test ${engine_TEST})
|
||||
add_executable(${test} tests/main.cpp tests/${test}.cpp)
|
||||
target_link_libraries(${test} CuraEngineLibrary)
|
||||
target_link_libraries(${test} cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
|
||||
@@ -62,6 +62,8 @@ message Polygon {
|
||||
SkirtType = 5;
|
||||
InfillType = 6;
|
||||
SupportInfillType = 7;
|
||||
MoveCombingType = 8;
|
||||
MoveRetractionType = 9;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
|
||||
+123
-65
@@ -16,7 +16,10 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
if (meshgroup_number == 1)
|
||||
{
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (command_socket)
|
||||
command_socket->beginGCode();
|
||||
@@ -35,8 +38,13 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
}
|
||||
meshgroup_number++;
|
||||
|
||||
unsigned int total_layers = storage.meshes[0].layers.size();
|
||||
//gcode.writeComment("Layer count: %d", totalLayers);
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
gcode.writeLayerCountComment(total_layers);
|
||||
|
||||
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
if (has_raft)
|
||||
@@ -61,18 +69,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
|
||||
if (command_socket)
|
||||
{
|
||||
finalize();
|
||||
command_socket->sendGCodeLayer();
|
||||
command_socket->endSendSlicedObject();
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:UltiGCode\n";
|
||||
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
command_socket->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +85,10 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
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");
|
||||
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");
|
||||
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,14 +188,19 @@ void FffGcodeWriter::setConfigInfill(SliceMeshStorage& mesh, int layer_thickness
|
||||
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
if (!command_socket)
|
||||
{
|
||||
if (!command_socket)
|
||||
std::ostringstream prefix;
|
||||
prefix << "FLAVOR:" << toString(gcode.getFlavor());
|
||||
gcode.writeComment(prefix.str().c_str());
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
gcode.writeComment("TIME:666");
|
||||
gcode.writeComment("MATERIAL:666");
|
||||
gcode.writeComment("MATERIAL2:-1");
|
||||
}
|
||||
}
|
||||
else
|
||||
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
|
||||
@@ -231,75 +234,95 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
|
||||
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
|
||||
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
|
||||
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
|
||||
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
|
||||
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
|
||||
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
|
||||
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
|
||||
bool retraction_combing = false; // the raft isn't added to the parts to avoid
|
||||
bool retraction_combing = true;
|
||||
|
||||
int n_raft_surface_layers = train->getSettingAsCount("raft_surface_layers");
|
||||
|
||||
{ // raft base layer
|
||||
gcode.writeLayerComment(-3);
|
||||
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
|
||||
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
|
||||
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
|
||||
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 2;
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
int64_t z = getSettingInMicrons("raft_base_thickness");
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
gcode.setZ(getSettingInMicrons("raft_base_thickness"));
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_base_config.getLayerHeight());
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_base_line_width"), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &raft_base_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, raft_base_config.getLineWidth());
|
||||
|
||||
gcode.writeFanCommand(train->getSettingInPercentage("raft_base_fan_speed"));
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_base_thickness"));
|
||||
}
|
||||
|
||||
{ // raft interface layer
|
||||
gcode.writeLayerComment(-2);
|
||||
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
|
||||
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
|
||||
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_interface_thickness"));
|
||||
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 1;
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness");
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness"));
|
||||
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_interface_config.getLayerHeight());
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &raft_interface_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, raft_interface_config.getLineWidth());
|
||||
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
|
||||
}
|
||||
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer<=train->getSettingAsCount("raft_surface_layers"); raftSurfaceLayer++)
|
||||
|
||||
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
|
||||
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
|
||||
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
|
||||
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_surface_thickness"));
|
||||
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
|
||||
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
|
||||
{ // raft surface layers
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
gcode.writeLayerComment(-1);
|
||||
gcode.writeComment("RAFT");
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer;
|
||||
gcode.setZ(z);
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setCombing(false);
|
||||
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer);
|
||||
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, raft_surface_config.getLayerHeight());
|
||||
|
||||
Polygons raft_lines;
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &raft_surface_config);
|
||||
sendPolygons(SupportType, layer_nr, raft_lines, raft_surface_config.getLineWidth());
|
||||
|
||||
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
|
||||
}
|
||||
@@ -335,9 +358,8 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
|
||||
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
|
||||
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
int z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode.setZ(z);
|
||||
gcode.resetStartPosition();
|
||||
|
||||
@@ -559,30 +581,30 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
int infill_angle = 45;
|
||||
if (layer_nr & 1)
|
||||
infill_angle += 90;
|
||||
int extrusion_width = mesh->infill_config[0].getLineWidth();
|
||||
int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
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);
|
||||
}
|
||||
|
||||
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
|
||||
|
||||
if (!mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
|
||||
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);
|
||||
}
|
||||
|
||||
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
|
||||
infill_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = 0;
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_angle, extrusion_width);
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_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"))
|
||||
@@ -681,10 +703,11 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
|
||||
{
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int bridge = -1;
|
||||
if (layer_nr > 0)
|
||||
@@ -703,18 +726,20 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
{
|
||||
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)
|
||||
{
|
||||
inner_skin_outline = &skin_part.insets.back();
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inner_skin_outline == nullptr)
|
||||
{
|
||||
inner_skin_outline = &skin_part.outline;
|
||||
@@ -725,12 +750,16 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
generateLineInfill(part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,9 +843,14 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,7 +870,11 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, roof_extruder_nr);
|
||||
|
||||
double fillAngle;
|
||||
if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height"))
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
fillAngle = 0;
|
||||
}
|
||||
else if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height") || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
fillAngle = 90; // perpendicular to support lines
|
||||
}
|
||||
@@ -852,8 +890,10 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
|
||||
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());
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
@@ -926,12 +966,30 @@ void FffGcodeWriter::processFanSpeedAndMinimalLayerTime(SliceDataStorage& storag
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (command_socket)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
|
||||
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
}
|
||||
command_socket->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
|
||||
gcode.finalize(max_object_height, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
|
||||
gcode.writeComment("End of Gcode");
|
||||
/*
|
||||
the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB.
|
||||
gcode.writeCode("M25 ;Stop reading from this point on.");
|
||||
gcode.writeComment("Cura profile string:");
|
||||
gcode.writeComment(FffProcessor::getInstance()->getAllLocalSettingsString() + FffProcessor::getInstance()->getProfileString());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -125,18 +125,18 @@ private:
|
||||
/*!
|
||||
* Add raft gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param totalLayers The total number of layers.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Add a layer to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param totalLayers The total number of layers.
|
||||
* \param total_layers The total number of layers.
|
||||
* \param has_raft Whether a raft is used for this print.
|
||||
*/
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int totalLayers, bool has_raft);
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Interpolate between the initial layer speeds and the eventual speeds.
|
||||
|
||||
+79
-158
@@ -1,9 +1,6 @@
|
||||
#include "FffPolygonGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random> // for bulging effect?
|
||||
#include <functional> // for bugling?
|
||||
#include <cmath> // for bulging?
|
||||
|
||||
#include "slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
@@ -49,13 +46,28 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
log("Slicing model...\n");
|
||||
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
|
||||
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Initial layer height %i is disallowed.",initial_layer_thickness);
|
||||
return false;
|
||||
}
|
||||
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
|
||||
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Layer height %i is disallowed.",layer_thickness);
|
||||
return false;
|
||||
}
|
||||
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
|
||||
{
|
||||
initial_layer_thickness = layer_thickness;
|
||||
}
|
||||
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
|
||||
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.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
std::vector<Slicer*> slicerList;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
|
||||
@@ -79,9 +91,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
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);
|
||||
|
||||
bulgeWalls(slicerList, meshgroup);
|
||||
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
@@ -99,20 +108,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = meshStorage.layers[layer_nr];
|
||||
meshStorage.layers[layer_nr].printZ +=
|
||||
meshStorage.getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
if (has_raft)
|
||||
{
|
||||
layer.printZ +=
|
||||
meshStorage.getSettingInMicrons("raft_base_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("layer_height") //raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap")
|
||||
- initial_slice_z;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshStorage.layers[layer_nr].printZ +=
|
||||
meshStorage.getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness") //raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap");
|
||||
}
|
||||
|
||||
|
||||
@@ -136,8 +141,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
// const
|
||||
unsigned int total_layers = storage.meshes.at(0).layers.size();
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
//layerparts2HTML(storage, "output/output.html");
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
@@ -157,6 +166,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
@@ -165,6 +175,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
@@ -184,10 +195,10 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
|
||||
}
|
||||
|
||||
for(unsigned int layer_number = total_layers-1; layer_number > 0; 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)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
combineInfillLayers(layer_number, mesh, mesh.getSettingAsCount("infill_sparse_combine"));
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
}
|
||||
|
||||
storage.primeTower.computePrimeTowerMax(storage);
|
||||
@@ -204,7 +215,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
processFuzzySkin(mesh);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,20 +251,12 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
{
|
||||
if (layer->parts[partNr].insets.size() > 0)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // only send polygon data
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, part.outline, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
for (PolygonRef polyline : layer->openPolyLines)
|
||||
@@ -260,10 +274,10 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers)
|
||||
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers)
|
||||
{
|
||||
int n_empty_first_layers = 0;
|
||||
for (unsigned int layer_idx = 0; layer_idx < totalLayers; layer_idx++)
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
bool layer_is_empty = true;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
@@ -297,7 +311,7 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
|
||||
layer.printZ -= n_empty_first_layers * layer_height;
|
||||
}
|
||||
}
|
||||
totalLayers -= n_empty_first_layers;
|
||||
total_layers -= n_empty_first_layers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,40 +321,45 @@ void FffPolygonGenerator::processSkins(SliceDataStorage& storage, unsigned int l
|
||||
{
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
|
||||
|
||||
int extrusionWidth = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
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");
|
||||
generateSkins(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), 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)
|
||||
{
|
||||
infill_skin_overlap = extrusionWidth / 2;
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
|
||||
if (mesh.getSettingString("fill_perimeter_gaps") == "Skin")
|
||||
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
}
|
||||
else if (mesh.getSettingString("fill_perimeter_gaps") == "Everywhere")
|
||||
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, 0, 0);
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
bool frontend_can_show_polygon_as_filled_polygon = false;
|
||||
if (frontend_can_show_polygon_as_filled_polygon)
|
||||
{
|
||||
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
|
||||
for (SkinPart& skin_part : part.skin_parts)
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_part.outline, extrusionWidth);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (!getSettingBoolean("ooze_shield_enabled"))
|
||||
{
|
||||
@@ -349,28 +368,28 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned
|
||||
|
||||
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
|
||||
|
||||
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
|
||||
}
|
||||
|
||||
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
|
||||
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
|
||||
}
|
||||
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
|
||||
for(unsigned int layer_nr=1; layer_nr<totalLayers; layer_nr++)
|
||||
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
|
||||
}
|
||||
for(unsigned int layer_nr=totalLayers-1; layer_nr>0; layer_nr--)
|
||||
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
|
||||
{
|
||||
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
|
||||
@@ -387,7 +406,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
|
||||
int layer_skip = 500 / layer_height + 1;
|
||||
|
||||
Polygons& draft_shield = storage.draft_protection_shield;
|
||||
for (unsigned int layer_nr = 0; layer_nr < totalLayers && layer_nr < max_screen_layer; layer_nr += layer_skip)
|
||||
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
|
||||
{
|
||||
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
|
||||
}
|
||||
@@ -420,14 +439,19 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
|
||||
void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
|
||||
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
{
|
||||
if (mesh.getSettingAsCount("wall_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
|
||||
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
for (SliceLayer& layer : mesh.layers)
|
||||
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons results;
|
||||
@@ -472,110 +496,7 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup)
|
||||
{
|
||||
|
||||
assert(slicerList.size() == meshgroup->meshes.size());
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < slicerList.size(); mesh_idx++)
|
||||
{
|
||||
Slicer* slicer = slicerList[mesh_idx];
|
||||
Mesh& mesh = meshgroup->meshes[mesh_idx];
|
||||
|
||||
if (!mesh.getSettingBoolean("magic_bulge_walls"))
|
||||
{
|
||||
// continue; // TODO
|
||||
}
|
||||
|
||||
auto getBulging = [](Point xy, int z)
|
||||
{
|
||||
std::hash<int> hash_fn;
|
||||
int cell_size = MM2INT(0.2);
|
||||
int cell_dim = 5; // surrounding taken into account
|
||||
double result = 0.0;
|
||||
int bulging = MM2INT(10.0);
|
||||
Point3 middle(xy.X / cell_size, xy.Y / cell_size, z / cell_size);
|
||||
double total_weight = 0.0;
|
||||
for (int x = middle.x - cell_dim; x < middle.x + cell_dim; x++)
|
||||
{
|
||||
for (int y = middle.y - cell_dim; y < middle.y + cell_dim; y++)
|
||||
{
|
||||
for (int z = middle.z - cell_dim; z < middle.z + cell_dim; z++)
|
||||
{
|
||||
srand(x ^ (y << 8) ^ (z << 16)); // set seed
|
||||
int h = rand();
|
||||
// int h = hash_fn(x ^ (y << 8) ^ (z << 16));
|
||||
double r = (double(h % 200000 - 100000))/100000.0; // between -1 and 1
|
||||
double weight = sqrt(1.0 / (1.0 + static_cast<double>(((Point3(xy.X, xy.Y, z) - Point3(x,y,z)* cell_size)).vSize()) * 4));
|
||||
total_weight += weight;
|
||||
result += r * weight ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(result / total_weight * bulging);
|
||||
// return rand() % (bulging*2) - bulging;
|
||||
};
|
||||
|
||||
int64_t avg_dist_between_points = MM2INT(0.5); // mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
|
||||
int layer_height = mesh.getSettingInMicrons("layer_height");
|
||||
|
||||
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
|
||||
{
|
||||
SlicerLayer& layer = slicer->layers[layer_nr];
|
||||
Polygons& outlines = layer.polygonList;
|
||||
Polygons results;
|
||||
|
||||
int z_approx = layer_nr * layer_height;
|
||||
|
||||
for (PolygonRef poly : outlines)
|
||||
{
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
for (Point& p1 : poly)
|
||||
{ // 'a' is the (next) new point between p0 and p1
|
||||
Point p0p1 = p1 - *p0;
|
||||
int64_t p0p1_size = vSize(p0p1);
|
||||
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
Point in_between = *p0 + normal(p0p1, p0pa_dist);
|
||||
int r = getBulging(in_between, z_approx);
|
||||
Point perp_to_p0p1 = crossZ(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = in_between + fuzz;
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
|
||||
p0 = &p1;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
if (point_idx == 0) { break; }
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
for (Point& p : poly)
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
outlines = results;
|
||||
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class Slicer; // forward declaration
|
||||
|
||||
/*!
|
||||
* Primary stage in Fused Filament Fabrication processing: Polygons are generated.
|
||||
* The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object.
|
||||
@@ -91,9 +89,9 @@ private:
|
||||
* Remove all bottom layers which are empty.
|
||||
* \param storage Input and Ouput parameter: stores all layers
|
||||
* \param layer_height The height of each layer
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers);
|
||||
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the inset polygons which form the walls.
|
||||
@@ -105,9 +103,9 @@ private:
|
||||
/*!
|
||||
* 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
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processOozeShield(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas.
|
||||
@@ -120,9 +118,9 @@ private:
|
||||
* Generate the polygons where the draft screen should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processDraftShield(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
/*!
|
||||
* Generate the skirt/brim/raft areas/insets.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
@@ -132,15 +130,12 @@ private:
|
||||
|
||||
|
||||
/*!
|
||||
* Special mode: Make the outer wall 'fuzzy'
|
||||
* Make the outer wall 'fuzzy'
|
||||
*/
|
||||
void processFuzzySkin(SliceMeshStorage& mesh);
|
||||
void processFuzzyWalls(SliceMeshStorage& mesh);
|
||||
|
||||
|
||||
/*!
|
||||
* Special mode: bulge the outer walls
|
||||
*/
|
||||
void bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup);
|
||||
|
||||
|
||||
};
|
||||
}//namespace cura
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
private:
|
||||
FffPolygonGenerator polygon_generator;
|
||||
FffGcodeWriter gcode_writer;
|
||||
CommandSocket* command_socket;
|
||||
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
|
||||
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
|
||||
}
|
||||
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
@@ -113,7 +113,7 @@ void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLaye
|
||||
generatePaths3(storage);
|
||||
}
|
||||
}
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers)
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
|
||||
+3
-3
@@ -44,10 +44,10 @@ public:
|
||||
* Generate the area where the prime tower should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param totalLayers The total number of layers
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void generatePaths(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers);
|
||||
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
|
||||
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
void computePrimeTowerMax(SliceDataStorage& storage);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class EPrintFeature : unsigned int
|
||||
{
|
||||
enum class EPrintFeature : unsigned int // unused!!
|
||||
{ // TODO: use in gcodePathConfigs ?
|
||||
OUTER_WALL,
|
||||
INNER_WALLS,
|
||||
INFILL,
|
||||
|
||||
@@ -27,7 +27,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
processSkirt(commandSocket);
|
||||
|
||||
|
||||
unsigned int totalLayers = wireFrame.layers.size();
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
gcode.writeLayerComment(0);
|
||||
gcode.writeTypeComment("SKIRT");
|
||||
|
||||
@@ -71,7 +71,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, totalLayers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
|
||||
+100
-51
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "utils/SVG.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -15,22 +16,41 @@ bool Comb::moveInsideBoundary(Point* p, int distance)
|
||||
|
||||
Polygons Comb::getLayerSecondWalls()
|
||||
{
|
||||
Polygons layer_walls;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
if (layer_nr < 0)
|
||||
{ // when a raft is present
|
||||
return storage.raftOutline.offset(MM2INT(0.1));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
Polygons layer_walls;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (part.insets.size() >= 2)
|
||||
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
|
||||
{
|
||||
layer_walls.add(part.insets[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer_walls.add(part.outline.offset(-offset_from_outlines));
|
||||
// we want the 2nd inner walls
|
||||
if (part.insets.size() >= 2) {
|
||||
layer_walls.add(part.insets[1]);
|
||||
continue;
|
||||
}
|
||||
// but we'll also take the inner wall if the 2nd doesn't exist
|
||||
if (part.insets.size() >= 1) {
|
||||
layer_walls.add(part.insets[0]);
|
||||
continue;
|
||||
}
|
||||
// and if there is no walls, we'll try to move inside from the outline
|
||||
Polygons newOutline = part.outline.offset(-offset_from_outlines);
|
||||
if(newOutline.polygonLength() > 0) {
|
||||
layer_walls.add(newOutline);
|
||||
continue;
|
||||
}
|
||||
// offset_from_outlines was so large that it completely destroyed our isle,
|
||||
// so we'll just use the regular outline
|
||||
layer_walls.add(part.outline);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return layer_walls;
|
||||
}
|
||||
return layer_walls;
|
||||
}
|
||||
|
||||
// boundary_outside is only computed when it's needed!
|
||||
@@ -44,7 +64,7 @@ Polygons* Comb::getBoundaryOutside()
|
||||
return boundary_outside;
|
||||
}
|
||||
|
||||
Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
Comb::Comb(SliceDataStorage& storage, int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, layer_nr(layer_nr)
|
||||
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
|
||||
@@ -64,7 +84,7 @@ Comb::~Comb()
|
||||
delete boundary_outside;
|
||||
}
|
||||
|
||||
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside)
|
||||
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
|
||||
{
|
||||
@@ -117,13 +137,15 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
{ // normal combing within part
|
||||
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
|
||||
Point middle_from;
|
||||
Point middle_to;
|
||||
Point inside_middle_from;
|
||||
Point inside_middle_to;
|
||||
|
||||
if (startInside && endInside)
|
||||
{
|
||||
@@ -131,27 +153,36 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
|
||||
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else
|
||||
else if(!startInside && !endInside)
|
||||
{
|
||||
if (!startInside && !endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
middle_to = endPoint;
|
||||
}
|
||||
else if (!startInside && endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]);
|
||||
middle_to = middle_to_cp.location;
|
||||
}
|
||||
else if (startInside && !endInside)
|
||||
{
|
||||
middle_to = endPoint;
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]);
|
||||
middle_from = middle_from_cp.location;
|
||||
}
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
}
|
||||
else if(!startInside && endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else if(startInside && !endInside)
|
||||
{
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
if (startInside)
|
||||
@@ -159,7 +190,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
// start to boundary
|
||||
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_begin, startPoint, middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part_begin, startPoint, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
// throught air from boundary to boundary
|
||||
@@ -178,14 +209,14 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
}
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) )
|
||||
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
|
||||
{ // via outside is a detour
|
||||
combPaths.back().push_back(middle_from);
|
||||
combPaths.back().push_back(middle_to);
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -193,8 +224,8 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
|
||||
combPaths.back().push_back(middle_from);
|
||||
combPaths.back().push_back(middle_to);
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
|
||||
if (endInside)
|
||||
@@ -202,7 +233,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
|
||||
// boundary to end
|
||||
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_end, middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
|
||||
LinePolygonsCrossings::comb(part_end, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -219,18 +250,32 @@ void LinePolygonsCrossings::calcScanlineCrossings()
|
||||
{
|
||||
PolyCrossings minMax(poly_idx);
|
||||
PolygonRef poly = boundary[poly_idx];
|
||||
Point p0 = transformation_matrix.apply(poly.back());
|
||||
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
|
||||
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point p1 = transformation_matrix.apply(poly[point_idx]);
|
||||
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
|
||||
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
|
||||
{
|
||||
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
|
||||
{
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
|
||||
{
|
||||
if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; }
|
||||
if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; }
|
||||
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
|
||||
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
|
||||
{
|
||||
minMax.min.x = x;
|
||||
minMax.min.point_idx = point_idx;
|
||||
}
|
||||
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
|
||||
{
|
||||
minMax.max.x = x;
|
||||
minMax.max.point_idx = point_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
@@ -276,14 +321,14 @@ bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
|
||||
}
|
||||
|
||||
|
||||
void LinePolygonsCrossings::getCombingPath(CombPath& combPath)
|
||||
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, Comb::max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
|
||||
{
|
||||
//We're not crossing any boundaries. So skip the comb generation.
|
||||
combPath.push_back(startPoint);
|
||||
combPath.push_back(endPoint);
|
||||
return;
|
||||
combPath.push_back(endPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
calcScanlineCrossings();
|
||||
@@ -308,7 +353,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
|
||||
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
|
||||
{
|
||||
PolygonRef poly = boundary[polyCrossings.poly_idx];
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x, transformed_startPoint.Y)));
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
|
||||
< poly.size() / 2 )
|
||||
{ // follow the path in the same direction as the winding order of the boundary polygon
|
||||
@@ -323,13 +368,13 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
|
||||
{ // follow the path in the opposite direction of the winding order of the boundary polygon
|
||||
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
|
||||
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
|
||||
|
||||
|
||||
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
|
||||
{
|
||||
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
|
||||
}
|
||||
}
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x, transformed_startPoint.Y)));
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
}
|
||||
|
||||
|
||||
@@ -348,10 +393,14 @@ LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlong
|
||||
}
|
||||
|
||||
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
|
||||
{
|
||||
{
|
||||
optimized_comb_path.push_back(startPoint);
|
||||
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
|
||||
{
|
||||
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Point& current_point = optimized_comb_path.back();
|
||||
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
|
||||
{
|
||||
|
||||
+6
-7
@@ -113,7 +113,7 @@ private:
|
||||
*
|
||||
* \param combPath Output parameter: the points along the combing path.
|
||||
*/
|
||||
void getCombingPath(CombPath& combPath);
|
||||
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
|
||||
/*!
|
||||
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
|
||||
@@ -178,10 +178,10 @@ public:
|
||||
* \param endPoint Where to end the combing move.
|
||||
* \param combPath Output parameter: the combing path generated.
|
||||
*/
|
||||
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside)
|
||||
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
|
||||
{
|
||||
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
|
||||
linePolygonsCrossings.getCombingPath(combPath);
|
||||
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -205,14 +205,13 @@ class Comb
|
||||
friend class LinePolygonsCrossings;
|
||||
private:
|
||||
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
|
||||
unsigned int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
|
||||
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
|
||||
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
|
||||
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
|
||||
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
|
||||
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
|
||||
static const int64_t max_comb_distance_ignored = MM2INT(1.5); //!< If the direct path from start point to end point is shorter than this, go directly without any combing.
|
||||
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
|
||||
|
||||
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
|
||||
@@ -240,7 +239,7 @@ public:
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
*/
|
||||
Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
Comb(SliceDataStorage& storage, int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
|
||||
~Comb();
|
||||
|
||||
@@ -257,7 +256,7 @@ public:
|
||||
* \param endInside Whether we want to end up inside the comb boundary
|
||||
* \return Whether combing has succeeded; otherwise a retraction is needed.
|
||||
*/
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false);
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
|
||||
/*!
|
||||
* Move \p p to inside the inner comb boundary with a \p distance from the boundary.
|
||||
|
||||
+24
-1
@@ -26,6 +26,8 @@ public:
|
||||
, object_count(0)
|
||||
, current_sliced_object(nullptr)
|
||||
, sliced_objects(0)
|
||||
, current_layer_count(0)
|
||||
, current_layer_offset(0)
|
||||
{ }
|
||||
|
||||
cura::proto::Layer* getLayerById(int id);
|
||||
@@ -43,6 +45,11 @@ public:
|
||||
|
||||
// Number of sliced objects for this sliced object list
|
||||
int sliced_objects;
|
||||
|
||||
// Number of layers sent to the front end so far
|
||||
// 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;
|
||||
@@ -80,11 +87,17 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
//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)
|
||||
{
|
||||
FffProcessor::getInstance()->processMeshGroup(object.get());
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
d->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
sendGCodeLayer();
|
||||
sendPrintTime();
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//d->processor->processModel(d->object_to_slice.get());
|
||||
@@ -261,11 +274,18 @@ void CommandSocket::beginSendSlicedObject()
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -295,6 +315,8 @@ void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
|
||||
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; });
|
||||
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
@@ -306,6 +328,7 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
}
|
||||
|
||||
return layer;
|
||||
|
||||
+44
-21
@@ -8,7 +8,7 @@
|
||||
namespace cura {
|
||||
|
||||
GCodeExport::GCodeExport()
|
||||
: output_stream(&std::cout), currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
|
||||
: output_stream(&std::cout),currentPosition(0,0,0),startPosition(INT32_MIN,INT32_MIN,0),commandSocket(nullptr),layer_nr(0)
|
||||
{
|
||||
extrusion_amount = 0;
|
||||
current_extruder = 0;
|
||||
@@ -19,7 +19,7 @@ GCodeExport::GCodeExport()
|
||||
currentSpeed = 1;
|
||||
retractionPrimeSpeed = 1;
|
||||
isRetracted = false;
|
||||
isZHopped = false;
|
||||
isZHopped = 0;
|
||||
last_coasted_amount_mm3 = 0;
|
||||
setFlavor(EGCodeFlavor::REPRAP);
|
||||
}
|
||||
@@ -28,6 +28,11 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
|
||||
commandSocket = commandSocket_;
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
|
||||
void GCodeExport::setOutputStream(std::ostream* stream)
|
||||
{
|
||||
output_stream = stream;
|
||||
@@ -137,10 +142,6 @@ double GCodeExport::getTotalFilamentUsed(int e)
|
||||
return extruder_attr[e].totalFilament;
|
||||
}
|
||||
|
||||
double GCodeExport::getTotalPrintTime(EPrintFeature print_feature)
|
||||
{
|
||||
return total_print_time_per_feature[(unsigned int)print_feature];
|
||||
}
|
||||
double GCodeExport::getTotalPrintTime()
|
||||
{
|
||||
return totalPrintTime;
|
||||
@@ -149,10 +150,6 @@ double GCodeExport::getTotalPrintTime()
|
||||
void GCodeExport::resetTotalPrintTimeAndFilament()
|
||||
{
|
||||
totalPrintTime = 0;
|
||||
for (unsigned int feat_idx = 0; feat_idx < (unsigned int)EPrintFeature::ENUM_COUNT; feat_idx++)
|
||||
{
|
||||
total_print_time_per_feature[feat_idx] = 0.0;
|
||||
}
|
||||
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
|
||||
{
|
||||
extruder_attr[e].totalFilament = 0.0;
|
||||
@@ -162,17 +159,25 @@ void GCodeExport::resetTotalPrintTimeAndFilament()
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::updateTotalPrintTime(EPrintFeature print_feature)
|
||||
void GCodeExport::updateTotalPrintTime()
|
||||
{
|
||||
double time = estimateCalculator.calculate();
|
||||
totalPrintTime += time;
|
||||
total_print_time_per_feature[(unsigned int)print_feature] += time;
|
||||
totalPrintTime += estimateCalculator.calculate();
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::writeComment(std::string comment)
|
||||
{
|
||||
*output_stream << ";" << comment << "\n";
|
||||
*output_stream << ";";
|
||||
for (unsigned int i = 0; i < comment.length(); i++)
|
||||
{
|
||||
if (comment[i] == '\n')
|
||||
{
|
||||
*output_stream << "\\n";
|
||||
}else{
|
||||
*output_stream << comment[i];
|
||||
}
|
||||
}
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
@@ -184,6 +189,11 @@ void GCodeExport::writeLayerComment(int layer_nr)
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLayerCountComment(int layer_count)
|
||||
{
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << "\n";
|
||||
@@ -204,7 +214,7 @@ void GCodeExport::resetExtrusionValue()
|
||||
void GCodeExport::writeDelay(double timeAmount)
|
||||
{
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
|
||||
totalPrintTime += timeAmount;
|
||||
estimateCalculator.addTime(timeAmount);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
|
||||
@@ -222,8 +232,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
|
||||
return;
|
||||
|
||||
assert(speed*60 < 10000 && speed*60 > 100); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
|
||||
#ifdef ASSERT_INSANE_OUTPUT
|
||||
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
|
||||
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
|
||||
#endif //ASSERT_INSANE_OUTPUT
|
||||
|
||||
if (extrusion_mm3_per_mm < 0)
|
||||
logWarning("Warning! Negative extrusion move!");
|
||||
@@ -289,7 +301,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = false;
|
||||
isZHopped = 0;
|
||||
}
|
||||
extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder);
|
||||
if (isRetracted)
|
||||
@@ -301,6 +313,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
if (last_coasted_amount_mm3 > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
|
||||
}else{
|
||||
@@ -317,6 +330,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
if (last_coasted_amount_mm3 > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
|
||||
}
|
||||
}
|
||||
@@ -325,6 +339,15 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
*output_stream << "G1";
|
||||
}else{
|
||||
*output_stream << "G0";
|
||||
|
||||
if (commandSocket) {
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons travelPoly;
|
||||
PolygonRef travel = travelPoly.newPoly();
|
||||
travel.add(Point(currentPosition.x, currentPosition.y));
|
||||
travel.add(Point(x, y));
|
||||
commandSocket->sendPolygons(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
@@ -337,7 +360,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
" X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y);
|
||||
if (z != currentPosition.z)
|
||||
*output_stream << " Z" << INT2MM(z);
|
||||
*output_stream << " Z" << INT2MM(z + isZHopped);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
|
||||
*output_stream << "\n";
|
||||
@@ -380,8 +403,8 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
}
|
||||
if (config->zHop > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n";
|
||||
isZHopped = true;
|
||||
isZHopped = config->zHop;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
|
||||
}
|
||||
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
|
||||
if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max)
|
||||
|
||||
+16
-5
@@ -10,7 +10,7 @@
|
||||
#include "utils/intpoint.h"
|
||||
#include "timeEstimate.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "PrintFeature.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -99,6 +99,11 @@ public:
|
||||
return line_width;
|
||||
}
|
||||
|
||||
int getLayerHeight()
|
||||
{
|
||||
return layer_thickness;
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateExtrusion()
|
||||
{
|
||||
@@ -150,7 +155,7 @@ private:
|
||||
double currentSpeed;
|
||||
int zPos;
|
||||
bool isRetracted;
|
||||
bool isZHopped;
|
||||
int isZHopped;
|
||||
|
||||
double last_coasted_amount_mm3; //!< The coasted amount of filament to be primed on the first next extrusion. (same type as GCodeExport::extrusion_amount)
|
||||
double retractionPrimeSpeed;
|
||||
@@ -159,15 +164,21 @@ private:
|
||||
EGCodeFlavor flavor;
|
||||
|
||||
double totalPrintTime;
|
||||
double total_print_time_per_feature[(unsigned int)EPrintFeature::ENUM_COUNT];
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
|
||||
// for sending jump data
|
||||
CommandSocket* commandSocket;
|
||||
unsigned int layer_nr;
|
||||
|
||||
public:
|
||||
|
||||
GCodeExport();
|
||||
~GCodeExport();
|
||||
|
||||
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
Point getExtruderOffset(int id);
|
||||
@@ -201,13 +212,13 @@ public:
|
||||
double getTotalFilamentUsed(int e);
|
||||
|
||||
double getTotalPrintTime();
|
||||
double getTotalPrintTime(EPrintFeature print_feature);
|
||||
void updateTotalPrintTime(EPrintFeature print_feature = EPrintFeature::UNCLASSIFIED);
|
||||
void updateTotalPrintTime();
|
||||
void resetTotalPrintTimeAndFilament();
|
||||
|
||||
void writeComment(std::string comment);
|
||||
void writeTypeComment(const char* type);
|
||||
void writeLayerComment(int layer_nr);
|
||||
void writeLayerCountComment(int layer_count);
|
||||
|
||||
void writeLine(const char* line);
|
||||
|
||||
|
||||
+17
-11
@@ -31,10 +31,12 @@ void GCodePlanner::forceNewPathStart()
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, GCodeExport& gcode, SliceDataStorage& storage, int z, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: gcode(gcode), storage(storage)
|
||||
, z(z)
|
||||
, travelConfig(retraction_config_travel, "MOVE")
|
||||
{
|
||||
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
|
||||
lastPosition = gcode.getPositionXY();
|
||||
travelConfig.setSpeed(travelSpeed);
|
||||
comb = nullptr;
|
||||
@@ -108,11 +110,14 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
GCodePath* path = nullptr;
|
||||
|
||||
|
||||
bool combed = false;
|
||||
|
||||
if (comb != nullptr && lastPosition != Point(0,0))
|
||||
{
|
||||
CombPaths combPaths;
|
||||
if (comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb))
|
||||
combed = comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb);
|
||||
if (combed)
|
||||
{
|
||||
bool retract = combPaths.size() > 1;
|
||||
{ // check whether we want to retract
|
||||
@@ -153,17 +158,18 @@ void GCodePlanner::addTravel(Point p)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
was_combing = is_going_to_comb;
|
||||
}
|
||||
|
||||
if(!combed) {
|
||||
// no combing? always retract!
|
||||
path = getLatestPathWithConfig(&travelConfig);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
|
||||
addTravel_simple(p, path);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "wallOverlap.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
@@ -42,6 +43,8 @@ private:
|
||||
GCodeExport& gcode;
|
||||
SliceDataStorage& storage;
|
||||
|
||||
int z;
|
||||
|
||||
Point lastPosition;
|
||||
std::vector<GCodePath> paths;
|
||||
|
||||
@@ -87,7 +90,7 @@ public:
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
*/
|
||||
GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
GCodePlanner(CommandSocket* commandSocket, GCodeExport& gcode, SliceDataStorage& storage, int z, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
~GCodePlanner();
|
||||
|
||||
void setCombing(bool going_to_comb);
|
||||
|
||||
@@ -317,6 +317,11 @@ int main(int argc, char **argv)
|
||||
{
|
||||
slice(argc, argv);
|
||||
}
|
||||
else if (stringcasecompare(argv[1], "help") == 0)
|
||||
{
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
cura::logError("Unknown command: %s\n", argv[1]);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ void generateRaft(SliceDataStorage& storage, int distance)
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true);
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-57
@@ -204,13 +204,14 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
}
|
||||
}
|
||||
|
||||
if (false && json_document.HasMember("overrides"))
|
||||
if (json_document.HasMember("overrides"))
|
||||
{
|
||||
const rapidjson::Value& json_object_container = json_document["overrides"];
|
||||
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
|
||||
{
|
||||
SettingConfig* conf = getSettingConfig(override_iterator->name.GetString());
|
||||
_addSettingToContainer(conf, override_iterator, false);
|
||||
std::string setting = override_iterator->name.GetString();
|
||||
SettingConfig* conf = getSettingConfig(setting);
|
||||
_loadSettingValues(conf, override_iterator, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,63 +250,68 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
|
||||
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
if (data.HasMember("type") && data["type"].IsString())
|
||||
{
|
||||
config->setType(data["type"].GetString());
|
||||
}
|
||||
if (data.HasMember("default"))
|
||||
{
|
||||
const rapidjson::Value& dflt = data["default"];
|
||||
if (dflt.IsString())
|
||||
{
|
||||
config->setDefault(dflt.GetString());
|
||||
}
|
||||
else if (dflt.IsTrue())
|
||||
{
|
||||
config->setDefault("true");
|
||||
}
|
||||
else if (dflt.IsFalse())
|
||||
{
|
||||
config->setDefault("false");
|
||||
}
|
||||
else if (dflt.IsNumber())
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << dflt.GetDouble();
|
||||
config->setDefault(ss.str());
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
{
|
||||
config->setUnit(data["unit"].GetString());
|
||||
}
|
||||
|
||||
/// Register the setting in the settings map lookup.
|
||||
if (warn_duplicates && settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
|
||||
}
|
||||
|
||||
if (add_to_settings)
|
||||
{
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
|
||||
}
|
||||
|
||||
/// When this setting has children, add those children to this setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
if (data.HasMember("type") && data["type"].IsString())
|
||||
{
|
||||
config->setType(data["type"].GetString());
|
||||
}
|
||||
if (data.HasMember("default"))
|
||||
{
|
||||
const rapidjson::Value& dflt = data["default"];
|
||||
if (dflt.IsString())
|
||||
{
|
||||
const rapidjson::Value& json_object_container = data["children"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
config->setDefault(dflt.GetString());
|
||||
}
|
||||
else if (dflt.IsTrue())
|
||||
{
|
||||
config->setDefault("true");
|
||||
}
|
||||
else if (dflt.IsFalse())
|
||||
{
|
||||
config->setDefault("false");
|
||||
}
|
||||
else if (dflt.IsNumber())
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << dflt.GetDouble();
|
||||
config->setDefault(ss.str());
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
{
|
||||
config->setUnit(data["unit"].GetString());
|
||||
}
|
||||
|
||||
/// Register the setting in the settings map lookup.
|
||||
if (warn_duplicates && settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
|
||||
}
|
||||
|
||||
if (add_to_settings)
|
||||
{
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
|
||||
/// When this setting has children, add those children to this setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
{
|
||||
const rapidjson::Value& json_object_container = data["children"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(config, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ private:
|
||||
* \param warn_duplicates whether to warn for duplicate definitions
|
||||
*/
|
||||
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
|
||||
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+40
-3
@@ -14,6 +14,26 @@ namespace cura
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
std::string toString(EGCodeFlavor flavor)
|
||||
{
|
||||
switch (flavor)
|
||||
{
|
||||
case EGCodeFlavor::BFB:
|
||||
return "BFB";
|
||||
case EGCodeFlavor::MACH3:
|
||||
return "Mach3";
|
||||
case EGCodeFlavor::MAKERBOT:
|
||||
return "Makerbot";
|
||||
case EGCodeFlavor::ULTIGCODE:
|
||||
return "UltiGCode";
|
||||
case EGCodeFlavor::REPRAP_VOLUMATRIC:
|
||||
return "RepRap(Volumetric)";
|
||||
case EGCodeFlavor::REPRAP:
|
||||
default:
|
||||
return "RepRap";
|
||||
}
|
||||
}
|
||||
|
||||
SettingsBaseVirtual::SettingsBaseVirtual()
|
||||
: parent(NULL)
|
||||
{
|
||||
@@ -92,7 +112,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
|
||||
if (!machine_extruder_trains)
|
||||
{
|
||||
logWarning("Error: no machine_extruder_trains category found in JSON!\n");
|
||||
// no machine_extruder_trains setting present; just use defaults for each train..
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +120,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
|
||||
if (!train)
|
||||
{
|
||||
logError("Not enough extruder trains specified in JSON: %i\n", extruder_nr);
|
||||
// not enough machine_extruder_trains settings present; just use defaults for this train..
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -229,7 +249,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "everywhere")
|
||||
return ESupportType::EVERYWHERE;
|
||||
if (value == "touching_buildplate")
|
||||
if (value == "buildplate")
|
||||
return ESupportType::PLATFORM_ONLY;
|
||||
return ESupportType::NONE;
|
||||
}
|
||||
@@ -258,5 +278,22 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
|
||||
return ESurfaceMode::NORMAL;
|
||||
}
|
||||
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "nowhere")
|
||||
{
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
if (value == "everywhere")
|
||||
{
|
||||
return FillPerimeterGapMode::EVERYWHERE;
|
||||
}
|
||||
if (value == "skin")
|
||||
{
|
||||
return FillPerimeterGapMode::SKIN;
|
||||
}
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -75,6 +75,11 @@ enum class EGCodeFlavor
|
||||
REPRAP_VOLUMATRIC = 5,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Converts a gcode flavor type to string so that it can be included in the gcode.
|
||||
*/
|
||||
std::string toString(EGCodeFlavor flavor);
|
||||
|
||||
/*!
|
||||
* In Cura different infill methods are available.
|
||||
* This enum defines which fill patterns are available to get a uniform naming troughout the engine.
|
||||
@@ -124,6 +129,13 @@ enum class ESurfaceMode
|
||||
BOTH
|
||||
};
|
||||
|
||||
enum class FillPerimeterGapMode
|
||||
{
|
||||
NOWHERE,
|
||||
EVERYWHERE,
|
||||
SKIN
|
||||
};
|
||||
|
||||
#define MAX_EXTRUDERS 16
|
||||
|
||||
//Maximum number of infill layers that can be combined into a single infill extrusion area.
|
||||
@@ -171,6 +183,7 @@ public:
|
||||
ESupportType getSettingAsSupportType(std::string key);
|
||||
EZSeamType getSettingAsZSeamType(std::string key);
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key);
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
+94
-41
@@ -2,15 +2,15 @@
|
||||
#include "skin.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
#define MIN_AREA_SIZE (INT2MM(extrusionWidth) * INT2MM(extrusionWidth))
|
||||
#define MIN_AREA_SIZE (0.4 * 0.4)
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
|
||||
generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, no_small_gaps_heuristic);
|
||||
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
@@ -20,39 +20,71 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
|
||||
}
|
||||
}
|
||||
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, bool no_small_gaps_heuristic)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
SliceLayer& layer = storage.layers[layer_nr];
|
||||
|
||||
if (downSkinCount == 0 && upSkinCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart& part = layer.parts[partNr];
|
||||
|
||||
Polygons upskin = part.insets.back().offset(-extrusionWidth/2);
|
||||
Polygons downskin = upskin;
|
||||
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
|
||||
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
|
||||
if (upSkinCount == 0) upskin = Polygons();
|
||||
|
||||
|
||||
if (static_cast<int>(layerNr - downSkinCount) >= 0)
|
||||
{
|
||||
SliceLayer& layer2 = storage.layers[layerNr - downSkinCount];
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
auto getInsidePolygons = [&part](SliceLayer& layer2)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
downskin = downskin.difference(part2.insets.back());
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
result.add(part2.insets.back());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
if (no_small_gaps_heuristic)
|
||||
{
|
||||
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
|
||||
{
|
||||
downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
{
|
||||
upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
else
|
||||
{
|
||||
SliceLayer& layer2 = storage.layers[layerNr + upSkinCount];
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
if (layer_nr > 0 && downSkinCount > 0)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
upskin = upskin.difference(part2.insets.back());
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
|
||||
}
|
||||
upskin = upskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
}
|
||||
|
||||
Polygons skin = upskin.unionPolygons(downskin);
|
||||
|
||||
|
||||
skin.removeSmallAreas(MIN_AREA_SIZE);
|
||||
|
||||
for (PolygonsPart& skin_area_part : skin.splitIntoParts())
|
||||
@@ -121,31 +153,52 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
|
||||
}
|
||||
}
|
||||
|
||||
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount)
|
||||
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
for(int n=1; n<amount; n++)
|
||||
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
|
||||
{
|
||||
if (layerNr < n)
|
||||
break;
|
||||
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - n];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
return;
|
||||
}
|
||||
if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* We need to round down the layer index we start at to the nearest
|
||||
divisible index. Otherwise we get some parts that have infill at divisible
|
||||
layers and some at non-divisible layers. Those layers would then miss each
|
||||
other. */
|
||||
size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
|
||||
min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
|
||||
size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
|
||||
max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
|
||||
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layer_idx];
|
||||
|
||||
for(unsigned int n = 1;n < amount;n++)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
if(layer_idx < n)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
|
||||
result.add(intersection);
|
||||
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
|
||||
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SliceLayer* layer2 = &storage.layers[layer_idx - n];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
{
|
||||
if(part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
|
||||
result.add(intersection);
|
||||
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
|
||||
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
part.infill_area.push_back(result);
|
||||
}
|
||||
|
||||
part.infill_area.push_back(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-3
@@ -26,11 +26,13 @@ void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusion
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
* \param innermost_wall_extrusion_width The line width of the inner most wall
|
||||
* \param insetCount The number of perimeters to surround the skin
|
||||
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
|
||||
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
|
||||
*/
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas (outlines)
|
||||
@@ -40,8 +42,9 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
|
||||
*/
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, bool no_small_gaps_heuristic);
|
||||
|
||||
/*!
|
||||
* Generate the skin insets.
|
||||
@@ -63,7 +66,18 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
|
||||
* \param infill_skin_overlap overlap distance between infill and skin
|
||||
*/
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
|
||||
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount);
|
||||
|
||||
/*!
|
||||
* \brief Combines the infill of multiple layers for a specified mesh.
|
||||
*
|
||||
* The infill layers are combined while the thickness of each layer is
|
||||
* multiplied such that the infill should fill up again to the full height of
|
||||
* all combined layers.
|
||||
*
|
||||
* \param storage The mesh to combine the infill layers of.
|
||||
* \param amount The number of layers to combine.
|
||||
*/
|
||||
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+41
-14
@@ -26,28 +26,55 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
|
||||
}
|
||||
|
||||
|
||||
Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only)
|
||||
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
|
||||
{
|
||||
Polygons total;
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getOutlines(total, external_polys_only);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
if (layer_nr < 0)
|
||||
{ // when processing raft
|
||||
if (include_helper_parts)
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
if (external_polys_only)
|
||||
{
|
||||
std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
|
||||
Polygons result;
|
||||
for (PolygonsPart& part : parts)
|
||||
{
|
||||
result.add(part.outerPolygon());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return raftOutline;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Polygons();
|
||||
}
|
||||
}
|
||||
if (include_helper_parts)
|
||||
else
|
||||
{
|
||||
if (support.generated)
|
||||
Polygons total;
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
total.add(support.supportLayers[layer_nr].supportAreas);
|
||||
total.add(support.supportLayers[layer_nr].roofs);
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getOutlines(total, external_polys_only);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
}
|
||||
}
|
||||
total.add(primeTower.ground_poly);
|
||||
if (include_helper_parts)
|
||||
{
|
||||
if (support.generated)
|
||||
{
|
||||
total.add(support.supportLayers[layer_nr].supportAreas);
|
||||
total.add(support.supportLayers[layer_nr].roofs);
|
||||
}
|
||||
total.add(primeTower.ground_poly);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -163,11 +163,11 @@ public:
|
||||
/*!
|
||||
* Get all outlines within a given layer.
|
||||
*
|
||||
* \param layer_nr the index of the layer for which to get the outlines
|
||||
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
|
||||
* \param include_helper_parts whether to include support and prime tower
|
||||
* \param external_polys_only whether to disregard all hole polygons
|
||||
*/
|
||||
Polygons getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only = false);
|
||||
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+28
-25
@@ -41,11 +41,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
storage.support.supportLayers.emplace_back();
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
std::vector<Polygons> supportAreas;
|
||||
supportAreas.resize(layer_count, Polygons());
|
||||
generateSupportAreas(storage, &mesh, layer_count, supportAreas, commandSocket);
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
|
||||
|
||||
if (mesh.getSettingBoolean("support_roof_enable"))
|
||||
{
|
||||
@@ -79,43 +80,45 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
*
|
||||
* for support buildplate only: purge all support not connected to buildplate
|
||||
*/
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, 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, CommandSocket* commandSocket)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
// given settings
|
||||
ESupportType support_type = object->getSettingAsSupportType("support_type");
|
||||
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
|
||||
|
||||
if (!object->getSettingBoolean("support_enable"))
|
||||
if (!mesh.getSettingBoolean("support_enable"))
|
||||
return;
|
||||
if (support_type == ESupportType::NONE)
|
||||
return;
|
||||
|
||||
double supportAngle = object->getSettingInAngleRadians("support_angle");
|
||||
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
|
||||
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
|
||||
int supportZDistance = object->getSettingInMicrons("support_z_distance");
|
||||
int supportZDistanceBottom = object->getSettingInMicrons("support_bottom_distance");
|
||||
int supportZDistanceTop = object->getSettingInMicrons("support_top_distance");
|
||||
int join_distance = object->getSettingInMicrons("support_join_distance");
|
||||
int support_bottom_stair_step_height = object->getSettingInMicrons("support_bottom_stair_step_height");
|
||||
int smoothing_distance = object->getSettingInMicrons("support_area_smoothing");
|
||||
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");
|
||||
int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
|
||||
int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing");
|
||||
|
||||
int extension_offset = object->getSettingInMicrons("support_offset");
|
||||
int extension_offset = mesh.getSettingInMicrons("support_offset");
|
||||
|
||||
int supportTowerDiameter = object->getSettingInMicrons("support_tower_diameter");
|
||||
int supportMinAreaSqrt = object->getSettingInMicrons("support_minimal_diameter");
|
||||
double supportTowerRoofAngle = object->getSettingInAngleRadians("support_tower_roof_angle");
|
||||
int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
|
||||
int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
|
||||
double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
|
||||
|
||||
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
|
||||
|
||||
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
|
||||
int z_layer_distance_tower = 1; // start tower directly below overhang point
|
||||
|
||||
int layerThickness = object->getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = object->getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = object->getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
int layerThickness = mesh.getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
|
||||
bool conical_support = object->getSettingBoolean("support_conical_enabled");
|
||||
double conical_support_angle = object->getSettingInAngleRadians("support_conical_angle");
|
||||
int64_t conical_smallest_breadth = object->getSettingInMicrons("support_conical_min_width");
|
||||
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:
|
||||
|
||||
@@ -160,7 +163,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
|
||||
|
||||
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
|
||||
AreaSupport::detectOverhangPoints(storage, *object, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
|
||||
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
|
||||
|
||||
|
||||
bool still_in_upper_empty_layers = true;
|
||||
@@ -173,7 +176,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
Polygons overhang;
|
||||
{
|
||||
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
|
||||
Polygons supportLayer_supportee = object->layers[layer_idx+layerZdistanceTop].getOutlines();
|
||||
Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
|
||||
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
|
||||
|
||||
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
|
||||
@@ -249,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
|
||||
still_in_upper_empty_layers = false;
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, support_layer_count - layer_idx, support_layer_count, commandSocket);
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
|
||||
+2
-2
@@ -26,11 +26,11 @@ private:
|
||||
* This function also handles small overhang areas (creates towers with larger diameter than just the overhang area) and single walls which could otherwise fall over.
|
||||
*
|
||||
* \param storage data storage containing the input layer outline data
|
||||
* \param object The object for which to generate support areas
|
||||
* \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, SliceMeshStorage* object, 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, CommandSocket* commandSocket);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,15 @@ void TimeEstimateCalculator::setPosition(Position newPos)
|
||||
currentPosition = newPos;
|
||||
}
|
||||
|
||||
void TimeEstimateCalculator::addTime(double time)
|
||||
{
|
||||
extra_time += time;
|
||||
}
|
||||
|
||||
|
||||
void TimeEstimateCalculator::reset()
|
||||
{
|
||||
extra_time = 0.0;
|
||||
blocks.clear();
|
||||
}
|
||||
|
||||
@@ -190,7 +197,7 @@ double TimeEstimateCalculator::calculate()
|
||||
forward_pass();
|
||||
recalculate_trapezoids();
|
||||
|
||||
double totalTime = 0;
|
||||
double totalTime = extra_time;
|
||||
for(unsigned int n=0; n<blocks.size(); n++)
|
||||
{
|
||||
Block& block = blocks[n];
|
||||
@@ -287,7 +294,7 @@ void TimeEstimateCalculator::recalculate_trapezoids()
|
||||
Block *current;
|
||||
Block *next = nullptr;
|
||||
|
||||
for(unsigned int n=0; n<blocks.size(); n--)
|
||||
for(unsigned int n=0; n<blocks.size(); n++)
|
||||
{
|
||||
current = next;
|
||||
next = &blocks[n];
|
||||
|
||||
+12
-4
@@ -7,10 +7,10 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/**
|
||||
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
|
||||
Some of this code has been addapted from the Marlin sources.
|
||||
*/
|
||||
/*!
|
||||
* The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
|
||||
* Some of this code has been adapted from the Marlin sources.
|
||||
*/
|
||||
|
||||
class TimeEstimateCalculator
|
||||
{
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
double extra_time;
|
||||
|
||||
Position previous_feedrate;
|
||||
double previous_nominal_feedrate;
|
||||
|
||||
@@ -61,8 +63,14 @@ private:
|
||||
|
||||
std::vector<Block> blocks;
|
||||
public:
|
||||
TimeEstimateCalculator()
|
||||
: extra_time(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
void setPosition(Position newPos);
|
||||
void plan(Position newPos, double feedRate);
|
||||
void addTime(double time);
|
||||
void reset();
|
||||
|
||||
double calculate();
|
||||
|
||||
+18
-6
@@ -18,7 +18,7 @@ public:
|
||||
Point min, max;
|
||||
|
||||
AABB()
|
||||
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
}
|
||||
AABB(Point&min, Point& max)
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
}
|
||||
AABB(Polygons& polys)
|
||||
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
calculate(polys);
|
||||
}
|
||||
@@ -39,10 +39,7 @@ public:
|
||||
{
|
||||
for(unsigned int j=0; j<polys[i].size(); j++)
|
||||
{
|
||||
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
|
||||
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
|
||||
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
|
||||
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
|
||||
include(polys[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +52,21 @@ public:
|
||||
if (min.Y > other.max.Y) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Includes the specified point in the bounding box.
|
||||
*
|
||||
* The bounding box is expanded if the point is not within the bounding box.
|
||||
*
|
||||
* \param point The point to include in the bounding box.
|
||||
*/
|
||||
void include(Point point)
|
||||
{
|
||||
min.X = std::min(min.X,point.X);
|
||||
min.Y = std::min(min.Y,point.Y);
|
||||
max.X = std::max(max.X,point.X);
|
||||
max.Y = std::max(max.Y,point.Y);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
+73
-10
@@ -6,6 +6,7 @@
|
||||
#include "polygon.h"
|
||||
#include "intpoint.h"
|
||||
#include "AABB.h"
|
||||
#include "logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -18,7 +19,8 @@ public:
|
||||
GRAY,
|
||||
RED,
|
||||
BLUE,
|
||||
GREEN
|
||||
GREEN,
|
||||
YELLOW
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -33,6 +35,7 @@ private:
|
||||
case SVG::Color::RED: return "red";
|
||||
case SVG::Color::BLUE: return "blue";
|
||||
case SVG::Color::GREEN: return "green";
|
||||
case SVG::Color::YELLOW: return "yellow";
|
||||
default: return "black";
|
||||
}
|
||||
}
|
||||
@@ -51,8 +54,12 @@ public:
|
||||
, scale(std::min(double(canvas_size.X - 20) / aabb_size.X, double(canvas_size.Y - 20) / aabb_size.Y))
|
||||
{
|
||||
out = fopen(filename, "w");
|
||||
if(!out)
|
||||
{
|
||||
logError("The file %s could not be opened for writing.",filename);
|
||||
}
|
||||
fprintf(out, "<!DOCTYPE html><html><body>\n");
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.Y, canvas_size.X);
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.X, canvas_size.Y);
|
||||
|
||||
// fprintf(out, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
|
||||
// fprintf(out, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
|
||||
@@ -69,7 +76,7 @@ public:
|
||||
/*!
|
||||
* transform a point in real space to canvas space
|
||||
*/
|
||||
Point transform(Point& p)
|
||||
Point transform(const Point& p)
|
||||
{
|
||||
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + Point(10,10);
|
||||
}
|
||||
@@ -123,7 +130,7 @@ public:
|
||||
for(Point& p : poly)
|
||||
{
|
||||
Point fp = transform(p);
|
||||
fprintf(out, "%lli,%lli ", fp.Y, fp.X);
|
||||
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
|
||||
}
|
||||
if (j == 0)
|
||||
fprintf(out, "\" style=\"fill:%s;stroke:%s;stroke-width:1\" />\n", toString(color).c_str(), toString(outline_color).c_str());
|
||||
@@ -133,14 +140,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void writePoint(Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
|
||||
{
|
||||
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
|
||||
for(Point& point : polygon) //Add every point to the list of points.
|
||||
{
|
||||
Point transformed = transform(point);
|
||||
fprintf(out,"%lli,%lli ",transformed.X,transformed.Y);
|
||||
}
|
||||
fprintf(out,"\" />\n"); //The end of the polygon tag.
|
||||
}
|
||||
|
||||
void writePoint(const Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
{
|
||||
Point pf = transform(p);
|
||||
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.Y, pf.X, size, toString(color).c_str(), toString(color).c_str());
|
||||
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.X, pf.Y, size, toString(color).c_str(), toString(color).c_str());
|
||||
|
||||
if (write_coords)
|
||||
{
|
||||
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.Y, pf.X, p.X, p.Y);
|
||||
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.X, pf.Y, p.X, p.Y);
|
||||
}
|
||||
}
|
||||
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
@@ -159,14 +177,59 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void writeLine(Point& a, Point& b, Color color = Color::BLACK)
|
||||
/*!
|
||||
* \brief Draws a polyline on the canvas.
|
||||
*
|
||||
* The polyline is the set of line segments between each pair of consecutive
|
||||
* points in the specified vector.
|
||||
*
|
||||
* \param polyline A set of points between which line segments must be
|
||||
* drawn.
|
||||
* \param color The colour of the line segments. If this is not specified,
|
||||
* black will be used.
|
||||
*/
|
||||
void writeLines(std::vector<Point> polyline,Color color = Color::BLACK)
|
||||
{
|
||||
if(polyline.size() <= 1) //Need at least 2 points.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />", a.Y, a.X, b.Y, b.X, toString(color).c_str());
|
||||
Point transformed = transform(polyline[0]); //Element 0 must exist due to the check above.
|
||||
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"1\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
|
||||
for(size_t point = 1;point < polyline.size();point++)
|
||||
{
|
||||
transformed = transform(polyline[point]);
|
||||
fprintf(out,"L%lli,%lli",transformed.X,transformed.Y); //Write a line segment to the next point.
|
||||
}
|
||||
fprintf(out,"\" />\n"); //Write the end of the tag.
|
||||
}
|
||||
|
||||
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
|
||||
{
|
||||
Point fa = transform(a);
|
||||
Point fb = transform(b);
|
||||
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Draws a dashed line on the canvas from point A to point B.
|
||||
*
|
||||
* This is useful in the case where multiple lines may overlap each other.
|
||||
*
|
||||
* \param a The starting endpoint of the line.
|
||||
* \param b The ending endpoint of the line.
|
||||
* \param color The stroke colour of the line.
|
||||
*/
|
||||
void writeDashedLine(const Point& a,const Point& b,Color color = Color::BLACK)
|
||||
{
|
||||
Point fa = transform(a);
|
||||
Point fb = transform(b);
|
||||
fprintf(out,"<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" stroke=\"%s\" stroke-width=\"1\" stroke-dasharray=\"5,5\" />\n",fa.X,fa.Y,fb.X,fb.Y,toString(color).c_str());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void printf(const char* txt, Args... args)
|
||||
void printf(const char* txt, Args&&... args)
|
||||
{
|
||||
fprintf(out, txt, args...);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
* \param c the second point on the line segment
|
||||
* \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1)
|
||||
*/
|
||||
static int64_t getDist2FromLineSegment(Point& a, Point& b, Point& c, char* b_is_beyond_ac = nullptr)
|
||||
static int64_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, char* b_is_beyond_ac = nullptr)
|
||||
{
|
||||
/*
|
||||
* a,
|
||||
|
||||
@@ -30,7 +30,9 @@ enum PolygonType
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType
|
||||
SupportInfillType,
|
||||
MoveCombingType,
|
||||
MoveRetractionType
|
||||
};
|
||||
|
||||
|
||||
@@ -81,7 +83,7 @@ public:
|
||||
ClipperLib::Path& operator*() { return *polygon; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args... args)
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
polygon->emplace_back(args...);
|
||||
}
|
||||
|
||||
@@ -104,8 +104,13 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
Point ab = b - a;
|
||||
Point ap = p - a;
|
||||
int64_t ab_length = vSize(ab);
|
||||
if(ab_length <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
|
||||
{
|
||||
p1 = p2; //Skip only one of the points.
|
||||
continue;
|
||||
}
|
||||
int64_t ax_length = dot(ab, ap) / ab_length;
|
||||
if (ax_length < 0) // x is projected to before ab
|
||||
if (ax_length <= 0) // x is projected to before ab
|
||||
{
|
||||
if (projected_p_beyond_prev_segment)
|
||||
{ // case which looks like: > .
|
||||
@@ -120,7 +125,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4));
|
||||
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;
|
||||
}
|
||||
@@ -134,7 +139,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ax_length > ab_length) // x is projected to beyond ab
|
||||
else if (ax_length >= ab_length) // x is projected to beyond ab
|
||||
{
|
||||
projected_p_beyond_prev_segment = true;
|
||||
p0 = p1;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "LinearAlg2DTest.h"
|
||||
|
||||
#include <../src/utils/linearAlg2D.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(LinearAlg2DTest);
|
||||
|
||||
void LinearAlg2DTest::setUp()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::tearDown()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getDist2FromLineSegmentTest()
|
||||
{
|
||||
char is_beyond;
|
||||
|
||||
//Horizontal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,3),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the horizontal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,0),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,3),Point(100,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Vertical line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(25), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(5,25),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,25),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the vertical line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,200),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,-100),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,0),Point(0,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//45 degrees diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(50), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(30,20),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,25),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,200),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-100),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(18), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,-3),Point(100,100),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Arbitrary degree diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(320), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(20,30),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(40,20),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
//Points beyond the diagonal line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,100),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-50),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(20), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-2,4),Point(100,50),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
|
||||
//Zero-distance line.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(100), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(10,0),&is_beyond));
|
||||
CPPUNIT_ASSERT(is_beyond != 0); //It's beyond, but which direction it is beyond in doesn't matter.
|
||||
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(0,0),&is_beyond));
|
||||
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef LINEARALG2DTEST_H
|
||||
#define LINEARALG2DTEST_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class LinearAlg2DTest : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(LinearAlg2DTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentTest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets up the test suite to prepare for testing.
|
||||
*
|
||||
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
|
||||
* needs to be created here.
|
||||
*/
|
||||
void setUp();
|
||||
|
||||
/*!
|
||||
* \brief Tears down the test suite when testing is done.
|
||||
*
|
||||
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
|
||||
* exists that needs to be destroyed.
|
||||
*/
|
||||
void tearDown();
|
||||
|
||||
/*!
|
||||
* \brief Tests the LinearAlg2D#getDist2FromLineSegment function.
|
||||
*/
|
||||
void getDist2FromLineSegmentTest();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LINEARALG2DTEST_H
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/XmlOutputter.h>
|
||||
|
||||
/*!
|
||||
* \brief Runs the test cases.
|
||||
*/
|
||||
int main(int argc,char** argv)
|
||||
{
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
|
||||
//Set the output type to be JUnit-style XML.
|
||||
std::ofstream output("output.xml");
|
||||
CppUnit::XmlOutputter* outputter = new CppUnit::XmlOutputter(&runner.result(), output);
|
||||
runner.setOutputter(outputter);
|
||||
|
||||
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
|
||||
runner.addTest(registry.makeTest()); //Makes a test suite from all test cases that are registered with CPPUNIT_TEST_SUITE_REGISTRATION().
|
||||
bool success = runner.run("", false); //Run the tests!
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário