Comparar commits
21 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| f1d59af2a9 | |||
| 7d354ca587 | |||
| 7560250348 | |||
| 9b7a985b54 | |||
| db0c47e314 | |||
| 4aed36d7d5 | |||
| 44b0522447 | |||
| 464fb29bca | |||
| fbf989282b | |||
| db99b46506 | |||
| c7ffd5b23d | |||
| 56d6d9196f | |||
| e9c9157ac8 | |||
| 8ad0416428 | |||
| c185c846e6 | |||
| e28cef3272 | |||
| 777f45ac16 | |||
| a9a3c8a4b7 | |||
| ad2cf3b7a6 | |||
| a57ec01cc4 | |||
| c787ffad02 |
+8
-44
@@ -17,10 +17,6 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
@@ -29,8 +25,8 @@ option(BUILD_TESTS OFF)
|
||||
|
||||
# Add a compiler flag to check the output for insane values if we are in debug mode.
|
||||
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
message(STATUS "Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
endif()
|
||||
|
||||
# Add warnings
|
||||
@@ -55,12 +51,12 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/gcodePlanner.cpp
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/LayerPlanBuffer.cpp
|
||||
src/Material.cpp
|
||||
src/MaterialBase.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
src/multiVolumes.cpp
|
||||
src/pathOrderOptimizer.cpp
|
||||
src/PrimeTower.cpp
|
||||
src/Progress.cpp
|
||||
@@ -70,27 +66,14 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/skin.cpp
|
||||
src/skirt.cpp
|
||||
src/sliceDataStorage.cpp
|
||||
src/slicer.cpp
|
||||
src/support.cpp
|
||||
src/timeEstimate.cpp
|
||||
src/TexturedMesh.cpp
|
||||
src/TextureProcessor.cpp
|
||||
src/wallOverlap.cpp
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
|
||||
src/infill/NoZigZagConnectorProcessor.cpp
|
||||
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
|
||||
|
||||
src/slicer/LayerPart.cpp
|
||||
src/slicer/MultiVolumes.cpp
|
||||
src/slicer/SlicerLayer.cpp
|
||||
src/slicer/Slicer.cpp
|
||||
|
||||
src/utils/gettime.cpp
|
||||
src/utils/LinearAlg2D.cpp
|
||||
src/utils/logoutput.cpp
|
||||
src/utils/polygonUtils.cpp
|
||||
src/utils/polygon.cpp
|
||||
@@ -98,12 +81,7 @@ set(engine_SRCS # Except main.cpp.
|
||||
|
||||
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
|
||||
set(engine_TEST
|
||||
GCodePlannerTest
|
||||
)
|
||||
set(engine_TEST_INFILL
|
||||
)
|
||||
set(engine_TEST_UTILS
|
||||
BucketGrid2DTest
|
||||
GCodePlannerTest
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
@@ -136,24 +114,10 @@ if (BUILD_TESTS)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
foreach (test ${engine_TEST_INFILL})
|
||||
add_executable(${test} tests/main.cpp tests/infill/${test}.cpp)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
foreach (test ${engine_TEST_UTILS})
|
||||
add_executable(${test} tests/main.cpp tests/utils/${test}.cpp)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
add_custom_command(TARGET CuraEngine POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:CuraEngine>)
|
||||
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
include(CPackConfig.cmake)
|
||||
|
||||
|
||||
@@ -98,7 +98,3 @@ message Setting {
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 8
|
||||
message SlicingFinished {
|
||||
}
|
||||
|
||||
+1
-1
@@ -178,7 +178,7 @@ JAVADOC_AUTOBRIEF = NO
|
||||
# requiring an explicit \brief command for a brief description.)
|
||||
# The default value is: NO.
|
||||
|
||||
QT_AUTOBRIEF = YES
|
||||
QT_AUTOBRIEF = NO
|
||||
|
||||
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
|
||||
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "Command line setting defaults CuraEngine",
|
||||
"author": "Ultimaker B.V.",
|
||||
"categories": {
|
||||
"command_line_settings": {
|
||||
"label": "Command Line Settings",
|
||||
"settings": {
|
||||
"center_object": {
|
||||
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"machine_print_temp_wait": {
|
||||
"description": "Whether to wait for the nozzle temperature to be reached when preheating the nozzles at the start of the gcode.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"mesh_position_x": {
|
||||
"description": "Offset applied to the object in the x direction.",
|
||||
"type": "float",
|
||||
"default": 0
|
||||
},
|
||||
"mesh_position_y": {
|
||||
"description": "Offset applied to the object in the y direction.",
|
||||
"type": "float",
|
||||
"default": 0
|
||||
},
|
||||
"mesh_position_z": {
|
||||
"description": "Offset applied to the object in the z direction. With this you can perform what was used to call 'Object Sink'.",
|
||||
"type": "float",
|
||||
"default": 0
|
||||
},
|
||||
"prime_tower_dir_outward": {
|
||||
"description": "Whether to start printing in the middle of the prime tower and end up at the perimeter, or the other way around. This is only used for certain types of prime tower.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+158
-140
@@ -12,6 +12,8 @@ namespace cura
|
||||
|
||||
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
PrimeTower primetower();
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
if (meshgroup_number == 1)
|
||||
@@ -19,10 +21,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->beginGCode();
|
||||
|
||||
setConfigFanSpeedLayerTime();
|
||||
|
||||
@@ -72,11 +72,17 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
processLayer(storage, layer_nr, total_layers, has_raft);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
|
||||
|
||||
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
|
||||
max_object_height = std::max(max_object_height, storage.model_max.z);
|
||||
|
||||
|
||||
if (command_socket)
|
||||
{
|
||||
command_socket->sendGCodeLayer();
|
||||
command_socket->endSendSlicedObject();
|
||||
}
|
||||
|
||||
layer_plan_buffer.flush();
|
||||
}
|
||||
|
||||
@@ -98,9 +104,12 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
|
||||
CoastingConfig& coasting_config = storage.coasting_config.back();
|
||||
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
|
||||
coasting_config.coasting_volume = train->getSettingInCubicMillimeters("coasting_volume");
|
||||
coasting_config.coasting_min_volume = train->getSettingInCubicMillimeters("coasting_min_volume");
|
||||
coasting_config.coasting_speed = train->getSettingInPercentage("coasting_speed") / 100.0;
|
||||
coasting_config.coasting_volume_move = train->getSettingInCubicMillimeters("coasting_volume_move");
|
||||
coasting_config.coasting_min_volume_move = train->getSettingInCubicMillimeters("coasting_min_volume_move");
|
||||
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move") / 100.0;
|
||||
coasting_config.coasting_volume_retract = train->getSettingInCubicMillimeters("coasting_volume_retract");
|
||||
coasting_config.coasting_min_volume_retract = train->getSettingInCubicMillimeters("coasting_min_volume_retract");
|
||||
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,19 +173,20 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
|
||||
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
|
||||
}
|
||||
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
if (!command_socket)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << "FLAVOR:" << toString(gcode.getFlavor());
|
||||
@@ -238,8 +248,7 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
gcode.resetExtrusionValue();
|
||||
gcode.setZ(max_object_height + 5000);
|
||||
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
|
||||
gcode.writeMove(last_position_planned, getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
@@ -264,37 +273,25 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
|
||||
}
|
||||
|
||||
// some infill config for all lines infill generation below
|
||||
Polygons* in_between = nullptr;
|
||||
int offset_from_poly_outline = 0;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
|
||||
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
|
||||
|
||||
{ // raft base layer
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 2;
|
||||
int layer_height = getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
|
||||
{
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
}
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
double fill_angle = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
|
||||
int offset_from_poly_outline = 0;
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_base_config.getLineWidth());
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -307,21 +304,17 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
int layer_nr = -n_raft_surface_layers - 1;
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config);
|
||||
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_interface_config.getLineWidth());
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -336,21 +329,17 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
{ // raft surface layers
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raft_lines;
|
||||
int offset_from_poly_outline = 0;
|
||||
double fill_angle = 90 * raftSurfaceLayer;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raft_lines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
|
||||
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config);
|
||||
sendPolygons(SupportType, layer_nr, raft_lines, storage.raft_surface_config.getLineWidth());
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
@@ -362,7 +351,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
|
||||
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, command_socket);
|
||||
|
||||
int layer_thickness = getSettingInMicrons("layer_height");
|
||||
if (layer_nr == 0)
|
||||
@@ -370,21 +359,18 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
int max_inner_wall_width = 0;
|
||||
int max_nozzle_size = 0;
|
||||
std::vector<bool> extruders_used = storage.getExtrudersUsed(layer_nr);
|
||||
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
if (extruders_used[extr_nr])
|
||||
{
|
||||
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(extr_nr);
|
||||
max_inner_wall_width = std::max(max_inner_wall_width, extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"));
|
||||
max_nozzle_size = std::max(max_nozzle_size, storage.meshgroup->getExtruderTrain(extr_nr)->getSettingInMicrons("machine_nozzle_size"));
|
||||
}
|
||||
}
|
||||
ExtruderTrain* current_extruder_train = storage.meshgroup->getExtruderTrain(current_extruder_planned);
|
||||
|
||||
int64_t comb_offset_from_outlines = current_extruder_train->getSettingInMicrons((current_extruder_train->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 2; // TODO: only used when there is no second wall.
|
||||
int64_t comb_offset_from_outlines = max_nozzle_size * 2;// TODO: only used when there is no second wall.
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
@@ -460,20 +446,21 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int layer_height_0 = getSettingInMicrons("layer_height_0");
|
||||
int layer_height = getSettingInMicrons("layer_height");
|
||||
|
||||
|
||||
int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
|
||||
|
||||
|
||||
if (int(layer_nr) > max_screen_layer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
|
||||
|
||||
}
|
||||
|
||||
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int current_extruder)
|
||||
@@ -544,7 +531,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, Slic
|
||||
lines.add(p);
|
||||
}
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config);
|
||||
|
||||
}
|
||||
|
||||
@@ -562,16 +549,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
return;
|
||||
}
|
||||
|
||||
int extruder_nr = mesh->getSettingAsIndex("extruder_nr");
|
||||
|
||||
{ // TODO: only do this for dual color texture
|
||||
if (layer_nr % 2 == 0)
|
||||
{
|
||||
extruder_nr = 1 - extruder_nr;
|
||||
}
|
||||
}
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, extruder_nr);
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
|
||||
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
@@ -590,29 +568,37 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
int infill_angle = 45;
|
||||
if ((infill_pattern == EFillMethod::LINES || infill_pattern == EFillMethod::ZIG_ZAG))
|
||||
if ((infill_pattern==EFillMethod::LINES || infill_pattern==EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
unsigned int combined_infill_layers = mesh->getSettingInMicrons("infill_sparse_thickness") / std::max(mesh->getSettingInMicrons("layer_height"), 1);
|
||||
if ((combined_infill_layers & 1 && layer_nr & 1) || (!(combined_infill_layers & 1) && (layer_nr / 2) & 1))
|
||||
{ // odd combine count and odd, or even combine count and switch direction every two layers
|
||||
infill_angle += 90;
|
||||
}
|
||||
infill_angle += 90;
|
||||
}
|
||||
int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
int infill_overlap = mesh->getSettingInMicrons("infill_overlap");
|
||||
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
|
||||
|
||||
int wall_reinforcement_line_distance = mesh->getSettingInMicrons("wall_reinforcement_line_distance");
|
||||
int wall_reinforcement_line_width = mesh->wall_reinforcement_config.getLineWidth();
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
for (unsigned int wall_idx = part.reinforcement_walls.size() - 1; int(wall_idx) >= 0; wall_idx--)
|
||||
{
|
||||
ReinforcementWall& reinforcement_wall = part.reinforcement_walls[wall_idx];
|
||||
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, true);
|
||||
}
|
||||
}
|
||||
|
||||
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
|
||||
|
||||
if (!mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
for (ReinforcementWall& reinforcement_wall : part.reinforcement_walls)
|
||||
{
|
||||
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, false);
|
||||
}
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
}
|
||||
@@ -626,14 +612,12 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
|
||||
skin_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = infill_overlap;
|
||||
int64_t skin_overlap = 0;
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
|
||||
|
||||
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
|
||||
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
|
||||
{
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
}
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons("machine_nozzle_size") * 1);
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
@@ -645,25 +629,25 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
|
||||
|
||||
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle, int extrusion_width)
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
if (infill_line_distance > 0)
|
||||
{
|
||||
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
|
||||
for(unsigned int n=1; n<part.infill_area.size(); n++)
|
||||
{
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
Infill infill_comp(infill_pattern, part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
Infill infill_comp(mesh->getSettingAsFillMethod("infill_pattern"), part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n]);
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle, int extrusion_width)
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
|
||||
if (infill_line_distance == 0 || part.infill_area.size() == 0)
|
||||
@@ -681,12 +665,61 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0]);
|
||||
}
|
||||
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out)
|
||||
{
|
||||
if (wall_reinforcement_line_distance == 0 || (reinforcement_wall.wall_reinforcement_area.size() == 0 && reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (inside_out)
|
||||
{
|
||||
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
|
||||
}
|
||||
|
||||
processWallReinforcement_infill(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width);
|
||||
|
||||
if (!inside_out)
|
||||
{
|
||||
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out)
|
||||
{
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
|
||||
{
|
||||
for(int inset_number=reinforcement_wall.wall_reinforcement_axtra_walls.size()-1; inset_number>-1; inset_number--)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(reinforcement_wall.wall_reinforcement_axtra_walls[inset_number], &mesh->insetX_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
void FffGcodeWriter::processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width)
|
||||
{
|
||||
if (reinforcement_wall.wall_reinforcement_area.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("wall_reinforcement_pattern");
|
||||
Infill infill_comp(pattern, reinforcement_wall.wall_reinforcement_area, 0, false, wall_reinforcement_line_width, wall_reinforcement_line_distance, infill_overlap, infill_angle, false, false);
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->wall_reinforcement_config);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->wall_reinforcement_config);
|
||||
sendPolygons(SupportInfillType, layer_nr, infill_lines, wall_reinforcement_line_width);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
|
||||
@@ -725,7 +758,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
}
|
||||
|
||||
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_overlap, int infill_angle, int extrusion_width)
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
|
||||
{
|
||||
@@ -742,10 +775,15 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
}
|
||||
Polygons* inner_skin_outline = nullptr;
|
||||
int offset_from_inner_skin_outline = 0;
|
||||
if (pattern != EFillMethod::CONCENTRIC)
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Polygons& skin_perimeter : skin_part.insets)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_perimeter, mesh->skin_config.getLineWidth());
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
|
||||
}
|
||||
if (skin_part.insets.size() > 0)
|
||||
@@ -754,13 +792,7 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, skin_part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, skin_lines, in_between);
|
||||
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -774,30 +806,17 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
|
||||
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
|
||||
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config);
|
||||
sendPolygons(SkinType, layer_nr, skin_polygons, mesh->skin_config.getLineWidth());
|
||||
sendPolygons(SkinType, layer_nr, skin_lines, mesh->skin_config.getLineWidth());
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
Polygons perimeter_gap_lines;
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
|
||||
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +866,7 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int support_line_distance = getSettingInMicrons("support_line_distance");
|
||||
int extrusion_width = storage.support_config.getLineWidth();
|
||||
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
|
||||
@@ -855,6 +874,7 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
|
||||
|
||||
@@ -873,25 +893,21 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
{
|
||||
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
|
||||
|
||||
int infill_overlap = 0; // support infill should not be expanded outward
|
||||
|
||||
int offset_from_outline = 0;
|
||||
bool remove_overlapping_perimeters = false;
|
||||
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
Polygons boundary;
|
||||
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
|
||||
offset_from_outline = -extrusion_width;
|
||||
infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInMicrons("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
|
||||
}
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, remove_overlapping_perimeters, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, false, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
Polygons support_polygons;
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(island, &storage.support_config);
|
||||
sendPolygons(SupportType, layer_nr, island, storage.support_config.getLineWidth());
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
|
||||
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
|
||||
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,7 +939,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
|
||||
}
|
||||
int infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
double infill_overlap = 0;
|
||||
int outline_offset = 0;
|
||||
|
||||
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
|
||||
@@ -932,7 +948,9 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config);
|
||||
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
|
||||
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
@@ -969,12 +987,12 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
|
||||
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
|
||||
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
|
||||
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
if (command_socket)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
|
||||
@@ -984,7 +1002,7 @@ void FffGcodeWriter::finalize()
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
}
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix.str());
|
||||
command_socket->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
|
||||
+114
-153
@@ -17,7 +17,6 @@
|
||||
#include "commandSocket.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
@@ -37,73 +36,53 @@ class FffGcodeWriter : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
|
||||
private:
|
||||
int max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
|
||||
|
||||
/*!
|
||||
* The number of the current meshgroup being processed.
|
||||
*
|
||||
* Used for sequential printing of objects.
|
||||
* The first meshgroup will get number 1.
|
||||
*/
|
||||
int meshgroup_number;
|
||||
|
||||
/*
|
||||
* Buffer for all layer plans (of type GCodePlanner)
|
||||
*
|
||||
* The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used.
|
||||
* Another reason is to perform Auto Temperature.
|
||||
*/
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
/*!
|
||||
* The class holding the current state of the gcode being written.
|
||||
*
|
||||
* It holds information such as the last written position etc.
|
||||
*/
|
||||
int max_object_height;
|
||||
int meshgroup_number; //!< used for sequential printing of objects
|
||||
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
GCodeExport gcode;
|
||||
|
||||
/*!
|
||||
* The gcode file to write to when using CuraEngine as command line tool.
|
||||
*/
|
||||
CommandSocket* command_socket;
|
||||
std::ofstream output_file;
|
||||
|
||||
|
||||
/*!
|
||||
* Layer number of the last layer in which a prime tower has been printed per extruder train.
|
||||
*
|
||||
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
|
||||
*/
|
||||
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
|
||||
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //!< The settings used relating to minimal layer time and fan speeds.
|
||||
|
||||
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings;
|
||||
|
||||
Point last_position_planned; //!< The position of the head before planning the next layer
|
||||
int current_extruder_planned; //!< The extruder train in use before planning the next layer
|
||||
public:
|
||||
FffGcodeWriter(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, layer_plan_buffer(this, gcode)
|
||||
, layer_plan_buffer(this, command_socket, gcode)
|
||||
, last_position_planned(no_point)
|
||||
, current_extruder_planned(0) // TODO: make configurable
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
max_object_height = 0;
|
||||
command_socket = NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Reset the meshgroup number to process the next slicing.
|
||||
*/
|
||||
void resetMeshGroupNumber()
|
||||
void resetFileNumber()
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the target to write gcode to: to a file.
|
||||
*
|
||||
* Used when CuraEngine is used as command line tool.
|
||||
*
|
||||
* \param filename The filename of the file to which to write the gcode.
|
||||
*/
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
command_socket = socket;
|
||||
}
|
||||
|
||||
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if (command_socket)
|
||||
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
output_file.open(filename);
|
||||
@@ -114,108 +93,61 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the target to write gcode to: an output stream.
|
||||
*
|
||||
* Used when CuraEngine is NOT used as command line tool.
|
||||
*
|
||||
* \param stream The stream to write gcode to.
|
||||
*/
|
||||
|
||||
void setTargetStream(std::ostream* stream)
|
||||
{
|
||||
gcode.setOutputStream(stream);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the total extruded volume for a specific extruder in mm^3
|
||||
*
|
||||
* Retractions and unretractions don't contribute to this.
|
||||
*
|
||||
* \param extruder_nr The extruder number for which to get the total netto extruded volume
|
||||
* \return total filament printed in mm^3
|
||||
*/
|
||||
double getTotalFilamentUsed(int extruder_nr)
|
||||
|
||||
double getTotalFilamentUsed(int e)
|
||||
{
|
||||
return gcode.getTotalFilamentUsed(extruder_nr);
|
||||
return gcode.getTotalFilamentUsed(e);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the total estimated print time in seconds
|
||||
*
|
||||
* \return total print time in seconds
|
||||
*/
|
||||
double getTotalPrintTime()
|
||||
{
|
||||
return gcode.getTotalPrintTime();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Write all the gcode for the current meshgroup.
|
||||
* This is the primary function of this class.
|
||||
*
|
||||
* \param[in] storage The data storage from which to get the polygons to print and the areas to fill.
|
||||
* \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process.
|
||||
*/
|
||||
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
|
||||
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Set the FffGcodeWriter::fan_speed_layer_time_settings by retrieving all settings from the global/per-meshgroup settings.
|
||||
*/
|
||||
void setConfigFanSpeedLayerTime();
|
||||
|
||||
/*!
|
||||
* Create and set the SliceDataStorage::coasting_config for each extruder.
|
||||
*
|
||||
* \param[out] storage The data storage to which to save the configuration
|
||||
*/
|
||||
|
||||
void setConfigCoasting(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Set the retraction config globally, per extruder and per mesh.
|
||||
*
|
||||
* \param[out] storage The data storage to which to save the configurations
|
||||
*/
|
||||
//Setup the retraction parameters.
|
||||
void setConfigRetraction(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Initialize the GcodePathConfig config parameters which don't change over all layers, for each feature.
|
||||
*
|
||||
* The features are: skirt, support and for each mesh: outer wall, inner walls, skin, infill (and combined infill)
|
||||
*
|
||||
* \param[out] storage The data storage to which to save the configurations
|
||||
* initialize GcodePathConfig config parameters which don't change over all layers
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
*
|
||||
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*/
|
||||
void processStartingCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Move up and over the already printed meshgroups to print the next meshgroup.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Move up and over the just printed model to print the next model.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*/
|
||||
void processNextMeshGroupCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Add raft gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* 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 total_layers The total number of layers.
|
||||
* \param has_raft Whether a raft is used for this print.
|
||||
@@ -223,27 +155,24 @@ private:
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Add the skirt to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Add the skirt to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param extruder_nr The extrudewr train for which to process the skirt
|
||||
*/
|
||||
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Adds the ooze shield to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Adds the ooze shield to the print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Adds the draft protection screen to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Adds the draft protection screen to the print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
@@ -251,18 +180,16 @@ private:
|
||||
|
||||
/*!
|
||||
* Calculate in which order to print the meshes.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param current_extruder The current extruder with which we last printed
|
||||
* \return A vector of mesh indices ordered on print order.
|
||||
*/
|
||||
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
|
||||
* Add a single layer from a single mesh-volume to the GCode in mesh surface mode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -270,10 +197,9 @@ private:
|
||||
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the GCode for mesh surface mode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -281,10 +207,9 @@ private:
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
|
||||
* Add a single layer from a single mesh-volume to the GCode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -292,36 +217,72 @@ private:
|
||||
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
|
||||
*
|
||||
* Add thicker (multiple layers) sparse infill for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle, int extrusionWidth);
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* Add normal sparse infill for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle, int extrusionWidth);
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* Add wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
* \param inside_out Whether to print from inside outward or other way around
|
||||
*/
|
||||
void processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out);
|
||||
|
||||
/*!
|
||||
* Add the inner extra walls of the wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
*/
|
||||
void processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out);
|
||||
|
||||
/*!
|
||||
* Add the infill of the wall reinforcement for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param reinforcement_wall The reinforcement wall for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param wall_reinforcement_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param wall_reinforcement_line_width extrusionWidth
|
||||
*/
|
||||
void processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width);
|
||||
|
||||
/*!
|
||||
* Generate the insets for the walls of a given layer part.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
|
||||
@@ -332,34 +293,34 @@ private:
|
||||
/*!
|
||||
* Add the gcode of the top/bottom skin of the given part.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, int infill_overlap, int infill_angle, int extrusion_width);
|
||||
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width);
|
||||
|
||||
/*!
|
||||
* Add the support to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Add the support to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
|
||||
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, or after.
|
||||
* \param before_rest Whether the function has been called before adding the rest to the gcode, or after.
|
||||
*/
|
||||
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
|
||||
/*!
|
||||
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Add the support lines/walls to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
/*!
|
||||
* Add the support roofs to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* Add the support roofs to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
@@ -370,7 +331,7 @@ private:
|
||||
*
|
||||
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param extruder_nr The extruder to which to switch
|
||||
@@ -379,7 +340,7 @@ private:
|
||||
|
||||
/*!
|
||||
* Add the prime tower gcode for the current layer.
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param prev_extruder The current extruder with which we last printed.
|
||||
|
||||
+139
-37
@@ -2,14 +2,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "slicer/Slicer.h"
|
||||
#include "slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "support.h"
|
||||
#include "slicer/MultiVolumes.h"
|
||||
#include "slicer/LayerPart.h"
|
||||
#include "TextureProcessor.h"
|
||||
#include "multiVolumes.h"
|
||||
#include "layerPart.h"
|
||||
#include "inset.h"
|
||||
#include "skirt.h"
|
||||
#include "skin.h"
|
||||
@@ -17,7 +16,6 @@
|
||||
#include "raft.h"
|
||||
#include "debug.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -25,8 +23,8 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->beginSendSlicedObject();
|
||||
if (commandSocket)
|
||||
commandSocket->beginSendSlicedObject();
|
||||
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
@@ -40,7 +38,7 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
|
||||
|
||||
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
|
||||
|
||||
storage.model_min = meshgroup->min();
|
||||
storage.model_max = meshgroup->max();
|
||||
@@ -67,42 +65,41 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
|
||||
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
std::vector<Slicer*> slicerList;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = *meshgroup->meshes[mesh_idx];
|
||||
Mesh& mesh = meshgroup->meshes[mesh_idx];
|
||||
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
slicerList.push_back(slicer);
|
||||
/*
|
||||
for(SlicerLayer& layer : slicer->layers)
|
||||
{
|
||||
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
|
||||
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
|
||||
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
|
||||
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
|
||||
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
|
||||
}
|
||||
*/
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
|
||||
}
|
||||
|
||||
log("Layer count: %i\n", layer_count);
|
||||
|
||||
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
|
||||
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
|
||||
{
|
||||
storage.meshes.emplace_back(meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
|
||||
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
|
||||
SliceMeshStorage& meshStorage = storage.meshes.back();
|
||||
Mesh& mesh = *storage.meshgroup->meshes[meshIdx];
|
||||
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
|
||||
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
|
||||
delete slicerList[meshIdx];
|
||||
|
||||
@@ -129,16 +126,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
if (commandSocket)
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
}
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,7 +152,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processInsets(storage, layer_number);
|
||||
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
|
||||
}
|
||||
|
||||
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
|
||||
@@ -166,24 +163,21 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
return;
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers);
|
||||
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
if (getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
@@ -198,12 +192,17 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processSkinsAndInfill(storage, layer_number);
|
||||
}
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
|
||||
}
|
||||
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
processWallReinforcement(storage, layer_number);
|
||||
}
|
||||
|
||||
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
}
|
||||
|
||||
@@ -223,6 +222,17 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
|
||||
{
|
||||
processFuzzyWalls(mesh);
|
||||
}
|
||||
else if (mesh.getSettingAsCount("wall_line_count") > 0)
|
||||
{ // only send polygon data
|
||||
for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
|
||||
{
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +250,17 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
|
||||
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
|
||||
inset_count += layer_nr % 2;
|
||||
generateInsets(layer, mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
if (layer->parts[partNr].insets.size() > 0)
|
||||
{
|
||||
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin
|
||||
for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++)
|
||||
sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
@@ -253,6 +273,72 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
|
||||
segment.add(polyline[point_idx-1]);
|
||||
segment.add(polyline[point_idx]);
|
||||
}
|
||||
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FffPolygonGenerator::processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{ // generate infill area
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (mesh.getSettingInMicrons("wall_reinforcement_thickness") == 0.0 && mesh.getSettingAsCount("wall_reinforcement_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int inset_count = mesh.getSettingAsCount("wall_reinforcement_line_count");
|
||||
int wall_line_width = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
if (part.infill_area.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int wall_reinforcement_count = mesh.getSettingAsCount("wall_reinforcement_count");
|
||||
part.reinforcement_walls.reserve(wall_reinforcement_count);
|
||||
|
||||
|
||||
for (unsigned int wall_idx = 0; int(wall_idx) < wall_reinforcement_count; wall_idx++)
|
||||
{
|
||||
part.reinforcement_walls.emplace_back();
|
||||
ReinforcementWall& reinforcement_wall = part.reinforcement_walls.back();
|
||||
|
||||
Polygons outer_wall_reinforcement_edge = part.infill_area[0].offset(-mesh.getSettingInMicrons("wall_reinforcement_thickness"));
|
||||
reinforcement_wall.wall_reinforcement_area = part.infill_area[0].difference(outer_wall_reinforcement_edge);
|
||||
if (mesh.getSettingAsCount("wall_reinforcement_line_count") > 0)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(outer_wall_reinforcement_edge.offset(-wall_line_width/2));
|
||||
}
|
||||
else
|
||||
{
|
||||
part.infill_area[0] = outer_wall_reinforcement_edge.offset(-wall_line_width/2);
|
||||
}
|
||||
|
||||
// generate reinforcement wall extra walls
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
generateWallReinforcementWallExtraWalls(&part, reinforcement_wall, wall_line_width, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
|
||||
{
|
||||
part.infill_area[0] = reinforcement_wall.wall_reinforcement_axtra_walls.back().offset(-wall_line_width/2); // update the infill area to one reinforcement wall insetted (updated each time a reinforcement wall is generated)
|
||||
}
|
||||
if (part.insets.size() > 0)
|
||||
{
|
||||
for(Polygons& polys : reinforcement_wall.wall_reinforcement_axtra_walls)
|
||||
sendPolygons(SupportType, layer_nr, polys, wall_line_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,17 +393,17 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
|
||||
|
||||
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
|
||||
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
|
||||
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int innermost_wall_extrusion_width = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
|
||||
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
|
||||
{
|
||||
int infill_skin_overlap = 0;
|
||||
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
|
||||
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
|
||||
{
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
|
||||
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
|
||||
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
@@ -327,6 +413,20 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool frontend_can_show_polygon_as_filled_polygon = false;
|
||||
if (frontend_can_show_polygon_as_filled_polygon)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
|
||||
for (SkinPart& skin_part : part.skin_parts)
|
||||
{
|
||||
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,6 +506,7 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
Polygons skirt_sent = storage.skirt[0];
|
||||
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
skirt_sent.add(storage.skirt[extruder]);
|
||||
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
|
||||
}
|
||||
|
||||
|
||||
@@ -441,7 +542,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
int r = rand() % (fuzziness * 2) - fuzziness;
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
Point perp_to_p0p1 = crossZ(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
|
||||
result.add(pa);
|
||||
@@ -466,6 +567,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "settings.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -25,14 +24,26 @@ namespace cura
|
||||
*/
|
||||
class FffPolygonGenerator : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
CommandSocket* commandSocket;
|
||||
public:
|
||||
/*!
|
||||
* Basic constructor
|
||||
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
|
||||
*/
|
||||
FffPolygonGenerator(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, commandSocket(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the FffAreaGenerator::commandSocket
|
||||
*/
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
commandSocket = socket;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
|
||||
@@ -44,6 +55,17 @@ public:
|
||||
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Send polygons over the command socket, if there is one.
|
||||
* \param type The type of polygon to send
|
||||
* \param layer_nr The layer number at which the polygons occur
|
||||
* \param polygons The polygons to be sent
|
||||
*/
|
||||
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Slice the \p object and store the outlines in the \p storage.
|
||||
@@ -79,6 +101,15 @@ private:
|
||||
*/
|
||||
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Generate the wall reinforcement extra wall polygons and its infill area which form the reinforcement.
|
||||
*
|
||||
* Also redefines the infill area;
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param layer_nr The layer for which to generate the insets.
|
||||
*/
|
||||
void processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
/*!
|
||||
* Generate the outline of the ooze shield.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
|
||||
+7
-12
@@ -11,7 +11,6 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
|
||||
std::stringstream sstream;
|
||||
if (first_meshgroup)
|
||||
{
|
||||
sstream << getAllLocalSettingsString(); // global settings
|
||||
sstream << " -g";
|
||||
}
|
||||
else
|
||||
@@ -26,7 +25,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
|
||||
}
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = *meshgroup.meshes[mesh_idx];
|
||||
Mesh& mesh = meshgroup.meshes[mesh_idx];
|
||||
sstream << " -e" << mesh.getSettingAsCount("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
|
||||
}
|
||||
sstream << "\n";
|
||||
@@ -67,7 +66,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
|
||||
if (meshgroup->meshes.empty())
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
@@ -79,11 +78,11 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
log("starting Neith Weaver...\n");
|
||||
|
||||
Weaver w(this);
|
||||
w.weave(meshgroup);
|
||||
w.weave(meshgroup, command_socket);
|
||||
|
||||
log("starting Neith Gcode generation...\n");
|
||||
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
|
||||
gcoder.writeGCode();
|
||||
gcoder.writeGCode(command_socket);
|
||||
log("finished Neith Gcode generation...\n");
|
||||
|
||||
} else
|
||||
@@ -94,17 +93,13 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
gcode_writer.setCommandSocket(command_socket);
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
|
||||
gcode_writer.writeGCode(storage, time_keeper);
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
CommandSocket::getInstance()->endSendSlicedObject();
|
||||
}
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
|
||||
+22
-98
@@ -19,9 +19,6 @@ namespace cura {
|
||||
class FffProcessor : public SettingsBase , NoCopy
|
||||
{
|
||||
private:
|
||||
/*!
|
||||
* The FffProcessor used for the (current) slicing (The instance of this singleton)
|
||||
*/
|
||||
static FffProcessor instance;
|
||||
|
||||
FffProcessor()
|
||||
@@ -29,142 +26,69 @@ private:
|
||||
, gcode_writer(this)
|
||||
, first_meshgroup(true)
|
||||
{
|
||||
command_socket = NULL;
|
||||
}
|
||||
public:
|
||||
/*!
|
||||
* Get the instance
|
||||
* \return The instance
|
||||
*/
|
||||
static FffProcessor* getInstance()
|
||||
{
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
* The polygon generator, which slices the models and generates all polygons to be printed and areas to be filled.
|
||||
*/
|
||||
FffPolygonGenerator polygon_generator;
|
||||
|
||||
/*!
|
||||
* The gcode writer, which generates paths in layer plans in a buffer, which converts these paths into gcode commands.
|
||||
*/
|
||||
FffGcodeWriter gcode_writer;
|
||||
|
||||
/*!
|
||||
* Whether the firs meshgroup is being processed.
|
||||
*/
|
||||
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
/*!
|
||||
* A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
|
||||
*
|
||||
* Used in debugging.
|
||||
*/
|
||||
|
||||
std::string profile_string = "";
|
||||
|
||||
/*!
|
||||
* Get all settings for the current meshgroup in the format by which CuraEngine is called via the command line.
|
||||
*
|
||||
* Also includes all global settings if this is the first meshgroup.
|
||||
*
|
||||
* Used in debugging.
|
||||
*
|
||||
* \param meshgroup The meshgroup for which to stringify all settings
|
||||
* \param first_meshgroup Whether this is the first meshgroup and all global settigns should be included as well
|
||||
*/
|
||||
|
||||
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
|
||||
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Get a string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
|
||||
*
|
||||
* \return A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
|
||||
*/
|
||||
std::string getProfileString() { return profile_string; }
|
||||
|
||||
/*!
|
||||
* The stop watch used to time how long the different stages take to compute.
|
||||
*/
|
||||
|
||||
TimeKeeper time_keeper; // TODO: use singleton time keeper
|
||||
|
||||
/*!
|
||||
* Reset the meshgroup number to the first meshgroup to start a new slicing.
|
||||
*/
|
||||
void resetMeshGroupNumber()
|
||||
|
||||
void resetFileNumber()
|
||||
{
|
||||
gcode_writer.resetMeshGroupNumber();
|
||||
gcode_writer.resetFileNumber();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the target to write gcode to: to a file.
|
||||
*
|
||||
* Used when CuraEngine is used as command line tool.
|
||||
*
|
||||
* \param filename The filename of the file to which to write the gcode.
|
||||
*/
|
||||
void setCommandSocket(CommandSocket* socket)
|
||||
{
|
||||
command_socket = socket;
|
||||
gcode_writer.setCommandSocket(socket);
|
||||
polygon_generator.setCommandSocket(socket);
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
return gcode_writer.setTargetFile(filename);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the target to write gcode to: an output stream.
|
||||
*
|
||||
* Used when CuraEngine is NOT used as command line tool.
|
||||
*
|
||||
* \param stream The stream to write gcode to.
|
||||
*/
|
||||
|
||||
void setTargetStream(std::ostream* stream)
|
||||
{
|
||||
return gcode_writer.setTargetStream(stream);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the total extruded volume for a specific extruder in mm^3
|
||||
*
|
||||
* Retractions and unretractions don't contribute to this.
|
||||
*
|
||||
* \param extruder_nr The extruder number for which to get the total netto extruded volume
|
||||
* \return total filament printed in mm^3
|
||||
*/
|
||||
double getTotalFilamentUsed(int extruder_nr)
|
||||
double getTotalFilamentUsed(int e)
|
||||
{
|
||||
return gcode_writer.getTotalFilamentUsed(extruder_nr);
|
||||
return gcode_writer.getTotalFilamentUsed(e);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the total estimated print time in seconds
|
||||
*
|
||||
* \return total print time in seconds
|
||||
*/
|
||||
double getTotalPrintTime()
|
||||
{
|
||||
return gcode_writer.getTotalPrintTime();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add the end gcode and set all temperatures to zero.
|
||||
*/
|
||||
|
||||
void finalize()
|
||||
{
|
||||
gcode_writer.finalize();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Process all files into one meshgroup
|
||||
*
|
||||
* \warning Unused!
|
||||
*/
|
||||
bool processFiles(const std::vector<std::string> &files);
|
||||
|
||||
/*!
|
||||
* Generate gcode for a given \p meshgroup
|
||||
* The primary function of this class.
|
||||
*
|
||||
* \param meshgroup The meshgroup for which to generate gcode
|
||||
* \return Whether this function succeeded
|
||||
*/
|
||||
|
||||
bool processMeshGroup(MeshGroup* meshgroup);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,16 +14,12 @@ void LayerPlanBuffer::flush()
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
while (!buffer.empty())
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
buffer.pop_front();
|
||||
layer_plan.writeGCode(gcode, getSettingBoolean("cool_lift_head"), layer_plan.getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
@@ -222,4 +218,10 @@ void LayerPlanBuffer::insertPreheatCommands()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -17,6 +17,8 @@ namespace cura
|
||||
|
||||
class LayerPlanBuffer : SettingsMessenger
|
||||
{
|
||||
CommandSocket* command_socket;
|
||||
|
||||
GCodeExport& gcode;
|
||||
|
||||
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
|
||||
@@ -27,8 +29,9 @@ class LayerPlanBuffer : SettingsMessenger
|
||||
public:
|
||||
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, CommandSocket* command_socket, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
, command_socket(command_socket)
|
||||
, gcode(gcode)
|
||||
{ }
|
||||
|
||||
@@ -52,10 +55,8 @@ public:
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->sendGCodeLayer();
|
||||
buffer.pop_front();
|
||||
}
|
||||
return buffer.back();
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef MAT_COORD_H
|
||||
#define MAT_COORD_H
|
||||
|
||||
#include "utils/FPoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Coordinates in a specific texture bitmap
|
||||
*/
|
||||
struct MatCoord
|
||||
{
|
||||
FPoint coords;
|
||||
int mat_id; //!< Material id
|
||||
MatCoord() //!< non-initializing constructor
|
||||
{}
|
||||
MatCoord(FPoint coords, int mat_id) //!< constructor
|
||||
: coords(coords)
|
||||
, mat_id(mat_id)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // MAT_COORD_H
|
||||
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef MAT_SEGMENT_H
|
||||
#define MAT_SEGMENT_H
|
||||
|
||||
#include "MatCoord.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Coordinates in a specific texture bitmap
|
||||
*/
|
||||
struct MatSegment
|
||||
{
|
||||
MatCoord start;
|
||||
MatCoord end;
|
||||
MatSegment() //!< non-initializing constructor
|
||||
{}
|
||||
MatSegment(MatCoord start, MatCoord end)
|
||||
: start(start)
|
||||
, end(end)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // MAT_SEGMENT_H
|
||||
@@ -1,33 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include <limits> // numeric limits
|
||||
#include <algorithm> // min max
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Material::setData(unsigned char* data)
|
||||
{
|
||||
this->data = data;
|
||||
}
|
||||
|
||||
void Material::setWidthHeight(int width, int height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
float Material::getColor(float x, float y)
|
||||
{
|
||||
int w_idx = std::max(0, std::min(int (x * width), width - 1));
|
||||
int h_idx = std::max(0, std::min(int (y * height), height - 1));
|
||||
unsigned char r = data[(h_idx * width + w_idx) * 3];
|
||||
return (float) r / std::numeric_limits<unsigned char>::max();
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,30 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef MATERIAL_H
|
||||
#define MATERIAL_H
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
void setData(unsigned char* data);
|
||||
void setWidthHeight(int width, int height);
|
||||
/*!
|
||||
* get some value representing the getColor
|
||||
*
|
||||
* red?
|
||||
*
|
||||
* TODO
|
||||
*
|
||||
* \return a value between zero and one
|
||||
*/
|
||||
float getColor(float x, float y);
|
||||
protected:
|
||||
unsigned char* data; //!< pixel data in rgb-row-first (or bgr-row first ?)
|
||||
int width, height; //!< image dimensions
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // MATERIAL_H
|
||||
@@ -1,42 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "MaterialBase.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
Material* MaterialBase::add(std::string name)
|
||||
{
|
||||
name_to_mat_idx[name] = materials.size();
|
||||
materials.emplace_back();
|
||||
return &materials.back();
|
||||
}
|
||||
|
||||
Material* MaterialBase::getMat(unsigned int id)
|
||||
{
|
||||
if (id < materials.size())
|
||||
{
|
||||
return &materials[id];
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MaterialBase::getMatId(std::string name)
|
||||
{
|
||||
auto it = name_to_mat_idx.find(name);
|
||||
if (it == name_to_mat_idx.end())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef MATERIAL_BASE_H
|
||||
#define MATERIAL_BASE_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class MaterialBase
|
||||
{
|
||||
public:
|
||||
int getMatId(std::string name);
|
||||
Material* add(std::string name);
|
||||
Material* getMat(unsigned int id);
|
||||
protected:
|
||||
std::unordered_map<std::string, int> name_to_mat_idx;
|
||||
std::vector<Material> materials;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // MATERIAL_BASE_H
|
||||
+40
-115
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <algorithm> // min
|
||||
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -14,7 +12,6 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
|
||||
double speed_mod = old_line_width / new_line_width_mm;
|
||||
double extrusion_mod = new_line_width_mm / old_line_width;
|
||||
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
|
||||
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
|
||||
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
|
||||
}
|
||||
|
||||
@@ -54,136 +51,64 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
|
||||
return false;
|
||||
};
|
||||
|
||||
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
int64_t max_line_width = nozzle_size * 3 / 2;
|
||||
|
||||
|
||||
unsigned int idx = path_idx_first_move;
|
||||
if (idx + 3 > paths.size()-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ( paths[idx+0].config != &travelConfig // must be travel
|
||||
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+1].config == &travelConfig // must be extrusion
|
||||
// || paths[idx+2].points.size() > 1 // travel must be direct
|
||||
|| paths[idx+2].config != &travelConfig // must be travel
|
||||
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+3].config == &travelConfig // must be extrusion
|
||||
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
|
||||
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
|
||||
{ // both extrusion moves must be of lines space filling type!
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t line_width = paths[idx+1].config->getLineWidth();
|
||||
if (idx + 3 > paths.size()-1) return false;
|
||||
if (paths[idx+0].config != &travelConfig) return false;
|
||||
if (paths[idx+1].points.size() > 1) return false;
|
||||
if (paths[idx+1].config == &travelConfig) return false;
|
||||
// if (paths[idx+2].points.size() > 1) return false;
|
||||
if (paths[idx+2].config != &travelConfig) return false;
|
||||
if (paths[idx+3].points.size() > 1) return false;
|
||||
if (paths[idx+3].config == &travelConfig) return false;
|
||||
|
||||
Point& a = paths[idx+0].points.back(); // first extruded line from
|
||||
Point& b = paths[idx+1].points.back(); // first extruded line to
|
||||
Point& c = paths[idx+2].points.back(); // second extruded line from
|
||||
Point& d = paths[idx+3].points.back(); // second extruded line to
|
||||
|
||||
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
|
||||
}
|
||||
|
||||
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
use_second_middle_as_first = false;
|
||||
int64_t max_line_width = nozzle_size * 3 / 2;
|
||||
|
||||
Point ab = b - a;
|
||||
Point cd = d - c;
|
||||
|
||||
if (b == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
int64_t ab_size = vSize(ab);
|
||||
int64_t cd_size = vSize(cd);
|
||||
|
||||
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
|
||||
{
|
||||
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
|
||||
}
|
||||
|
||||
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
|
||||
int64_t prod = dot(ab,cd);
|
||||
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
|
||||
{
|
||||
return false; // extrusion moves not in the same or opposite diraction
|
||||
}
|
||||
|
||||
// make lines in the same direction by flipping one
|
||||
if (prod < 0)
|
||||
{
|
||||
ab = ab * -1;
|
||||
}
|
||||
else if (prod == 0)
|
||||
{
|
||||
return false; // lines are orthogonal!
|
||||
}
|
||||
else if (b == d || a == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
int64_t prod = dot(ab,cd);
|
||||
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
|
||||
return false; // extrusion moves not in the same or opposite diraction
|
||||
if (prod < 0) { ab = ab * -1; }
|
||||
|
||||
|
||||
Point infill_vector = (cd + ab) / 2;
|
||||
|
||||
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
|
||||
|
||||
first_middle = (use_second_middle_as_first)?
|
||||
second_middle :
|
||||
(a + b) / 2;
|
||||
second_middle = (c + d) / 2;
|
||||
|
||||
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
|
||||
Point dir_vector_perp = crossZ(second_middle - first_middle);
|
||||
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
|
||||
if (dir_vector_perp_length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (dir_vector_perp_length > 5 * nozzle_size)
|
||||
{
|
||||
return false; // infill lines too far apart
|
||||
}
|
||||
|
||||
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
|
||||
|
||||
// compute the resulting line width
|
||||
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
|
||||
if (resulting_line_width > max_line_width)
|
||||
{
|
||||
return false; // combined lines would be too wide
|
||||
}
|
||||
if (resulting_line_width == 0)
|
||||
{
|
||||
return false; // dot is zero, so lines are in each others extension, not next to eachother
|
||||
}
|
||||
|
||||
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
|
||||
Point ac = c - first_middle;
|
||||
Point infill_vector_perp = turn90CCW(infill_vector);
|
||||
int64_t perp_proj = dot(ac, infill_vector_perp);
|
||||
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
|
||||
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
|
||||
{
|
||||
return false; // lines are too far apart or too close together
|
||||
if (dir_vector_perp_length == 0) return false;
|
||||
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
|
||||
|
||||
|
||||
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
|
||||
if (line_width > max_line_width) return false; // combined lines would be too wide
|
||||
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
|
||||
|
||||
{ // check whether the two lines are adjacent
|
||||
Point ca = first_middle - c;
|
||||
double ca_size = vSizeMM(ca);
|
||||
double cd_size = vSizeMM(cd);
|
||||
double prod = INT2MM(dot(ca, cd));
|
||||
double fraction = prod / ( ca_size * cd_size );
|
||||
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
|
||||
|
||||
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
|
||||
}
|
||||
|
||||
// check whether the two line segments are adjacent.
|
||||
// full infill in a narrow area might result in line segments with arbitrary distance between them
|
||||
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
|
||||
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
|
||||
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,42 +12,24 @@ class MergeInfillLines
|
||||
{
|
||||
// void merge(Point& from, Point& p0, Point& p1);
|
||||
GCodeExport& gcode; //!< Where to write the combined line to
|
||||
int layer_nr; //!< The current layer number
|
||||
std::vector<GCodePath>& paths; //!< The paths currently under consideration
|
||||
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
|
||||
|
||||
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
/*!
|
||||
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
|
||||
*
|
||||
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
|
||||
*
|
||||
* \param a first from
|
||||
* \param b first to
|
||||
* \param c second from
|
||||
* \param d second to
|
||||
* \param line_width The line width of the moves
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
|
||||
|
||||
/*!
|
||||
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
|
||||
*
|
||||
@@ -61,8 +43,8 @@ public:
|
||||
/*!
|
||||
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
|
||||
*/
|
||||
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
|
||||
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
|
||||
|
||||
/*!
|
||||
* Check for lots of small moves and combine them into one large line.
|
||||
@@ -78,21 +60,6 @@ public:
|
||||
*/
|
||||
bool mergeInfillLines(double speed, unsigned int& path_idx);
|
||||
|
||||
/*!
|
||||
* send a polygon through the command socket from the previous point to the given point
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons pathPoly;
|
||||
PolygonRef path = pathPoly.newPoly();
|
||||
path.add(from);
|
||||
path.add(to);
|
||||
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+16
-228
@@ -22,17 +22,13 @@ void* fgets_(char* ptr, size_t len, FILE* f)
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
else if (*ptr =='\0')
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
ptr++;
|
||||
len--;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
char buffer[1024];
|
||||
@@ -65,41 +61,29 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matri
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
|
||||
fseek(f, 0L, SEEK_END);
|
||||
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
|
||||
rewind(f); //Seek back to start.
|
||||
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
|
||||
|
||||
char buffer[80];
|
||||
uint32_t faceCount;
|
||||
//Skip the header
|
||||
if (fread(buffer, 80, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t reported_face_count;
|
||||
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
|
||||
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
|
||||
//Read the face count
|
||||
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (reported_face_count != face_count)
|
||||
{
|
||||
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
|
||||
}
|
||||
|
||||
//For each face read:
|
||||
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
|
||||
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
|
||||
mesh->faces.reserve(face_count);
|
||||
mesh->vertices.reserve(face_count);
|
||||
for (unsigned int i = 0; i < face_count; i++)
|
||||
mesh->faces.reserve(faceCount);
|
||||
mesh->vertices.reserve(faceCount);
|
||||
for(unsigned int i=0;i<faceCount;i++)
|
||||
{
|
||||
if (fread(buffer, 50, 1, f) != 1)
|
||||
{
|
||||
@@ -118,34 +102,13 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matr
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
if (f == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Skip any whitespace at the beginning of the file.
|
||||
unsigned long long num_whitespace = 0; //Number of whitespace characters.
|
||||
unsigned char whitespace;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
while(isspace(whitespace))
|
||||
{
|
||||
num_whitespace++;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
|
||||
|
||||
char buffer[6];
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
|
||||
if (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
@@ -172,194 +135,19 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
return loadMeshSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
|
||||
void readBMP(Material* mat, const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == nullptr)
|
||||
{
|
||||
logError("ERROR: couldn't load image file %s.\n", filename);
|
||||
return;
|
||||
}
|
||||
unsigned char info[54];
|
||||
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
|
||||
|
||||
// extract image height and width from header
|
||||
int width = *(int*)&info[18];
|
||||
int height = *(int*)&info[22];
|
||||
|
||||
int size = 3 * width * height;
|
||||
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
|
||||
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
|
||||
fclose(f);
|
||||
|
||||
|
||||
// for (int i = 0; i < size; i += 3)
|
||||
// {
|
||||
// unsigned char tmp = data[i];
|
||||
// data[i] = data[i+2];
|
||||
// data[i+2] = tmp;
|
||||
// } // BGR ==> RGB
|
||||
mat->setData(data);
|
||||
mat->setWidthHeight(width, height);
|
||||
}
|
||||
void loadMatImage(Material* mat, const char* filename)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".bmp") == 0 || strcmp(ext, ".BMP") == 0))
|
||||
{
|
||||
readBMP(mat, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("ERROR: trying to load unsupported image. File %s has %s extension.\n", filename, ext);
|
||||
}
|
||||
}
|
||||
|
||||
void loadMaterialBase(TexturedMesh* mesh, const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
if (f == nullptr)
|
||||
{
|
||||
logError("ERROR: Couldn't load MTL file %s.\n", filename);
|
||||
return;
|
||||
}
|
||||
char buffer[1024];
|
||||
char mat_name [100];
|
||||
char mat_file [100];
|
||||
char map_type [10];
|
||||
Material* last_mat = nullptr;
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (buffer[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sscanf(buffer, "map_%s %s", map_type, mat_file) == 2 // we don't care what type of map it specifies (currently)
|
||||
|| sscanf(buffer, "bump %s", mat_file) == 1
|
||||
|| sscanf(buffer, "disp %s", mat_file) == 1
|
||||
|| sscanf(buffer, "decal %s", mat_file) == 1
|
||||
|| sscanf(buffer, "refl %s", mat_file) == 1
|
||||
)
|
||||
{
|
||||
std::string parent_dir = std::string(filename).substr(0, std::string(filename).find_last_of("/\\"));
|
||||
std::string mtl_file = parent_dir + "/" + mat_file;
|
||||
if (last_mat)
|
||||
{
|
||||
loadMatImage(last_mat, mtl_file.c_str());
|
||||
}
|
||||
}
|
||||
else if (sscanf(buffer, "newmtl %s", mat_name) == 1)
|
||||
{
|
||||
last_mat = mesh->addMaterial(mat_name);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
bool loadMeshOBJ(TexturedMesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
if (f == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
char buffer[1024];
|
||||
FPoint3 vertex;
|
||||
Point3 vertex_indices;
|
||||
float texture_x;
|
||||
float texture_y;
|
||||
float temp;
|
||||
char face_index_buffer_1 [100];
|
||||
char face_index_buffer_2 [100];
|
||||
char face_index_buffer_3 [100];
|
||||
char str_buffer [100];
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (buffer[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sscanf(buffer, "v %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
{
|
||||
Point3 v = matrix.apply(vertex);
|
||||
mesh->addVertex(v);
|
||||
}
|
||||
else if (sscanf(buffer, "vt %f %f", &texture_x, &texture_y) == 2)
|
||||
{
|
||||
mesh->addTextureCoord(texture_x, texture_y);
|
||||
}
|
||||
else if (sscanf(buffer, "f %s %s %s", face_index_buffer_1, face_index_buffer_2, face_index_buffer_3) == 3)
|
||||
{
|
||||
int normal_vector_index; // unused
|
||||
Point3 texture_indices(0, 0, 0); // becomes -1 if no texture data supplied
|
||||
int n_scanned_1 = sscanf(face_index_buffer_1, "%d/%d/%d", &vertex_indices.x, &texture_indices.x, &normal_vector_index);
|
||||
int n_scanned_2 = sscanf(face_index_buffer_2, "%d/%d/%d", &vertex_indices.y, &texture_indices.y, &normal_vector_index);
|
||||
int n_scanned_3 = sscanf(face_index_buffer_3, "%d/%d/%d", &vertex_indices.z, &texture_indices.z, &normal_vector_index);
|
||||
if (n_scanned_1 > 0 && n_scanned_2 > 0 && n_scanned_3 > 0)
|
||||
{
|
||||
mesh->addFace(vertex_indices.x - 1, vertex_indices.y - 1, vertex_indices.z - 1, texture_indices.x - 1, texture_indices.y - 1, texture_indices.z - 1);
|
||||
// obj files count vertex indices starting from 1!
|
||||
}
|
||||
}
|
||||
else if (sscanf(buffer, "mtllib %s", str_buffer) == 1)
|
||||
{
|
||||
std::string parent_dir = std::string(filename).substr(0, std::string(filename).find_last_of("/\\"));
|
||||
std::string mtl_file = parent_dir + "/" + str_buffer;
|
||||
loadMaterialBase(mesh, mtl_file.c_str());
|
||||
}
|
||||
else if (sscanf(buffer, "usemtl %s", str_buffer) == 1)
|
||||
{
|
||||
mesh->setMaterial(str_buffer);
|
||||
}
|
||||
else if (sscanf(buffer, "vn %f %f %f", &temp, &temp, &temp) == 3)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (buffer[0] == '\0')
|
||||
{
|
||||
// empty line, do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Cannot parse line \"%s\"\n", buffer);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
Mesh* mesh = new Mesh(object_parent_settings ? object_parent_settings : meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if (loadMeshSTL(mesh,filename,transformation)) //Load it! If successful...
|
||||
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
else if (ext && (strcmp(ext, ".obj") == 0 || strcmp(ext, ".OBJ") == 0))
|
||||
{
|
||||
TexturedMesh* mesh = new TexturedMesh(object_parent_settings ? object_parent_settings : meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if (loadMeshOBJ(mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
}//namespace cura
|
||||
+16
-21
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "TexturedMesh.h"
|
||||
#include "ExtruderTrain.h"
|
||||
|
||||
namespace cura
|
||||
@@ -44,10 +43,6 @@ public:
|
||||
delete extruders[extruder];
|
||||
}
|
||||
}
|
||||
for (Mesh* mesh : meshes)
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -68,34 +63,34 @@ public:
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
std::vector<Mesh*> meshes;
|
||||
std::vector<Mesh> meshes;
|
||||
|
||||
Point3 min() const //! minimal corner of bounding box
|
||||
Point3 min() //! minimal corner of bounding box
|
||||
{
|
||||
if (meshes.size() < 1)
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0]->min();
|
||||
Point3 ret = meshes[0].min();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i]->min();
|
||||
Point3 v = meshes[i].min();
|
||||
ret.x = std::min(ret.x, v.x);
|
||||
ret.y = std::min(ret.y, v.y);
|
||||
ret.z = std::min(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max() const //! maximal corner of bounding box
|
||||
Point3 max() //! maximal corner of bounding box
|
||||
{
|
||||
if (meshes.size() < 1)
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0]->max();
|
||||
Point3 ret = meshes[0].max();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i]->max();
|
||||
Point3 v = meshes[i].max();
|
||||
ret.x = std::max(ret.x, v.x);
|
||||
ret.y = std::max(ret.y, v.y);
|
||||
ret.z = std::max(ret.z, v.z);
|
||||
@@ -105,9 +100,9 @@ public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(Mesh* m : meshes)
|
||||
for(Mesh& m : meshes)
|
||||
{
|
||||
m->clear();
|
||||
m.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,17 +117,17 @@ public:
|
||||
}
|
||||
|
||||
// If a mesh position was given, put the mesh at this position in 3D space.
|
||||
for(Mesh* mesh : meshes)
|
||||
for(Mesh& mesh : meshes)
|
||||
{
|
||||
Point3 mesh_offset(mesh->getSettingInMicrons("mesh_position_x"), mesh->getSettingInMicrons("mesh_position_y"), mesh->getSettingInMicrons("mesh_position_z"));
|
||||
if (mesh->getSettingBoolean("center_object"))
|
||||
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
|
||||
if (mesh.getSettingBoolean("center_object"))
|
||||
{
|
||||
Point3 object_min = mesh->min();
|
||||
Point3 object_max = mesh->max();
|
||||
Point3 object_min = mesh.min();
|
||||
Point3 object_max = mesh.max();
|
||||
Point3 object_size = object_max - object_min;
|
||||
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
|
||||
}
|
||||
mesh->offset(mesh_offset + meshgroup_offset);
|
||||
mesh.offset(mesh_offset + meshgroup_offset);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -146,7 +141,7 @@ public:
|
||||
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
|
||||
* \return whether the file could be loaded
|
||||
*/
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
|
||||
|
||||
}//namespace cura
|
||||
#endif//MESH_GROUP_H
|
||||
|
||||
+21
-29
@@ -5,7 +5,6 @@
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "infill.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -23,7 +22,7 @@ void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "SUPPORT");// so that visualization in the old Cura still works (TODO)
|
||||
}
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
@@ -56,9 +55,8 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
{ // compute max_object_height_per_extruder
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
max_object_height_per_extruder[extr_nr] =
|
||||
std::max( max_object_height_per_extruder[extr_nr]
|
||||
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
|
||||
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
@@ -104,7 +102,7 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
|
||||
{
|
||||
PolygonRef p = storage.primeTower.ground_poly.newPoly();
|
||||
int tower_size = storage.getSettingInMicrons("prime_tower_size");
|
||||
int tower_distance = 0;
|
||||
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
|
||||
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
|
||||
p.add(Point(x + tower_distance, y + tower_distance));
|
||||
@@ -117,7 +115,9 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
|
||||
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
{
|
||||
generatePaths3(storage);
|
||||
}
|
||||
@@ -125,11 +125,11 @@ void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_lay
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
{
|
||||
PolygonRef p = storage.primeTower.ground_poly.newPoly();
|
||||
int tower_size = storage.getSettingInMicrons("prime_tower_size");
|
||||
int tower_distance = 0;
|
||||
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
|
||||
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
|
||||
p.add(Point(x + tower_distance, y + tower_distance));
|
||||
@@ -168,7 +168,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
{
|
||||
|
||||
int n_patterns = 2; // alternating patterns between layers
|
||||
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
|
||||
double infill_overlap = 15; // so that it can't be zero
|
||||
|
||||
generateGroundpoly(storage);
|
||||
|
||||
@@ -179,24 +179,18 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
std::vector<Polygons>& patterns = patterns_per_extruder.back();
|
||||
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
|
||||
{
|
||||
Polygons result_polygons; // should remain empty, since we generate lines pattern!
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int outline_offset = -line_width/2;
|
||||
int line_distance = line_width;
|
||||
double fill_angle = 45 + pattern_idx * 90;
|
||||
Polygons& result_lines = patterns[pattern_idx];
|
||||
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
|
||||
infill_comp.generate(result_polygons, result_lines, in_between);
|
||||
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -214,10 +208,10 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
|
||||
{
|
||||
wipe = false;
|
||||
}
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
@@ -233,14 +227,12 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
|
||||
GCodePathConfig& config = config_per_extruder[new_extruder];
|
||||
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
|
||||
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config);
|
||||
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
|
||||
}
|
||||
if (command_socket)
|
||||
command_socket->sendPolygons(SupportType, layer_nr, pattern, config.getLineWidth());
|
||||
|
||||
if (wipe)
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
@@ -248,7 +240,7 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
|
||||
+3
-3
@@ -54,9 +54,9 @@ public:
|
||||
|
||||
PrimeTower();
|
||||
|
||||
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
|
||||
|
||||
};
|
||||
|
||||
|
||||
+13
-12
@@ -4,23 +4,24 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class PrintFeatureType
|
||||
{
|
||||
NoneType, // unused, but libArcus depends on it
|
||||
OuterWall,
|
||||
InnerWall,
|
||||
Skin,
|
||||
Support,
|
||||
Skirt,
|
||||
Infill,
|
||||
SupportInfill,
|
||||
MoveCombing,
|
||||
MoveRetraction
|
||||
enum class EPrintFeature : unsigned int // unused!!
|
||||
{ // TODO: use in gcodePathConfigs ?
|
||||
OUTER_WALL,
|
||||
INNER_WALLS,
|
||||
INFILL,
|
||||
SKIN,
|
||||
HELPERS,
|
||||
UNCLASSIFIED,
|
||||
ENUM_COUNT
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PRINT_FEATURE
|
||||
+6
-6
@@ -64,22 +64,22 @@ void Progress::init()
|
||||
total_timing = accumulated_time;
|
||||
}
|
||||
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
|
||||
{
|
||||
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
|
||||
if (CommandSocket::getInstance())
|
||||
if (command_socket)
|
||||
{
|
||||
CommandSocket::getInstance()->sendProgress(percentage);
|
||||
command_socket->sendProgress(percentage);
|
||||
}
|
||||
|
||||
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
|
||||
}
|
||||
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
|
||||
{
|
||||
if (CommandSocket::getInstance())
|
||||
if (command_socket)
|
||||
{
|
||||
CommandSocket::getInstance()->sendProgressStage(stage);
|
||||
command_socket->sendProgressStage(stage);
|
||||
}
|
||||
|
||||
if (time_keeper)
|
||||
|
||||
+5
-3
@@ -52,20 +52,22 @@ private:
|
||||
public:
|
||||
static void init(); //!< Initialize some values needed in a fast computation of the progress
|
||||
/*!
|
||||
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
*
|
||||
* \param stage The current stage of processing
|
||||
* \param progress_in_stage Any number giving the progress within the stage
|
||||
* \param progress_in_stage_max The maximal value of \p progress_in_stage
|
||||
* \param commandSocket The command socket over which to communicate the progress.
|
||||
*/
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
|
||||
/*!
|
||||
* Message the progress stage over the command socket.
|
||||
*
|
||||
* \param stage The current stage
|
||||
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
|
||||
* \param commandSocket The command socket over which to communicate (optional)
|
||||
*/
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef SPACE_FILL_TYPE
|
||||
#define SPACE_FILL_TYPE
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Enum class enumerating the strategies with which an area can be occupied with filament
|
||||
*
|
||||
* The walls/perimeters are Polygons
|
||||
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
|
||||
* Grid, Triangles and lines infill is Lines
|
||||
*/
|
||||
enum class SpaceFillType
|
||||
{
|
||||
None,
|
||||
Polygons,
|
||||
PolyLines,
|
||||
Lines
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SPACE_FILL_TYPE
|
||||
@@ -1,130 +0,0 @@
|
||||
#include "TextureProcessor.h"
|
||||
|
||||
#include <algorithm> // swap
|
||||
|
||||
#include "slicer/SlicerSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
#define POINT_DIST 400
|
||||
#define AMPLITUDE 500
|
||||
#define EXTRA_OFFSET 500
|
||||
|
||||
/*
|
||||
void TextureProcessor::process(std::vector< Slicer* >& slicer_list)
|
||||
{
|
||||
for (Slicer* slicer : slicer_list)
|
||||
{
|
||||
for (SlicerLayer& layer : slicer->layers)
|
||||
{
|
||||
process(slicer->mesh, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void TextureProcessor::processBumpMap(Mesh* mesh, SlicerLayer& layer)
|
||||
{
|
||||
process(mesh, layer, false);
|
||||
}
|
||||
|
||||
void TextureProcessor::processDualColorTexture(Mesh* mesh, SlicerLayer& layer)
|
||||
{
|
||||
process(mesh, layer, true);
|
||||
}
|
||||
|
||||
void TextureProcessor::process(Mesh* mesh, SlicerLayer& layer, bool dual_color_texture)
|
||||
{
|
||||
bool flipped = false;
|
||||
if (dual_color_texture)
|
||||
{
|
||||
flipped = layer.layer_nr % 2 == 0;
|
||||
}
|
||||
Polygons results;
|
||||
for (PolygonRef poly : layer.polygonList)
|
||||
{
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
int64_t dist_left_over = (POINT_DIST / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
bool even = false;
|
||||
for (Point& p1 : poly)
|
||||
{
|
||||
SlicerSegment segment(*p0, p1);
|
||||
auto it = layer.segment_to_material_segment.find(segment);
|
||||
if (it != layer.segment_to_material_segment.end())
|
||||
{
|
||||
MatSegment& mat = it->second;
|
||||
MatCoord mat_start = mat.start;
|
||||
MatCoord mat_end = mat.end;
|
||||
if (it->first.start != *p0)
|
||||
{
|
||||
std::swap(mat_start, mat_end);
|
||||
}
|
||||
Point p0p1 = p1 - *p0;
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
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
|
||||
// TODO: move start point (which was already moved last iteration
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += POINT_DIST)
|
||||
{
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist);
|
||||
if (even ^ flipped)
|
||||
{
|
||||
MatCoord mat_coord_now = mat_start;
|
||||
mat_coord_now.coords = mat_start.coords + (mat_end.coords - mat_start.coords) * p0pa_dist / p0p1_size;
|
||||
float val = mesh->getColor(mat_coord_now); // between 0 and 1
|
||||
if (flipped)
|
||||
{
|
||||
val = 1.0f - val;
|
||||
}
|
||||
assert(val > -0.001 && val < 1.001);
|
||||
int r = val * (AMPLITUDE * 2) - AMPLITUDE + EXTRA_OFFSET;
|
||||
Point displacement = normal(perp_to_p0p1, r);
|
||||
pa -= displacement;
|
||||
}
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
even = !even;
|
||||
}
|
||||
// TODO: move end point as well
|
||||
float val = mesh->getColor(mat_end);
|
||||
int r = val * (AMPLITUDE * 2) - AMPLITUDE + EXTRA_OFFSET;
|
||||
Point displacement = normal(perp_to_p0p1, r);
|
||||
if (dual_color_texture)
|
||||
{
|
||||
result.emplace_back(p1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(p1 - displacement);
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(p1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
layer.polygonList = results;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -1,32 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSOR_H
|
||||
#define TEXTURE_PROCESSOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "slicer/Slicer.h"
|
||||
#include "mesh.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class TextureProcessor
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Apply offsets in the xy plane corresponding to pixel intensities
|
||||
*/
|
||||
static void processBumpMap(Mesh* mesh, SlicerLayer& layer);
|
||||
|
||||
/*!
|
||||
* Apply a zigzag pattern with offsets corresponding to pixel intensities
|
||||
*/
|
||||
static void processDualColorTexture(Mesh* mesh, SlicerLayer& layer);
|
||||
protected:
|
||||
static void process(Mesh* mesh, SlicerLayer& layer, bool dual_color_texture);
|
||||
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSOR_H
|
||||
@@ -1,137 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "TexturedMesh.h"
|
||||
|
||||
#include <cassert>
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
TexturedMesh::TexturedMesh(SettingsBaseVirtual* sb)
|
||||
: Mesh(sb)
|
||||
, current_mat(-1) // not set yet
|
||||
{
|
||||
}
|
||||
|
||||
void TexturedMesh::addTextureCoord(float x, float y)
|
||||
{
|
||||
texture_coords.emplace_back(x, y);
|
||||
}
|
||||
|
||||
void TexturedMesh::addFace(int vi0, int vi1, int vi2, int ti0, int ti1, int ti2)
|
||||
{
|
||||
if (vi0 < -1)
|
||||
{
|
||||
vi0 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (vi1 < -1)
|
||||
{
|
||||
vi1 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (vi2 < -1)
|
||||
{
|
||||
vi2 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti0 < -1)
|
||||
{
|
||||
ti0 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti1 < -1)
|
||||
{
|
||||
ti1 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti2 < -1)
|
||||
{
|
||||
ti2 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
bool made_new_face = Mesh::addFace(vi0, vi1, vi2);
|
||||
if (made_new_face)
|
||||
{
|
||||
face_texture_indices.emplace_back(ti0, ti1, ti2, current_mat);
|
||||
assert(Mesh::faces.size() == face_texture_indices.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool TexturedMesh::setMaterial(std::string name)
|
||||
{
|
||||
current_mat = material_base.getMatId(name);
|
||||
return current_mat >= 0;
|
||||
}
|
||||
|
||||
Material* TexturedMesh::addMaterial(std::__cxx11::string name)
|
||||
{
|
||||
return material_base.add(name);
|
||||
}
|
||||
|
||||
|
||||
bool TexturedMesh::getFaceEdgeMatCoord(unsigned int face_idx, int64_t z, unsigned int p0_idx, unsigned int p1_idx, MatCoord& result)
|
||||
{
|
||||
if (face_idx >= face_texture_indices.size() || face_idx >= faces.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FaceTextureCoordIndices texture_idxs = face_texture_indices[face_idx];
|
||||
if (texture_idxs.index[0] < 0 || texture_idxs.index[1] < 0 || texture_idxs.index[2] < 0 || texture_idxs.mat_id < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
MeshFace& face = faces[face_idx];
|
||||
Point3 p0(vertices[face.vertex_index[p0_idx]].p);
|
||||
Point3 p1(vertices[face.vertex_index[p1_idx]].p);
|
||||
|
||||
float dzp0 = z - p0.z;
|
||||
float dp0p1 = p1.z - p0.z;
|
||||
|
||||
if (dzp0 * dp0p1 < 0)
|
||||
{ // z doesn't lie between p0 and p1
|
||||
return false;
|
||||
}
|
||||
if (dzp0 == 0)
|
||||
{ // edge is not cut by horizontal plane!
|
||||
return false;
|
||||
}
|
||||
float ratio = INT2MM(dzp0) / INT2MM(dp0p1);
|
||||
|
||||
FPoint t0 = texture_coords[texture_idxs.index[p0_idx]];
|
||||
FPoint t1 = texture_coords[texture_idxs.index[p1_idx]];
|
||||
|
||||
result.mat_id = texture_idxs.mat_id;
|
||||
result.coords.x = t0.x + (t1.x - t0.x) * ratio;
|
||||
result.coords.y = t0.y + (t1.y - t0.y) * ratio;
|
||||
|
||||
if (result.coords.x > 1.001 || result.coords.x < -0.001 || result.coords.y > 1.001 || result.coords.y < -0.001)
|
||||
{
|
||||
logError("WARNING: wapping material to outside image!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TexturedMesh::registerFaceSlice(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result)
|
||||
{
|
||||
if (!getFaceEdgeMatCoord(face_idx, z, idx_shared, idx_first, result.start))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!getFaceEdgeMatCoord(face_idx, z, idx_shared, idx_second, result.end))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float TexturedMesh::getColor(MatCoord bitmap_coord)
|
||||
{
|
||||
Material* mat = material_base.getMat(bitmap_coord.mat_id);
|
||||
if (mat)
|
||||
{
|
||||
return mat->getColor(bitmap_coord.coords.x, bitmap_coord.coords.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,78 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURED_MESH_H
|
||||
#define TEXTURED_MESH_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "MaterialBase.h"
|
||||
|
||||
#include "mesh.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* A mesh with bitmap textures to it.
|
||||
*
|
||||
* material coordinates are defined separately, and can be reused for different bitmap textures
|
||||
*/
|
||||
class TexturedMesh : public Mesh
|
||||
{
|
||||
public:
|
||||
TexturedMesh(SettingsBaseVirtual* sb);
|
||||
|
||||
|
||||
/*!
|
||||
*
|
||||
*/
|
||||
struct FaceTextureCoordIndices
|
||||
{
|
||||
int index[3]; //!< indices into texture_coords or -1 if no texture data available
|
||||
int mat_id; //!< Material id
|
||||
FaceTextureCoordIndices(int i1, int i2, int i3, int mat_id)
|
||||
: mat_id(mat_id)
|
||||
{
|
||||
index[0] = i1;
|
||||
index[1] = i2;
|
||||
index[2] = i3;
|
||||
}
|
||||
};
|
||||
void addTextureCoord(float x, float y);
|
||||
void addFace(int vi0, int vi1, int vi2, int ti0, int ti1, int ti2);
|
||||
using Mesh::addFace; // otherwise above addFace would shadow the parent addFace
|
||||
|
||||
bool setMaterial(std::string name); //!< set the material to be used in the comming data to be loaded
|
||||
Material* addMaterial(std::string name);
|
||||
|
||||
|
||||
|
||||
virtual bool registerFaceSlice(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result);
|
||||
|
||||
protected:
|
||||
std::vector<FPoint> texture_coords; //!< all texture coordinates by all faces
|
||||
std::vector<FaceTextureCoordIndices> face_texture_indices; //!< for each face the corresponding texture coordinates in TexturedMesh::texture_coords
|
||||
// TODO clean up above lists when super class clear() is called
|
||||
// TODO when to clean up below material base?
|
||||
MaterialBase material_base;
|
||||
/*!
|
||||
* Get the material coordinate corresponding to the point on a plane cutting a given edge of the face.
|
||||
* \param face_idx The face for which to get the material coord
|
||||
* \param z The z of the horizontal plane cutting the face
|
||||
* \param p0_idx The index into the first vert of the edge
|
||||
* \param p1_idx The index into the second vert of the edge
|
||||
* \param result The resulting material Coordinates
|
||||
* \return Whether a Material coordinate is defined at the given location
|
||||
*/
|
||||
bool getFaceEdgeMatCoord(unsigned int face_idx, int64_t z, unsigned int p0_idx, unsigned int p1_idx, MatCoord& result);
|
||||
|
||||
virtual float getColor(MatCoord bitmap_coord);
|
||||
private:
|
||||
int current_mat; //!< material currently used in loading the face material info
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURED_MESH_H
|
||||
+12
-14
@@ -6,12 +6,11 @@
|
||||
|
||||
#include "Progress.h"
|
||||
#include "weaveDataStorage.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Weaver::weave(MeshGroup* meshgroup)
|
||||
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
|
||||
{
|
||||
wireFrame.meshgroup = meshgroup;
|
||||
|
||||
@@ -23,9 +22,9 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
|
||||
std::vector<cura::Slicer*> slicerList;
|
||||
|
||||
for (Mesh* mesh : meshgroup->meshes)
|
||||
for(Mesh& mesh : meshgroup->meshes)
|
||||
{
|
||||
cura::Slicer* slicer = new cura::Slicer(mesh, initial_layer_thickness, connectionHeight, layer_count, mesh->getSettingBoolean("meshfix_keep_open_polygons"), mesh->getSettingBoolean("meshfix_extensive_stitching"));
|
||||
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
slicerList.push_back(slicer);
|
||||
}
|
||||
|
||||
@@ -53,8 +52,8 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
|
||||
|
||||
if (slicerList.empty()) //Wait, there is nothing to slice.
|
||||
{
|
||||
@@ -71,10 +70,10 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
else
|
||||
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
|
||||
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
Polygons parts1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
@@ -85,10 +84,9 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
|
||||
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
|
||||
}
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
|
||||
|
||||
if (chainified.size() > 0)
|
||||
{
|
||||
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
|
||||
@@ -109,10 +107,10 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
|
||||
+3
-2
@@ -6,7 +6,7 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer/Slicer.h"
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygon.h"
|
||||
@@ -61,8 +61,9 @@ public:
|
||||
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
|
||||
*
|
||||
* \param objects The objects for which to create a wireframe print
|
||||
* \param commandSocket the commandSocket
|
||||
*/
|
||||
void weave(MeshGroup* objects);
|
||||
void weave(MeshGroup* objects, CommandSocket* commandSocket);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
+21
-15
@@ -12,15 +12,15 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void Wireframe2gcode::writeGCode()
|
||||
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
|
||||
{
|
||||
|
||||
gcode.preSetup(wireFrame.meshgroup);
|
||||
|
||||
if (CommandSocket::getInstance())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
if (commandSocket)
|
||||
commandSocket->beginGCode();
|
||||
|
||||
processStartingCode();
|
||||
processStartingCode(commandSocket);
|
||||
|
||||
int maxObjectHeight;
|
||||
if (wireFrame.layers.empty())
|
||||
@@ -32,7 +32,7 @@ void Wireframe2gcode::writeGCode()
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
}
|
||||
|
||||
processSkirt();
|
||||
processSkirt(commandSocket);
|
||||
|
||||
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
@@ -76,10 +76,10 @@ void Wireframe2gcode::writeGCode()
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
@@ -166,6 +166,12 @@ void Wireframe2gcode::writeGCode()
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
finalize();
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
commandSocket->sendGCodeLayer();
|
||||
commandSocket->endSendSlicedObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -544,11 +550,11 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
|
||||
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
}
|
||||
|
||||
void Wireframe2gcode::processStartingCode()
|
||||
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
if (!command_socket)
|
||||
{
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
}
|
||||
@@ -589,7 +595,7 @@ void Wireframe2gcode::processStartingCode()
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::processSkirt()
|
||||
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
|
||||
{
|
||||
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
|
||||
{
|
||||
@@ -600,14 +606,14 @@ void Wireframe2gcode::processSkirt()
|
||||
order.addPolygons(skirt);
|
||||
order.optimize();
|
||||
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
|
||||
{
|
||||
unsigned int poly_idx = order.polyOrder[poly_order_idx];
|
||||
PolygonRef poly = skirt[poly_idx];
|
||||
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
|
||||
PolygonRef poly = skirt[actual_poly_idx];
|
||||
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
|
||||
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
|
||||
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer/Slicer.h"
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "Weaver.h"
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
|
||||
|
||||
void writeGCode();
|
||||
void writeGCode(CommandSocket* commandSocket);
|
||||
|
||||
|
||||
private:
|
||||
@@ -80,12 +80,12 @@ private:
|
||||
/*!
|
||||
* Startup gcode: nozzle temp up, retraction settings, bed temp
|
||||
*/
|
||||
void processStartingCode();
|
||||
void processStartingCode(CommandSocket* command_socket);
|
||||
|
||||
/*!
|
||||
* Lay down a skirt
|
||||
*/
|
||||
void processSkirt();
|
||||
void processSkirt(CommandSocket* commandSocket);
|
||||
|
||||
/*!
|
||||
* End gcode: nozzle temp down
|
||||
|
||||
@@ -385,7 +385,6 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
|
||||
}
|
||||
}
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+93
-167
@@ -8,8 +8,6 @@
|
||||
|
||||
#ifdef ARCUS
|
||||
#include <Arcus/Socket.h>
|
||||
#include <Arcus/SocketListener.h>
|
||||
#include <Arcus/Error.h>
|
||||
#endif
|
||||
|
||||
#include <string> // stoi
|
||||
@@ -27,33 +25,7 @@ namespace cura {
|
||||
#define FLOATS_PER_VECTOR 3
|
||||
#define VECTORS_PER_FACE 3
|
||||
|
||||
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
|
||||
|
||||
#ifdef ARCUS
|
||||
class Listener : public Arcus::SocketListener
|
||||
{
|
||||
public:
|
||||
void stateChanged(Arcus::SocketState::SocketState newState) override
|
||||
{
|
||||
}
|
||||
|
||||
void messageReceived() override
|
||||
{
|
||||
}
|
||||
|
||||
void error(const Arcus::Error & error) override
|
||||
{
|
||||
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
|
||||
{
|
||||
log("%s\n", error.toString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("%s\n", error.toString().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CommandSocket::Private
|
||||
{
|
||||
public:
|
||||
@@ -95,70 +67,65 @@ public:
|
||||
|
||||
// Print object that olds one or more meshes that need to be sliced.
|
||||
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
|
||||
|
||||
std::unordered_map<int, cura::proto::Layer*> sliced_layers;
|
||||
};
|
||||
#endif
|
||||
|
||||
CommandSocket::CommandSocket()
|
||||
#ifdef ARCUS
|
||||
: private_data(new Private)
|
||||
: d(new Private)
|
||||
#endif
|
||||
{
|
||||
#ifdef ARCUS
|
||||
FffProcessor::getInstance()->setCommandSocket(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandSocket* CommandSocket::getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void CommandSocket::instantiate()
|
||||
{
|
||||
instance = new CommandSocket();
|
||||
}
|
||||
|
||||
bool CommandSocket::isInstantiated()
|
||||
{
|
||||
return instance != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
private_data->socket = new Arcus::Socket();
|
||||
private_data->socket->addListener(new Listener());
|
||||
d->socket = new Arcus::Socket();
|
||||
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
|
||||
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
|
||||
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
|
||||
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
|
||||
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
|
||||
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
|
||||
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
|
||||
|
||||
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SlicedObjectList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
|
||||
|
||||
private_data->socket->connect(ip, port);
|
||||
|
||||
log("Connecting to %s:%i\n", ip.c_str(), port);
|
||||
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
log("Connected to %s:%i\n", ip.c_str(), port);
|
||||
d->socket->connect(ip, port);
|
||||
|
||||
bool slice_another_time = true;
|
||||
|
||||
// Start & continue listening as long as socket is not closed and there is no error.
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
|
||||
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
|
||||
{
|
||||
//If there is an object to slice, do so.
|
||||
if(d->objects_to_slice.size())
|
||||
{
|
||||
FffProcessor::getInstance()->resetFileNumber();
|
||||
for(auto object : d->objects_to_slice)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
d->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
sendGCodeLayer();
|
||||
sendPrintTime();
|
||||
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//d->processor->processModel(d->object_to_slice.get());
|
||||
//d->object_to_slice.reset();
|
||||
//d->processor->resetFileNumber();
|
||||
|
||||
//sendPrintTime();
|
||||
}
|
||||
|
||||
// Actually start handling messages.
|
||||
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
|
||||
Arcus::MessagePtr message = d->socket->takeNextMessage();
|
||||
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
|
||||
if(setting_list)
|
||||
{
|
||||
@@ -175,59 +142,33 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
if(slice)
|
||||
{
|
||||
// Reset object counts
|
||||
private_data->object_count = 0;
|
||||
private_data->object_ids.clear();
|
||||
d->object_count = 0;
|
||||
d->object_ids.clear();
|
||||
for(auto object : slice->object_lists())
|
||||
{
|
||||
handleObjectList(&object);
|
||||
}
|
||||
}
|
||||
|
||||
//If there is an object to slice, do so.
|
||||
if(private_data->objects_to_slice.size())
|
||||
{
|
||||
FffProcessor::getInstance()->resetMeshGroupNumber();
|
||||
for(auto object : private_data->objects_to_slice)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
private_data->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
flushGcode();
|
||||
sendPrintTime();
|
||||
sendFinishedSlicing();
|
||||
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//private_data->processor->processModel(private_data->object_to_slice.get());
|
||||
//private_data->object_to_slice.reset();
|
||||
//private_data->processor->resetFileNumber();
|
||||
|
||||
//sendPrintTime();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
if(!d->socket->errorString().empty())
|
||||
{
|
||||
logError("%s\n", d->socket->errorString().data());
|
||||
d->socket->clearError();
|
||||
}
|
||||
}
|
||||
log("Closing connection\n");
|
||||
private_data->socket->close();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
if(list->objects_size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
//private_data->object_count = 0;
|
||||
//private_data->object_ids.clear();
|
||||
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
|
||||
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
|
||||
//d->object_count = 0;
|
||||
//d->object_ids.clear();
|
||||
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
|
||||
MeshGroup* meshgroup = d->objects_to_slice.back().get();
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
@@ -241,14 +182,6 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
|
||||
for(auto object : list->objects())
|
||||
{
|
||||
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int face_count = object.vertices().size() / bytes_per_face;
|
||||
|
||||
if(face_count <= 0)
|
||||
{
|
||||
logWarning("Got an empty mesh, ignoring it!");
|
||||
continue;
|
||||
}
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
|
||||
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
|
||||
for(auto setting : object.settings())
|
||||
@@ -261,9 +194,11 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
}
|
||||
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
|
||||
|
||||
meshgroup->meshes.push_back(new Mesh(extruder_train)); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = *meshgroup->meshes.back();
|
||||
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = meshgroup->meshes.back();
|
||||
|
||||
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int face_count = object.vertices().size() / bytes_per_face;
|
||||
for(int i = 0; i < face_count; ++i)
|
||||
{
|
||||
//TODO: Apply matrix
|
||||
@@ -290,11 +225,11 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
private_data->object_ids.push_back(object.id());
|
||||
d->object_ids.push_back(object.id());
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
private_data->object_count++;
|
||||
d->object_count++;
|
||||
meshgroup->finalize();
|
||||
}
|
||||
|
||||
@@ -310,31 +245,31 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!private_data->current_sliced_object)
|
||||
if(!d->current_sliced_object)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cura::proto::Layer* layer = private_data->getLayerById(layer_nr);
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!private_data->current_sliced_object)
|
||||
if(!d->current_sliced_object)
|
||||
return;
|
||||
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
cura::proto::Layer* proto_layer = private_data->getLayerById(layer_nr);
|
||||
cura::proto::Layer* layer = d->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
cura::proto::Polygon* p = proto_layer->add_polygons();
|
||||
cura::proto::Polygon* p = layer->add_polygons();
|
||||
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
|
||||
std::string polydata;
|
||||
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
|
||||
@@ -348,10 +283,10 @@ void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::Progress>();
|
||||
amount /= private_data->object_count;
|
||||
amount += private_data->sliced_objects * (1. / private_data->object_count);
|
||||
amount /= d->object_count;
|
||||
amount += d->sliced_objects * (1. / d->object_count);
|
||||
message->set_amount(amount);
|
||||
private_data->socket->sendMessage(message);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -366,7 +301,7 @@ void CommandSocket::sendPrintTime()
|
||||
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
|
||||
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
|
||||
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
|
||||
private_data->socket->sendMessage(message);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -382,62 +317,54 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!private_data->sliced_object_list)
|
||||
if(!d->sliced_object_list)
|
||||
{
|
||||
private_data->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
|
||||
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
|
||||
}
|
||||
|
||||
private_data->current_sliced_object = private_data->sliced_object_list->add_objects();
|
||||
private_data->current_sliced_object->set_id(private_data->object_ids[private_data->sliced_objects]);
|
||||
d->current_sliced_object = d->sliced_object_list->add_objects();
|
||||
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
private_data->sliced_objects++;
|
||||
private_data->current_layer_offset = private_data->current_layer_count;
|
||||
std::cout << "End sliced object called. Sliced objects " << private_data->sliced_objects << " object count: " << private_data->object_count << std::endl;
|
||||
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;
|
||||
|
||||
if(private_data->sliced_objects >= private_data->object_count)
|
||||
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)
|
||||
{
|
||||
private_data->socket->sendMessage(private_data->sliced_object_list);
|
||||
private_data->sliced_objects = 0;
|
||||
private_data->current_layer_count = 0;
|
||||
private_data->current_layer_offset = 0;
|
||||
private_data->sliced_object_list.reset();
|
||||
private_data->current_sliced_object = nullptr;
|
||||
private_data->sliced_layers.clear();
|
||||
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
d->socket->sendMessage(d->sliced_object_list);
|
||||
d->sliced_objects = 0;
|
||||
d->current_layer_count = 0;
|
||||
d->current_layer_offset = 0;
|
||||
d->sliced_object_list.reset();
|
||||
d->current_sliced_object = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendFinishedSlicing()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
|
||||
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::flushGcode()
|
||||
void CommandSocket::sendGCodeLayer()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodeLayer>();
|
||||
message->set_id(private_data->object_ids[0]);
|
||||
message->set_data(private_data->gcode_output_stream.str());
|
||||
private_data->socket->sendMessage(message);
|
||||
message->set_id(d->object_ids[0]);
|
||||
message->set_data(d->gcode_output_stream.str());
|
||||
d->socket->sendMessage(message);
|
||||
|
||||
private_data->gcode_output_stream.str("");
|
||||
d->gcode_output_stream.str("");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -446,7 +373,7 @@ void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
private_data->socket->sendMessage(message);
|
||||
d->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -455,19 +382,18 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
id += current_layer_offset;
|
||||
|
||||
auto itr = sliced_layers.find(id);
|
||||
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
|
||||
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
if(itr != sliced_layers.end())
|
||||
if(itr != current_sliced_object->mutable_layers()->end())
|
||||
{
|
||||
layer = itr->second;
|
||||
layer = &(*itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
sliced_layers[id] = layer;
|
||||
}
|
||||
|
||||
return layer;
|
||||
|
||||
+6
-41
@@ -5,7 +5,6 @@
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -18,18 +17,8 @@ namespace cura
|
||||
|
||||
class CommandSocket
|
||||
{
|
||||
private:
|
||||
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
|
||||
|
||||
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
|
||||
|
||||
public:
|
||||
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
|
||||
|
||||
static void instantiate(); //!< Instantiate the CommandSocket.
|
||||
|
||||
static bool isInstantiated(); //!< Check whether the singleton is instantiated
|
||||
|
||||
CommandSocket();
|
||||
/*!
|
||||
* Connect with the GUI
|
||||
* This creates and initialises the arcus socket and then continues listening for messages.
|
||||
@@ -53,20 +42,15 @@ public:
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
|
||||
* Does nothing at the moment
|
||||
*/
|
||||
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine. This is used for the layerview in the GUI
|
||||
*/
|
||||
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
|
||||
*/
|
||||
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send progress to GUI
|
||||
*/
|
||||
@@ -87,36 +71,17 @@ public:
|
||||
*/
|
||||
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
|
||||
|
||||
/*!
|
||||
* Start the slicing of a new meshgroup
|
||||
*/
|
||||
void beginSendSlicedObject();
|
||||
|
||||
/*!
|
||||
* Conclude the slicing of the current meshgroup, so that we can start the next
|
||||
*/
|
||||
void endSendSlicedObject();
|
||||
|
||||
/*!
|
||||
* \brief Sends a message to indicate that all the slicing is done.
|
||||
*
|
||||
* This should indicate that no more data (g-code, prefix/postfix, metadata
|
||||
* or otherwise) should be sent any more regarding the latest slice job.
|
||||
*/
|
||||
void sendFinishedSlicing();
|
||||
|
||||
void beginGCode();
|
||||
|
||||
/*!
|
||||
* Flush the gcode in gcode_output_stream into a message queued in the socket.
|
||||
*/
|
||||
void flushGcode();
|
||||
void sendGCodeLayer();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
#ifdef ARCUS
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> private_data;
|
||||
const std::unique_ptr<Private> d;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+17
-53
@@ -5,13 +5,13 @@
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
GCodeExport::GCodeExport()
|
||||
: output_stream(&std::cout)
|
||||
, currentPosition(0,0,MM2INT(20))
|
||||
, commandSocket(nullptr)
|
||||
, layer_nr(0)
|
||||
{
|
||||
current_e_value = 0;
|
||||
@@ -29,7 +29,8 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
|
||||
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
|
||||
commandSocket = commandSocket_;
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
|
||||
@@ -134,11 +135,11 @@ double GCodeExport::getCurrentExtrudedVolume()
|
||||
}
|
||||
|
||||
|
||||
double GCodeExport::getTotalFilamentUsed(int extruder_nr)
|
||||
double GCodeExport::getTotalFilamentUsed(int e)
|
||||
{
|
||||
if (extruder_nr == current_extruder)
|
||||
return extruder_attr[extruder_nr].totalFilament + getCurrentExtrudedVolume();
|
||||
return extruder_attr[extruder_nr].totalFilament;
|
||||
if (e == current_extruder)
|
||||
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
|
||||
return extruder_attr[e].totalFilament;
|
||||
}
|
||||
|
||||
double GCodeExport::getTotalPrintTime()
|
||||
@@ -183,41 +184,6 @@ void GCodeExport::writeTypeComment(const char* type)
|
||||
{
|
||||
*output_stream << ";TYPE:" << type << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(PrintFeatureType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PrintFeatureType::OuterWall:
|
||||
*output_stream << ";TYPE:WALL-OUTER\n";
|
||||
break;
|
||||
case PrintFeatureType::InnerWall:
|
||||
*output_stream << ";TYPE:WALL-INNER\n";
|
||||
break;
|
||||
case PrintFeatureType::Skin:
|
||||
*output_stream << ";TYPE:SKIN\n";
|
||||
break;
|
||||
case PrintFeatureType::Support:
|
||||
*output_stream << ";TYPE:SUPPORT\n";
|
||||
break;
|
||||
case PrintFeatureType::Skirt:
|
||||
*output_stream << ";TYPE:SKIRT\n";
|
||||
break;
|
||||
case PrintFeatureType::Infill:
|
||||
*output_stream << ";TYPE:FILL\n";
|
||||
break;
|
||||
case PrintFeatureType::SupportInfill:
|
||||
*output_stream << ";TYPE:SUPPORT\n";
|
||||
break;
|
||||
case PrintFeatureType::MoveCombing:
|
||||
case PrintFeatureType::MoveRetraction:
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GCodeExport::writeLayerComment(int layer_nr)
|
||||
{
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
@@ -388,6 +354,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
}
|
||||
else if (prime_volume > 0.0)
|
||||
{
|
||||
current_e_value += prime_volume;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
@@ -399,16 +366,16 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
else
|
||||
{
|
||||
*output_stream << "G0";
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons travelPoly;
|
||||
PolygonRef travel = travelPoly.newPoly();
|
||||
travel.add(Point(currentPosition.x, currentPosition.y));
|
||||
travel.add(Point(x, y));
|
||||
CommandSocket::getInstance()->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
commandSocket->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
@@ -448,7 +415,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{ // handle retraction limitation
|
||||
double current_extruded_volume = getCurrentExtrudedVolume();
|
||||
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
|
||||
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
|
||||
while (int(extruded_volume_at_previous_n_retractions.size()) >= config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
|
||||
{
|
||||
// extruder switch could have introduced data which falls outside the retraction window
|
||||
// also the retraction_count_max could have changed between the last retraction and this
|
||||
@@ -458,13 +425,13 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
|
||||
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1
|
||||
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].filament_area)
|
||||
{
|
||||
return;
|
||||
}
|
||||
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
|
||||
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
|
||||
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max)
|
||||
{
|
||||
extruded_volume_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
@@ -628,11 +595,8 @@ void GCodeExport::finalize(double moveSpeed, const char* endCode)
|
||||
{
|
||||
writeFanCommand(0);
|
||||
writeCode(endCode);
|
||||
int print_time = getTotalPrintTime();
|
||||
int mat_0 = getTotalFilamentUsed(0);
|
||||
log("Print time: %d\n", print_time);
|
||||
log("Print time (readable): %dh %dm %ds\n", print_time / 60 / 60, (print_time / 60) % 60, print_time % 60);
|
||||
log("Filament: %d\n", mat_0);
|
||||
log("Print time: %d\n", int(getTotalPrintTime()));
|
||||
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
|
||||
for(int n=1; n<MAX_EXTRUDERS; n++)
|
||||
if (getTotalFilamentUsed(n) > 0)
|
||||
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
|
||||
|
||||
+16
-32
@@ -18,9 +18,13 @@ namespace cura {
|
||||
struct CoastingConfig
|
||||
{
|
||||
bool coasting_enable;
|
||||
double coasting_volume;
|
||||
double coasting_speed;
|
||||
double coasting_min_volume;
|
||||
double coasting_volume_move;
|
||||
double coasting_speed_move;
|
||||
double coasting_min_volume_move;
|
||||
|
||||
double coasting_volume_retract;
|
||||
double coasting_speed_retract;
|
||||
double coasting_min_volume_retract;
|
||||
};
|
||||
|
||||
class RetractionConfig
|
||||
@@ -47,12 +51,12 @@ private:
|
||||
int layer_thickness; //!< layer height
|
||||
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
|
||||
public:
|
||||
PrintFeatureType type; //!< name of the feature type
|
||||
const char* name; //!< name of the feature type
|
||||
bool spiralize;
|
||||
RetractionConfig *const retraction_config;
|
||||
|
||||
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), type(type), spiralize(false), retraction_config(retraction_config) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
|
||||
|
||||
/*!
|
||||
* Initialize some of the member variables.
|
||||
@@ -116,11 +120,6 @@ public:
|
||||
return line_width == 0;
|
||||
}
|
||||
|
||||
double getFlowPercentage()
|
||||
{
|
||||
return flow;
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateExtrusion()
|
||||
{
|
||||
@@ -170,7 +169,7 @@ private:
|
||||
, retraction_e_amount_current(0.0)
|
||||
, retraction_e_amount_at_e_start(0.0)
|
||||
, prime_volume(0.0)
|
||||
, last_retraction_prime_speed(0.0)
|
||||
, last_retraction_prime_speed(1.0)
|
||||
{ }
|
||||
};
|
||||
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
|
||||
@@ -187,12 +186,13 @@ private:
|
||||
int currentFanSpeed;
|
||||
EGCodeFlavor flavor;
|
||||
|
||||
double totalPrintTime; //!< The total estimated print time in seconds
|
||||
double totalPrintTime;
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
|
||||
|
||||
|
||||
CommandSocket* commandSocket; //!< for sending travel data
|
||||
unsigned int layer_nr; //!< for sending travel data
|
||||
|
||||
public:
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
GCodeExport();
|
||||
~GCodeExport();
|
||||
|
||||
void setLayerNr(unsigned int layer_nr);
|
||||
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
@@ -229,29 +229,15 @@ public:
|
||||
void setFilamentDiameter(unsigned int n, int diameter);
|
||||
|
||||
double getCurrentExtrudedVolume();
|
||||
|
||||
double getTotalFilamentUsed(int e);
|
||||
|
||||
/*!
|
||||
* Get the total extruded volume for a specific extruder in mm^3
|
||||
*
|
||||
* Retractions and unretractions don't contribute to this.
|
||||
*
|
||||
* \param extruder_nr The extruder number for which to get the total netto extruded volume
|
||||
* \return total filament printed in mm^3
|
||||
*/
|
||||
double getTotalFilamentUsed(int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Get the total estimated print time in seconds
|
||||
*
|
||||
* \return total print time in seconds
|
||||
*/
|
||||
double getTotalPrintTime();
|
||||
void updateTotalPrintTime();
|
||||
void resetTotalPrintTimeAndFilament();
|
||||
|
||||
void writeComment(std::string comment);
|
||||
void writeTypeComment(const char* type);
|
||||
void writeTypeComment(PrintFeatureType type);
|
||||
void writeLayerComment(int layer_nr);
|
||||
void writeLayerCountComment(int layer_count);
|
||||
|
||||
@@ -304,8 +290,6 @@ public:
|
||||
extruder_attr[n].extruder_switch_retraction_distance = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
|
||||
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
|
||||
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
|
||||
|
||||
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
|
||||
}
|
||||
|
||||
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
|
||||
|
||||
+91
-120
@@ -22,18 +22,17 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
|
||||
return *this;
|
||||
}
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow)
|
||||
return &paths.back();
|
||||
paths.emplace_back();
|
||||
GCodePath* ret = &paths.back();
|
||||
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done && paths[paths.size()-1].flow == flow)
|
||||
return &paths[paths.size()-1];
|
||||
paths.push_back(GCodePath());
|
||||
GCodePath* ret = &paths[paths.size()-1];
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->done = false;
|
||||
ret->flow = flow;
|
||||
ret->space_fill_type = space_fill_type;
|
||||
if (config != &storage.travel_config)
|
||||
{
|
||||
last_retraction_config = config->retraction_config;
|
||||
@@ -48,8 +47,9 @@ void GCodePlanner::forceNewPathStart()
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, commandSocket(commandSocket)
|
||||
, layer_nr(layer_nr)
|
||||
, z(z)
|
||||
, layer_thickness(layer_thickness)
|
||||
@@ -105,6 +105,7 @@ void GCodePlanner::setIsInside(bool _is_inside)
|
||||
is_inside = _is_inside;
|
||||
}
|
||||
|
||||
|
||||
bool GCodePlanner::setExtruder(int extruder)
|
||||
{
|
||||
if (extruder == extruder_plans.back().extruder)
|
||||
@@ -157,13 +158,13 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
|
||||
void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
{
|
||||
int max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
|
||||
int max_dist = MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
|
||||
// this function is to be used to move from the boudary of a part to inside the part
|
||||
Point p = lastPosition; // copy, since we are going to move p
|
||||
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
|
||||
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist) != NO_INDEX)
|
||||
{
|
||||
//Move inside again, so we move out of tight 90deg corners
|
||||
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2);
|
||||
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
|
||||
if (comb_boundary_inside.inside(p))
|
||||
{
|
||||
addTravel_simple(p);
|
||||
@@ -186,29 +187,21 @@ void GCodePlanner::addTravel(Point p)
|
||||
if (combed)
|
||||
{
|
||||
bool retract = combPaths.size() > 1;
|
||||
if (!retract)
|
||||
{ // check whether we want to retract
|
||||
for (CombPath& combPath : combPaths)
|
||||
{ // retract when path moves through a boundary
|
||||
if (combPath.cross_boundary || combPath.throughAir)
|
||||
{
|
||||
retract = true;
|
||||
break;
|
||||
}
|
||||
if (!retract && combPaths.size() == 1 && combPaths[0].throughAir && combPaths[0].size() > 2)
|
||||
{ // retract when avoiding obstacles through air
|
||||
retract = true;
|
||||
}
|
||||
if (combPaths.size() == 1)
|
||||
{
|
||||
CombPath path = combPaths[0];
|
||||
if (path.throughAir && !path.cross_boundary && path.size() == 2 && path[0] == lastPosition && path[1] == p)
|
||||
{ // limit the retractions from support to support, which didn't cross anything
|
||||
retract = false;
|
||||
}
|
||||
|
||||
for (unsigned int path_idx = 0; path_idx < combPaths.size() && !retract; path_idx++)
|
||||
{ // retract when path moves through a boundary
|
||||
if (combPaths[path_idx].cross_boundary) { retract = true; }
|
||||
}
|
||||
}
|
||||
|
||||
if (retract && last_retraction_config->zHop > 0)
|
||||
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
path->retract = true;
|
||||
@@ -222,7 +215,7 @@ void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path->retract = retract;
|
||||
for (Point& combPoint : combPath)
|
||||
{
|
||||
@@ -238,13 +231,13 @@ void GCodePlanner::addTravel(Point p)
|
||||
// no combing? always retract!
|
||||
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
|
||||
{
|
||||
if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc)
|
||||
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
|
||||
if (was_inside)
|
||||
{
|
||||
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
|
||||
assert (extr != nullptr);
|
||||
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
|
||||
}
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
@@ -257,16 +250,16 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
|
||||
{
|
||||
if (path == nullptr)
|
||||
{
|
||||
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
|
||||
path = getLatestPathWithConfig(&storage.travel_config);
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, float flow)
|
||||
{
|
||||
getLatestPathWithConfig(config, space_fill_type, flow)->points.push_back(p);
|
||||
getLatestPathWithConfig(config, flow)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
@@ -277,60 +270,51 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
|
||||
for(unsigned int i=1; i<polygon.size(); i++)
|
||||
{
|
||||
Point p1 = polygon[(startIdx + i) % polygon.size()];
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
p0 = p1;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
{
|
||||
Point& p1 = polygon[startIdx];
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning("WARNING: line added as polygon! (gcodePlanner)\n");
|
||||
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type)
|
||||
{
|
||||
if (polygons.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
orderOptimizer.addPolygon(polygons[poly_idx]);
|
||||
}
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for (int poly_idx : orderOptimizer.polyOrder)
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation);
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config, wall_overlap_computation);
|
||||
}
|
||||
}
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist)
|
||||
{
|
||||
LineOrderOptimizer orderOptimizer(lastPosition);
|
||||
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
|
||||
{
|
||||
orderOptimizer.addPolygon(polygons[line_idx]);
|
||||
}
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for (int poly_idx : orderOptimizer.polyOrder)
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
PolygonRef polygon = polygons[poly_idx];
|
||||
int start = orderOptimizer.polyStart[poly_idx];
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
// addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
PolygonRef polygon = polygons[nr];
|
||||
int start = orderOptimizer.polyStart[nr];
|
||||
int end = 1 - start;
|
||||
Point& p0 = polygon[start];
|
||||
addTravel(p0);
|
||||
Point& p1 = polygon[end];
|
||||
addExtrusionMove(p1, config, space_fill_type);
|
||||
addExtrusionMove(p1, config);
|
||||
if (wipe_dist != 0)
|
||||
{
|
||||
int line_width = config->getLineWidth();
|
||||
if (vSize2(p1-p0) > line_width * line_width * 4)
|
||||
{ // otherwise line will get optimized by combining multiple into a single extrusion move
|
||||
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, space_fill_type, 0.0);
|
||||
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,21 +441,7 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
|
||||
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
|
||||
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
|
||||
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
|
||||
|
||||
/*
|
||||
min layer time
|
||||
:
|
||||
: min layer time fan speed min
|
||||
| : :
|
||||
^ max..|__: :
|
||||
| \ :
|
||||
fan | \ :
|
||||
speed min..|... \:___________
|
||||
|________________
|
||||
layer time >
|
||||
|
||||
|
||||
*/
|
||||
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
|
||||
fan_speed = fsml.cool_fan_speed_min;
|
||||
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
|
||||
@@ -482,25 +452,8 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
|
||||
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
|
||||
{
|
||||
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
|
||||
double fan_speed_diff = fsml.cool_fan_speed_max - fsml.cool_fan_speed_min;
|
||||
double layer_time_diff = fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time;
|
||||
double fraction_of_slope = (totalLayerTime - fsml.cool_min_layer_time) / layer_time_diff;
|
||||
fan_speed = fsml.cool_fan_speed_max - fan_speed_diff * fraction_of_slope;
|
||||
fan_speed = fsml.cool_fan_speed_max - (fsml.cool_fan_speed_max-fsml.cool_fan_speed_min) * (totalLayerTime - fsml.cool_min_layer_time) / (fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time);
|
||||
}
|
||||
/*
|
||||
Supposing no influence of minimal layer time; i.e. layer time > min layer time fan speed min:
|
||||
|
||||
max.. fan 'full' on layer
|
||||
| :
|
||||
| :
|
||||
^ min..|..:________________
|
||||
fan | /
|
||||
speed | /
|
||||
zero..|/__________________
|
||||
layer nr >
|
||||
|
||||
|
||||
*/
|
||||
if (layer_nr < fsml.cool_fan_full_layer)
|
||||
{
|
||||
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
|
||||
@@ -513,7 +466,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
{
|
||||
completeConfigs();
|
||||
|
||||
gcode.setLayerNr(layer_nr);
|
||||
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
|
||||
|
||||
gcode.writeLayerComment(layer_nr);
|
||||
|
||||
@@ -549,7 +502,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
}
|
||||
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
|
||||
{
|
||||
gcode.writeTypeComment(path.config->type);
|
||||
gcode.writeTypeComment(path.config->name);
|
||||
last_extrusion_config = path.config;
|
||||
}
|
||||
double speed = path.config->getSpeed();
|
||||
@@ -558,10 +511,10 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
speed *= getTravelSpeedFactor();
|
||||
else
|
||||
speed *= getExtrudeSpeedFactor();
|
||||
|
||||
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
|
||||
|
||||
int64_t nozzle_size = 400; // TODO
|
||||
|
||||
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
|
||||
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
|
||||
{ // !! has effect on path_idx !!
|
||||
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
|
||||
continue;
|
||||
@@ -592,11 +545,13 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
bool coasting = coasting_config.coasting_enable;
|
||||
if (coasting)
|
||||
{
|
||||
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
|
||||
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness
|
||||
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
|
||||
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
|
||||
}
|
||||
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
|
||||
{ // normal path to gcode algorithm
|
||||
if ( // change infill |||||| to /\/\/\/\/ ...
|
||||
if ( // change |||||| to /\/\/\/\/ ...
|
||||
false &&
|
||||
path_idx + 2 < paths.size() // has a next move
|
||||
&& paths[path_idx+1].points.size() == 1 // is single extruded line
|
||||
@@ -607,7 +562,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
|
||||
)
|
||||
{
|
||||
sendPolygon(paths[path_idx+2].config->type, gcode.getPositionXY(), paths[path_idx+2].points.back(), paths[path_idx+2].getLineWidth());
|
||||
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
|
||||
path_idx += 2;
|
||||
}
|
||||
@@ -615,7 +569,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
{
|
||||
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
@@ -642,7 +595,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
|
||||
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
@@ -675,9 +627,10 @@ void GCodePlanner::completeConfigs()
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.setLayerHeight(layer_thickness);
|
||||
|
||||
mesh.insetX_config.setLayerHeight(layer_thickness);
|
||||
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
|
||||
mesh.skin_config.setLayerHeight(layer_thickness);
|
||||
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setLayerHeight(layer_thickness);
|
||||
@@ -703,7 +656,9 @@ void GCodePlanner::processInitialLayersSpeedup()
|
||||
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
@@ -780,12 +735,8 @@ bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int ext
|
||||
}
|
||||
}
|
||||
|
||||
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume)
|
||||
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
|
||||
{
|
||||
if (coasting_volume <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (path_idx + 1 >= paths.size()
|
||||
@@ -797,6 +748,21 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GCodePath& path_next = paths[path_idx + 1];
|
||||
|
||||
if (path_next.retract)
|
||||
{
|
||||
if (coasting_volume_retract <= 0) { return false; }
|
||||
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (coasting_volume_move <= 0) { return false; }
|
||||
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
|
||||
}
|
||||
}
|
||||
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
|
||||
{
|
||||
|
||||
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
|
||||
|
||||
@@ -804,8 +770,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
|
||||
double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed
|
||||
|
||||
int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
|
||||
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
|
||||
// /\ the minimal distance when coasting will coast the full coasting volume instead of linearly less with linearly smaller paths
|
||||
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
|
||||
|
||||
|
||||
std::vector<int64_t> accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...)
|
||||
@@ -858,11 +823,14 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert (acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size()); // something has gone wrong; coasting_min_dist < coasting_dist ?
|
||||
|
||||
|
||||
if (acc_dist_idx_gt_coast_dist == NO_INDEX)
|
||||
{ // something has gone wrong; coasting_min_dist < coasting_dist ?
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist;
|
||||
|
||||
|
||||
Point start;
|
||||
{ // computation of begin point of coasting
|
||||
int64_t residual_dist = actual_coasting_dist - accumulated_dist_per_point[acc_dist_idx_gt_coast_dist - 1];
|
||||
@@ -870,24 +838,27 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
|
||||
Point& b = path.points[point_idx_before_start + 1];
|
||||
start = b + normal(a-b, residual_dist);
|
||||
}
|
||||
|
||||
|
||||
{ // write normal extrude path:
|
||||
for(unsigned int point_idx = 0; point_idx <= point_idx_before_start; point_idx++)
|
||||
{
|
||||
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
|
||||
gcode.writeMove(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
sendPolygon(path.config->type, gcode.getPositionXY(), start, path.getLineWidth());
|
||||
gcode.writeMove(start, extrude_speed, path.getExtrusionMM3perMM());
|
||||
}
|
||||
|
||||
// write coasting path
|
||||
|
||||
if (path_next.retract)
|
||||
{
|
||||
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
|
||||
}
|
||||
|
||||
for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++)
|
||||
{
|
||||
gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0);
|
||||
}
|
||||
|
||||
|
||||
gcode.addLastCoastedVolume(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+33
-46
@@ -10,7 +10,6 @@
|
||||
#include "wallOverlap.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "SpaceFillType.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
@@ -151,7 +150,6 @@ class GCodePath
|
||||
{
|
||||
public:
|
||||
GCodePathConfig* config; //!< The configuration settings of the path.
|
||||
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
|
||||
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
|
||||
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
|
||||
std::vector<Point> points; //!< The points constituting this path.
|
||||
@@ -171,15 +169,6 @@ public:
|
||||
{
|
||||
return flow * config->getExtrusionMM3perMM();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the actual line width (modulated by the flow)
|
||||
* \return the actual line width as shown in layer view
|
||||
*/
|
||||
int getLineWidth()
|
||||
{
|
||||
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
|
||||
}
|
||||
};
|
||||
|
||||
class ExtruderPlan
|
||||
@@ -247,6 +236,8 @@ class GCodePlanner : public NoCopy
|
||||
private:
|
||||
SliceDataStorage& storage;
|
||||
|
||||
CommandSocket* commandSocket;
|
||||
|
||||
int layer_nr;
|
||||
|
||||
int z;
|
||||
@@ -281,11 +272,10 @@ private:
|
||||
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
|
||||
*
|
||||
* \param config The config used for the path returned
|
||||
* \param space_fill_type The type of space filling which this path employs
|
||||
* \param flow (optional) A ratio for the extrusion speed
|
||||
* \return A path with the given config which is now the last path in GCodePlanner::paths
|
||||
*/
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
|
||||
|
||||
/*!
|
||||
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
|
||||
@@ -305,7 +295,7 @@ public:
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
* \param last_position The position of the head at the start of this gcode layer
|
||||
*/
|
||||
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
~GCodePlanner();
|
||||
|
||||
private:
|
||||
@@ -326,22 +316,6 @@ public:
|
||||
return lastPosition;
|
||||
}
|
||||
|
||||
/*!
|
||||
* send a polygon through the command socket from the previous point to the given point
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons pathPoly;
|
||||
PolygonRef path = pathPoly.newPoly();
|
||||
path.add(from);
|
||||
path.add(to);
|
||||
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set whether the next destination is inside a layer part or not.
|
||||
*
|
||||
@@ -400,16 +374,8 @@ public:
|
||||
* \param path (optional) The travel path to which to add the point \p p
|
||||
*/
|
||||
void addTravel_simple(Point p, GCodePath* path = nullptr);
|
||||
|
||||
/*!
|
||||
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
|
||||
*
|
||||
* \param p The point to extrude to
|
||||
* \param config The config with which to extrude
|
||||
* \param space_fill_type Of what space filling type this extrusion move is a part
|
||||
* \param flow A modifier of the extrusion width which would follow from the \p config
|
||||
*/
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
|
||||
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
|
||||
|
||||
@@ -419,10 +385,9 @@ public:
|
||||
* Add lines to the gcode with optimized order.
|
||||
* \param polygons The lines
|
||||
* \param config The config of the lines
|
||||
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
|
||||
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
|
||||
*/
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
|
||||
|
||||
/*!
|
||||
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
|
||||
@@ -473,13 +438,35 @@ public:
|
||||
* \param extruder_plan_idx The index of the current extruder plan
|
||||
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
|
||||
* \param layerThickness The height of the current layer.
|
||||
* \param coasting_volume The volume otherwise leaked during a normal move.
|
||||
* \param coasting_speed The speed at which to move during move-coasting.
|
||||
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
|
||||
* \param coasting_volume_move The volume otherwise leaked during a normal move.
|
||||
* \param coasting_speed_move The speed at which to move during move-coasting.
|
||||
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
|
||||
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
|
||||
* \param coasting_speed_retract The speed at which to move during retract-coasting.
|
||||
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
|
||||
* \return Whether any GCode has been written for the path.
|
||||
*/
|
||||
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
|
||||
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
|
||||
|
||||
/*!
|
||||
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
|
||||
*
|
||||
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
|
||||
*
|
||||
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
|
||||
*
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
* \param path The extrusion path to be written to GCode.
|
||||
* \param path_next The next travel path to be written to GCode.
|
||||
* \param layerThickness The height of the current layer.
|
||||
* \param coasting_volume The volume otherwise leaked.
|
||||
* \param coasting_speed The speed at which to move during coasting.
|
||||
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
|
||||
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
|
||||
* \return Whether any GCode has been written for the path.
|
||||
*/
|
||||
bool writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
|
||||
|
||||
/*!
|
||||
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
|
||||
+348
-180
@@ -2,6 +2,7 @@
|
||||
#include "infill.h"
|
||||
#include "functional"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
@@ -15,28 +16,36 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
switch(pattern)
|
||||
{
|
||||
case EFillMethod::GRID:
|
||||
generateGridInfill(result_lines);
|
||||
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
|
||||
break;
|
||||
case EFillMethod::LINES:
|
||||
generateLineInfill(result_lines, line_distance, fill_angle);
|
||||
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
|
||||
break;
|
||||
case EFillMethod::TRIANGLES:
|
||||
generateTriangleInfill(result_lines);
|
||||
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
|
||||
break;
|
||||
case EFillMethod::CONCENTRIC:
|
||||
PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
|
||||
outline = &outline_offsetted;
|
||||
if (abs(infill_line_width - line_distance) < 10)
|
||||
if (outlineOffset != 0)
|
||||
{
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
|
||||
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
|
||||
outline = &outline_offsetted;
|
||||
}
|
||||
else
|
||||
if (abs(extrusion_width - line_distance) < 10)
|
||||
{
|
||||
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
|
||||
}
|
||||
else
|
||||
{
|
||||
generateConcentricInfill(*outline, result_polygons, line_distance);
|
||||
}
|
||||
break;
|
||||
case EFillMethod::ZIG_ZAG:
|
||||
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
|
||||
if (outlineOffset != 0)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
|
||||
outline = &outline_offsetted;
|
||||
}
|
||||
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
|
||||
break;
|
||||
default:
|
||||
logError("Fill pattern has unknown value.\n");
|
||||
@@ -44,9 +53,9 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
|
||||
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -56,13 +65,13 @@ void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, P
|
||||
result.add(r);
|
||||
}
|
||||
Polygons next_outline;
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
outline = next_outline;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
@@ -76,216 +85,375 @@ void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int in
|
||||
}
|
||||
|
||||
|
||||
void Infill::generateGridInfill(Polygons& result)
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
{
|
||||
generateLineInfill(result, line_distance * 2, fill_angle);
|
||||
generateLineInfill(result, line_distance * 2, fill_angle + 90);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 90);
|
||||
}
|
||||
|
||||
void Infill::generateTriangleInfill(Polygons& result)
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, double infillOverlap,
|
||||
double rotation)
|
||||
{
|
||||
generateLineInfill(result, line_distance * 3, fill_angle);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 60);
|
||||
generateLineInfill(result, line_distance * 3, fill_angle + 120);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 60);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 120);
|
||||
}
|
||||
|
||||
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
|
||||
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
|
||||
{
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(rotation_matrix.unapply(from));
|
||||
p.add(rotation_matrix.unapply(to));
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
|
||||
auto compare_int64_t = [](const void* a, const void* b)
|
||||
{
|
||||
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
|
||||
if (n < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (n < 0) return -1;
|
||||
if (n > 0) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
int scanline_idx = 0;
|
||||
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
|
||||
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
|
||||
{
|
||||
std::vector<int64_t>& crossings = cut_list[scanline_idx];
|
||||
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
|
||||
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
|
||||
{
|
||||
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
|
||||
{ // segment is too short to create infill
|
||||
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
|
||||
continue;
|
||||
}
|
||||
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
|
||||
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
|
||||
}
|
||||
scanline_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
|
||||
{
|
||||
PointMatrix rotation_matrix(fill_angle);
|
||||
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
|
||||
bool connected_zigzags = false;
|
||||
bool safe_outline_offset = false;
|
||||
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
|
||||
}
|
||||
|
||||
|
||||
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
|
||||
{
|
||||
bool safe_outline_offset = true;
|
||||
|
||||
PointMatrix rotation_matrix(fill_angle);
|
||||
if (use_endpieces)
|
||||
{
|
||||
if (connected_zigzags)
|
||||
{
|
||||
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
|
||||
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* rough explanation of the zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
|
||||
*
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
|
||||
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
|
||||
*/
|
||||
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
|
||||
{
|
||||
if (line_distance == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (in_outline.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (lineSpacing == 0) return;
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
Polygons outline;
|
||||
if (outline_offset != 0)
|
||||
{
|
||||
PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
outline = in_outline;
|
||||
}
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline = outline.offset(infill_overlap);
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
|
||||
if (outline.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
outline.applyMatrix(rotation_matrix);
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / line_distance;
|
||||
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
|
||||
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
|
||||
{
|
||||
cut_list.push_back(std::vector<int64_t>());
|
||||
}
|
||||
|
||||
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
|
||||
{
|
||||
PolygonRef poly = outline[poly_idx];
|
||||
Point p0 = poly.back();
|
||||
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
|
||||
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
|
||||
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
|
||||
{
|
||||
Point p1 = poly[point_idx];
|
||||
if (p1.X == p0.X)
|
||||
{
|
||||
zigzag_connector_processor.registerVertex(p1);
|
||||
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
|
||||
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
|
||||
Point p1 = outline[poly_idx][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
|
||||
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
|
||||
// otherwise only 1 will be created, counting as an actual intersection
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
}
|
||||
else
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
}
|
||||
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
|
||||
{
|
||||
int x = scanline_idx * line_distance;
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cut_list[scanline_idx - scanline_min_idx].push_back(y);
|
||||
Point scanline_linesegment_intersection(x, y);
|
||||
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
}
|
||||
zigzag_connector_processor.registerVertex(p1);
|
||||
p0 = p1;
|
||||
}
|
||||
zigzag_connector_processor.registerPolyFinished();
|
||||
}
|
||||
|
||||
if (cut_list.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
|
||||
{
|
||||
return; // don't add connection if boundary already contains whole outline!
|
||||
}
|
||||
|
||||
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
|
||||
{
|
||||
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
|
||||
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
|
||||
}
|
||||
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
|
||||
{
|
||||
// if (in_outline.size() == 0) return;
|
||||
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
Polygons empty;
|
||||
Polygons outline = in_outline.difference(empty); // copy
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven = false;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
Point lastPoint = p0;
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
lastPoint = p1;
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
|
||||
addLine(lastPoint, Point(x,y));
|
||||
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
|
||||
{ // add whole unevenBoundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
|
||||
{
|
||||
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
|
||||
}
|
||||
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
|
||||
unevenBoundarySegment.clear();
|
||||
}
|
||||
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
|
||||
unevenBoundarySegment.push_back(Point(x,y));
|
||||
else
|
||||
unevenBoundarySegment.clear();
|
||||
|
||||
}
|
||||
lastPoint = Point(x,y);
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
}
|
||||
|
||||
}
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (isEvenScanSegment)
|
||||
addLine(lastPoint, p1);
|
||||
else if (connect_zigzags)
|
||||
unevenBoundarySegment.push_back(p1);
|
||||
}
|
||||
|
||||
lastPoint = p1;
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
|
||||
{
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
{
|
||||
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
else if (!firstBoundarySegmentEndsInEven)
|
||||
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
|
||||
}
|
||||
|
||||
if (cutList.size() == 0) return;
|
||||
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
|
||||
{
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
std::vector<Point> boundarySegment;
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven = true;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
else boundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && !isEvenScanSegment)
|
||||
{ // add whole boundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < boundarySegment.size(); p++)
|
||||
{
|
||||
addLine(boundarySegment[p-1], boundarySegment[p]);
|
||||
}
|
||||
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
|
||||
boundarySegment.clear();
|
||||
}
|
||||
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
|
||||
{
|
||||
boundarySegment.clear();
|
||||
boundarySegment.emplace_back(x,y);
|
||||
} else
|
||||
boundarySegment.clear();
|
||||
|
||||
}
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
boundarySegment.emplace_back(x,y);
|
||||
}
|
||||
|
||||
}
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment)
|
||||
boundarySegment.push_back(p1);
|
||||
|
||||
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
|
||||
{
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+76
-134
@@ -4,141 +4,99 @@
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
// #include "ZigzagConnectorProcessor.h"
|
||||
#include "infill/ZigzagConnectorProcessor.h"
|
||||
#include "infill/NoZigZagConnectorProcessor.h"
|
||||
#include "infill/ActualZigzagConnectorProcessor.h"
|
||||
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
|
||||
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/AABB.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class Infill
|
||||
{
|
||||
EFillMethod pattern; //!< the space filling pattern of the infill to generate
|
||||
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
|
||||
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
|
||||
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
|
||||
int infill_line_width; //!< The line width of the infill lines to generate
|
||||
int line_distance; //!< The distance between two infill lines / polygons
|
||||
int infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill
|
||||
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
|
||||
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
|
||||
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
|
||||
|
||||
public:
|
||||
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, bool remove_overlapping_perimeters, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
|
||||
: pattern(pattern)
|
||||
, in_outline(in_outline)
|
||||
, outline_offset(outline_offset)
|
||||
, remove_overlapping_perimeters(remove_overlapping_perimeters)
|
||||
, infill_line_width(infill_line_width)
|
||||
, line_distance(line_distance)
|
||||
, infill_overlap(infill_overlap)
|
||||
, fill_angle(fill_angle)
|
||||
, connected_zigzags(connected_zigzags)
|
||||
, use_endpieces(use_endpieces)
|
||||
class Infill
|
||||
{
|
||||
}
|
||||
/*!
|
||||
* Generate the infill.
|
||||
*
|
||||
* \param result_polygons (output) The resulting polygons (from concentric infill)
|
||||
* \param result_lines (output) The resulting line segments (from linear infill types)
|
||||
* \param in_between (optional output) The areas in between two concecutive concentric infill polygons
|
||||
*/
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
|
||||
EFillMethod pattern;
|
||||
const Polygons& in_outline;
|
||||
int outlineOffset;
|
||||
bool avoidOverlappingPerimeters;
|
||||
int extrusion_width;
|
||||
int line_distance;
|
||||
double infill_overlap;
|
||||
double fill_angle;
|
||||
bool connect_zigzags;
|
||||
bool use_endPieces;
|
||||
|
||||
private:
|
||||
public:
|
||||
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
|
||||
: pattern(pattern)
|
||||
, in_outline(in_outline)
|
||||
, outlineOffset(outlineOffset)
|
||||
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
|
||||
, extrusion_width(extrusion_width)
|
||||
, line_distance(line_distance)
|
||||
, infill_overlap(infill_overlap)
|
||||
, fill_angle(fill_angle)
|
||||
, connect_zigzags(connect_zigzags)
|
||||
, use_endPieces(use_endPieces)
|
||||
{
|
||||
}
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
|
||||
};
|
||||
|
||||
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
|
||||
|
||||
/*!
|
||||
* Generate sparse concentric infill
|
||||
* \param outline The actual outline of the area within which to generate infill
|
||||
* \param result (output) The resulting polygons
|
||||
* \param inset_value The offset between each consecutive two polygons
|
||||
*/
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
|
||||
/*!
|
||||
* Generate dense concentric infill (100%)
|
||||
*
|
||||
* \param outline The actual outline of the area within which to generate infill
|
||||
* \param result (output) The resulting polygons
|
||||
* \param in_between (output) The areas in between each two consecutive polygons
|
||||
* \param remove_overlapping_perimeters Whether to remove overlapping perimeter parts
|
||||
*/
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool remove_overlapping_perimeters);
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
|
||||
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
/*!
|
||||
* Generate a rectangular grid of infill lines
|
||||
* \param result (output) The resulting lines
|
||||
*/
|
||||
void generateGridInfill(Polygons& result);
|
||||
|
||||
/*!
|
||||
* Generate a triangular grid of infill lines
|
||||
* \param result (output) The resulting lines
|
||||
*/
|
||||
void generateTriangleInfill(Polygons& result);
|
||||
|
||||
/*!
|
||||
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
|
||||
* \param result (output) The resulting lines
|
||||
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
|
||||
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param boundary The axis aligned boundary box within which the polygon is
|
||||
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
|
||||
*/
|
||||
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param fill_angle The angle of the generated lines
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
*
|
||||
* algorithm:
|
||||
* 1) for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2) for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
*/
|
||||
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
|
||||
|
||||
/*!
|
||||
* Function for creating linear based infill types (Lines, ZigZag).
|
||||
*
|
||||
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
|
||||
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
|
||||
*
|
||||
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
|
||||
*
|
||||
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
|
||||
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
|
||||
* \param zigzag_connector_processor The processor used to generate zigzag connectors
|
||||
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
|
||||
*/
|
||||
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
|
||||
|
||||
/*!
|
||||
* adapted from generateLineInfill(.)
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
|
||||
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
|
||||
* zigzag:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
|
||||
* sigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* Note that ZigZag consists of 3 types:
|
||||
* - without endpieces
|
||||
* - with disconnected endpieces
|
||||
* - with connected endpieces
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'
|
||||
*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* if polygon intersects with even scanline again (instead of odd)
|
||||
* dont add the last line segment to the boundary (unless [connect_zigzags])
|
||||
*
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
@@ -148,37 +106,21 @@ private:
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
* ^ ^ no endpieces
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* v disconnected end piece: leave out last line segment
|
||||
* _____
|
||||
* | | | \ .
|
||||
* | | | ,
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
*
|
||||
*
|
||||
* v connected end piece
|
||||
* ________
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/ .
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
*
|
||||
* \param result (output) The resulting lines
|
||||
* \param line_distance The distance between two lines which are in the same direction
|
||||
* \param fill_angle The angle of the generated lines
|
||||
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
|
||||
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
|
||||
* ^ disconnected end piece
|
||||
*/
|
||||
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
|
||||
};
|
||||
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
|
||||
|
||||
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
|
||||
}//namespace cura
|
||||
|
||||
#endif//INFILL_H
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessor.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* In contrast to NoZigZagConnectorProcessor
|
||||
*/
|
||||
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
/*!
|
||||
* The line segments belonging the zigzag connector to which the very first vertex belongs.
|
||||
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
|
||||
*
|
||||
* Because the boundary polygon may start in in the middle of a zigzag connector,
|
||||
*/
|
||||
std::vector<Point> first_zigzag_connector;
|
||||
/*!
|
||||
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
|
||||
*/
|
||||
std::vector<Point> zigzag_connector;
|
||||
|
||||
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
|
||||
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
|
||||
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
|
||||
|
||||
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessor(rotation_matrix, result)
|
||||
, is_first_zigzag_connector(true)
|
||||
, first_zigzag_connector_ends_in_even_scanline(true)
|
||||
, last_scanline_is_even(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -1,25 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "NoZigZagConnectorProcessor.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NoZigZagConnectorProcessor::registerPolyFinished()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,28 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
|
||||
{
|
||||
public:
|
||||
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessor(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
|
||||
void registerVertex(const Point& vertex);
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -1,154 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
|
||||
*
|
||||
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
|
||||
* at the same time as the lines are created.
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
|
||||
* zigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'
|
||||
*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* if polygon intersects with even scanline again (instead of odd)
|
||||
* dont add the last line segment to the boundary (unless [connected_zigzags])
|
||||
*
|
||||
* Note that ZigZag consists of 3 types:
|
||||
* - without endpieces
|
||||
* - with disconnected endpieces
|
||||
* - with connected endpieces
|
||||
*
|
||||
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
|
||||
* The inheritance structure is as such:
|
||||
* ZigzagConnectorProcessor
|
||||
* / \ .
|
||||
* / \ .
|
||||
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
|
||||
* / \ for lines infill .
|
||||
* / \ .
|
||||
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
|
||||
* / \ for zigzag infill (without end pieces) .
|
||||
* / \ .
|
||||
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
|
||||
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
|
||||
*
|
||||
* v v zigzag connectors
|
||||
* <--
|
||||
* :___: : < scanlines
|
||||
* | | |
|
||||
* | | | < infill lines along scanlines
|
||||
* | |___|
|
||||
* : : :
|
||||
* --> winding order of polygon
|
||||
*
|
||||
* ^ = even scanline
|
||||
* ^ ^ no endpieces
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
|
||||
*
|
||||
* ________
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/ .
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ connected end piece
|
||||
* include a boundary segment also if it starts in an odd scanline and ends odd,
|
||||
* or starts in an even scanline and ends in an even scanline,
|
||||
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
|
||||
*
|
||||
* _____
|
||||
* | | | \ .
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
|
||||
*/
|
||||
class ZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
|
||||
Polygons& result; //!< The result of the computation
|
||||
|
||||
virtual ~ZigzagConnectorProcessor()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Add a line to the result bu unapplying the rotation rotation_matrix.
|
||||
*
|
||||
* \param from The one end of the line segment
|
||||
* \param to The other end of the line segment
|
||||
*/
|
||||
void addLine(Point from, Point to)
|
||||
{
|
||||
PolygonRef line_poly = result.newPoly();
|
||||
line_poly.add(rotation_matrix.unapply(from));
|
||||
line_poly.add(rotation_matrix.unapply(to));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Basic constructor. Inheriting children should call this constructor.
|
||||
*
|
||||
* \param rotation_matrix The rotation matrix used to enforce the infill angle
|
||||
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
|
||||
*/
|
||||
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: rotation_matrix(rotation_matrix)
|
||||
, result(result)
|
||||
{}
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Handle the next vertex on the outer boundary.
|
||||
* \param vertex The vertex
|
||||
*/
|
||||
virtual void registerVertex(const Point& vertex) = 0;
|
||||
|
||||
/*!
|
||||
* Handle the next intersection between a scanline and the outer boundary.
|
||||
*
|
||||
* \param intersection The intersection
|
||||
* \param scanline_is_even Whether the scanline was even
|
||||
*/
|
||||
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
|
||||
|
||||
/*!
|
||||
* Handle the end of a polygon and prepare for the next.
|
||||
* This function should reset all member variables.
|
||||
*/
|
||||
virtual void registerPolyFinished() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
|
||||
@@ -1,75 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even;
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even)
|
||||
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
|
||||
addLine(last_connector_point, intersection);
|
||||
}
|
||||
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
|
||||
{ // add whole zigzag_connector (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
addLine(zigzag_connector.back(), intersection);
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
|
||||
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
|
||||
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
last_connector_point = intersection;
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
|
||||
{
|
||||
// write end segment if needed (first half of start/end-crossing segment)
|
||||
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write begin segment if needed (second half of start/end-crossing segment)
|
||||
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
|| is_first_zigzag_connector)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
|
||||
{
|
||||
public:
|
||||
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
|
||||
@@ -1,79 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even;
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even && !this_scanline_is_even)
|
||||
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
|
||||
addLine(last_connector_point, intersection);
|
||||
}
|
||||
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
|
||||
{ // add whole oddBoundarySegment (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
// skip the last segment to the [intersection]
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
|
||||
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
|
||||
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
last_connector_point = intersection;
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
|
||||
{
|
||||
// write end segment if needed (first half of start/end-crossing segment)
|
||||
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write begin segment if needed (second half of start/end-crossing segment)
|
||||
if (last_scanline_is_even || is_first_zigzag_connector)
|
||||
{
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// write very last line segment if needed
|
||||
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{ // only add last element if boundary segment ends in odd scanline
|
||||
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,26 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
|
||||
{
|
||||
|
||||
public:
|
||||
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
|
||||
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
|
||||
{
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(vertex);
|
||||
}
|
||||
else if (last_scanline_is_even)
|
||||
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
|
||||
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
|
||||
addLine(last_connector_point, vertex);
|
||||
}
|
||||
else
|
||||
{ // it's yet unclear whether the line segment should be included, so we store it until we know
|
||||
zigzag_connector.push_back(vertex);
|
||||
}
|
||||
last_connector_point = vertex;
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,32 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ActualZigzagConnectorProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
|
||||
{
|
||||
protected:
|
||||
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
|
||||
|
||||
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ActualZigzagConnectorProcessor(rotation_matrix, result)
|
||||
, last_connector_point(0,0)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void registerVertex(const Point& vertex);
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
|
||||
@@ -1,72 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "ZigzagConnectorProcessorNoEndPieces.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
|
||||
{
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(vertex);
|
||||
}
|
||||
else if (last_scanline_is_even)
|
||||
{
|
||||
zigzag_connector.push_back(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
|
||||
{
|
||||
bool previous_scanline_is_even = last_scanline_is_even;
|
||||
last_scanline_is_even = scanline_is_even;
|
||||
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
|
||||
|
||||
if (is_first_zigzag_connector)
|
||||
{
|
||||
first_zigzag_connector.push_back(intersection);
|
||||
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
|
||||
is_first_zigzag_connector = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previous_scanline_is_even && !this_scanline_is_even)
|
||||
{ // add whole zigzag_connector (including the just obtained point)
|
||||
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
|
||||
{
|
||||
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
|
||||
}
|
||||
addLine(zigzag_connector.back(), intersection);
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
}
|
||||
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
|
||||
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
|
||||
{
|
||||
zigzag_connector.push_back(intersection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
|
||||
{
|
||||
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|
||||
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
|
||||
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
|
||||
{
|
||||
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
|
||||
}
|
||||
}
|
||||
// reset member variables
|
||||
is_first_zigzag_connector = true;
|
||||
first_zigzag_connector_ends_in_even_scanline = true;
|
||||
last_scanline_is_even = false;
|
||||
first_zigzag_connector.clear();
|
||||
zigzag_connector.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -1,29 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "ActualZigzagConnectorProcessor.h"
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
|
||||
{
|
||||
public:
|
||||
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
|
||||
: ActualZigzagConnectorProcessor(rotation_matrix, result)
|
||||
{
|
||||
}
|
||||
|
||||
void registerVertex(const Point& vertex);
|
||||
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
|
||||
void registerPolyFinished();
|
||||
};
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
|
||||
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
|
||||
+50
-9
@@ -3,7 +3,7 @@
|
||||
#include "utils/polygonUtils.h"
|
||||
namespace cura {
|
||||
|
||||
void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
if (insetCount == 0)
|
||||
{
|
||||
@@ -16,14 +16,26 @@ void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, in
|
||||
part->insets.push_back(Polygons());
|
||||
if (i == 0)
|
||||
{
|
||||
PolygonUtils::offsetSafe(part->outline, -line_width_0 / 2 - wall_0_inset, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
|
||||
if (line_width_0 < nozzle_width)
|
||||
{
|
||||
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PolygonUtils::offsetSafe(part->outline, - line_width_0/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
|
||||
}
|
||||
} else if (i == 1)
|
||||
{
|
||||
int offset_from_first_boundary_for_edge_of_outer_wall = -line_width_0 / 2; // the outer bounds of the perimeter gaps
|
||||
// you might think this /\ should be (1): -line_width_0 / 2; or you might think it should be (2): -nozzle_size / 2
|
||||
// (1): the volume extruded is the right volume; the infill gaps overlap more with the outer wall
|
||||
// (2): the outer wall already fills up extra space due to the fact that the nozzle hole overlaps with a part inside the outer wall
|
||||
PolygonUtils::offsetSafe(part->insets[0], -line_width_0 / 2 + wall_0_inset - line_width_x / 2, offset_from_first_boundary_for_edge_of_outer_wall, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
if (line_width_0 < nozzle_width)
|
||||
{
|
||||
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
|
||||
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
|
||||
PolygonUtils::offsetSafe(part->insets[0], nozzle_width/2 - line_width_0 - line_width_x/2, offset_from_first_boundary_for_edge_of_outer_wall, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
}
|
||||
else
|
||||
{
|
||||
PolygonUtils::offsetSafe(part->insets[0], -line_width_0/2 - line_width_x/2, -line_width_0/2, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
}
|
||||
} else
|
||||
{
|
||||
PolygonUtils::offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
@@ -40,11 +52,11 @@ void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, in
|
||||
}
|
||||
}
|
||||
|
||||
void generateInsets(SliceLayer* layer, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
|
||||
{
|
||||
generateInsets(&layer->parts[partNr], wall_0_inset, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
|
||||
generateInsets(&layer->parts[partNr], nozzle_width, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
|
||||
}
|
||||
|
||||
//Remove the parts which did not generate an inset. As these parts are too small to print,
|
||||
@@ -59,4 +71,33 @@ void generateInsets(SliceLayer* layer, int wall_0_inset, int line_width_0, int l
|
||||
}
|
||||
}
|
||||
|
||||
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
// optimize all the polygons. Every point removed saves time in the long run.
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls[0].simplify();
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() < 1)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
|
||||
}
|
||||
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() > 0)
|
||||
{
|
||||
for(int i=1; i<insetCount; i++)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(Polygons());
|
||||
PolygonUtils::offsetExtrusionWidth(reinforcement_wall.wall_reinforcement_axtra_walls[i-1], true, line_width_x, reinforcement_wall.wall_reinforcement_axtra_walls[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
|
||||
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls[i].simplify();
|
||||
if (reinforcement_wall.wall_reinforcement_axtra_walls[i].size() < 1)
|
||||
{
|
||||
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+14
-4
@@ -11,14 +11,14 @@ namespace cura
|
||||
* Generates the insets / perimeters for a single layer part.
|
||||
*
|
||||
* \param part The part for which to generate the insets.
|
||||
* \param wall_0_inset The offset applied to the outer wall
|
||||
* \param nozzle_width The diameter of the hole in the nozzle
|
||||
* \param line_width_0 line width of the outer wall
|
||||
* \param line_width_x line width of other walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \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 part)
|
||||
*/
|
||||
void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generates the insets / perimeters for all parts in a layer.
|
||||
@@ -27,14 +27,24 @@ void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, in
|
||||
* which leads to better results for a smaller \p line_width_0 than \p line_width_x and when printing the outer wall last.
|
||||
*
|
||||
* \param layer The layer for which to generate the insets.
|
||||
* \param wall_0_inset The offset applied to the outer wall
|
||||
* \param nozzle_width The diameter of the hole in the nozzle
|
||||
* \param line_width_0 line width of the outer wall
|
||||
* \param line_width_x line width of other walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \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 part)
|
||||
*/
|
||||
void generateInsets(SliceLayer* layer, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generates the wall reinforcement extra walls for a single layer part.
|
||||
*
|
||||
* \param part The part for which to generate the extra walls.
|
||||
* \param line_width_x line width of the walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
|
||||
*/
|
||||
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "LayerPart.h"
|
||||
#include "../settings.h"
|
||||
#include "../Progress.h"
|
||||
#include "layerPart.h"
|
||||
#include "settings.h"
|
||||
#include "Progress.h"
|
||||
|
||||
#include "../utils/SVG.h" // debug output
|
||||
#include "utils/SVG.h" // debug output
|
||||
|
||||
/*
|
||||
The layer-part creation step is the first step in creating actual useful data for 3D printing.
|
||||
@@ -1,10 +1,10 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_LAYERPART_H
|
||||
#define SLICER_LAYERPART_H
|
||||
#ifndef LAYERPART_H
|
||||
#define LAYERPART_H
|
||||
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "Slicer.h"
|
||||
#include "../commandSocket.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "slicer.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
/*
|
||||
The layer-part creation step is the first step in creating actual useful data for 3D printing.
|
||||
@@ -28,4 +28,4 @@ void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_l
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_LAYERPART_H
|
||||
#endif//LAYERPART_H
|
||||
+5
-7
@@ -66,7 +66,7 @@ void print_call(int argc, char **argv)
|
||||
|
||||
void connect(int argc, char **argv)
|
||||
{
|
||||
CommandSocket::instantiate();
|
||||
CommandSocket* commandSocket = new CommandSocket();
|
||||
std::string ip;
|
||||
int port = 49674;
|
||||
|
||||
@@ -107,7 +107,7 @@ void connect(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
CommandSocket::getInstance()->connect(ip, port);
|
||||
commandSocket->connect(ip, port);
|
||||
}
|
||||
|
||||
void slice(int argc, char **argv)
|
||||
@@ -148,13 +148,12 @@ void slice(int argc, char **argv)
|
||||
FffProcessor::getInstance()->time_keeper.restart();
|
||||
delete meshgroup;
|
||||
meshgroup = new MeshGroup(FffProcessor::getInstance());
|
||||
last_extruder_train = meshgroup->createExtruderTrain(0);
|
||||
last_settings_object = meshgroup;
|
||||
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}else{
|
||||
cura::logError("Unknown option: %s\n", str);
|
||||
}
|
||||
@@ -194,7 +193,7 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
last_settings_object = meshgroup->meshes.back();
|
||||
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
@@ -240,8 +239,7 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
int extruder_count = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
|
||||
for (extruder_train_nr = 0; extruder_train_nr < extruder_count; extruder_train_nr++)
|
||||
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_train_nr++)
|
||||
{ // initialize remaining extruder trains and load the defaults
|
||||
meshgroup->createExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // create new extruder train objects or use already existing ones
|
||||
}
|
||||
|
||||
+8
-39
@@ -5,7 +5,7 @@ namespace cura
|
||||
{
|
||||
|
||||
const int vertex_meld_distance = MM2INT(0.03);
|
||||
static inline uint32_t pointHash(const Point3& p)
|
||||
static inline uint32_t pointHash(Point3& p)
|
||||
{
|
||||
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
|
||||
}
|
||||
@@ -15,21 +15,12 @@ Mesh::Mesh(SettingsBaseVirtual* parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
{
|
||||
int vi0 = findIndexOfVertex(v0);
|
||||
int vi1 = findIndexOfVertex(v1);
|
||||
int vi2 = findIndexOfVertex(v2);
|
||||
return addFace(vi0, vi1, vi2);
|
||||
}
|
||||
|
||||
bool Mesh::addFace(int vi0, int vi1, int vi2)
|
||||
{
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2)
|
||||
{
|
||||
// the face has two vertices which get assigned the same location. Don't add the face.
|
||||
return false;
|
||||
}
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2) return; // the face has two vertices which get assigned the same location. Don't add the face.
|
||||
|
||||
int idx = faces.size(); // index of face to be added
|
||||
faces.emplace_back();
|
||||
@@ -40,8 +31,6 @@ bool Mesh::addFace(int vi0, int vi1, int vi2)
|
||||
vertices[face.vertex_index[0]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[1]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[2]].connected_faces.push_back(idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mesh::clear()
|
||||
@@ -66,24 +55,16 @@ void Mesh::finish()
|
||||
}
|
||||
}
|
||||
|
||||
Point3 Mesh::min() const
|
||||
Point3 Mesh::min()
|
||||
{
|
||||
return aabb.min;
|
||||
}
|
||||
Point3 Mesh::max() const
|
||||
Point3 Mesh::max()
|
||||
{
|
||||
return aabb.max;
|
||||
}
|
||||
|
||||
void Mesh::addVertex(const Point3& v)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
vertex_hash_map[hash].push_back(vertices.size());
|
||||
vertices.emplace_back(v);
|
||||
aabb.include(v);
|
||||
}
|
||||
|
||||
int Mesh::findIndexOfVertex(const Point3& v)
|
||||
int Mesh::findIndexOfVertex(Point3& v)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
|
||||
@@ -126,7 +107,7 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
|
||||
|
||||
|
||||
*/
|
||||
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const
|
||||
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
{
|
||||
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
|
||||
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
|
||||
@@ -198,16 +179,4 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const
|
||||
return bestIdx;
|
||||
}
|
||||
|
||||
bool Mesh::registerFaceSlice(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result)
|
||||
{
|
||||
// do nothing for a non-textured mesh
|
||||
return false;
|
||||
}
|
||||
|
||||
float Mesh::getColor(MatCoord bitmap_coord)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
}//namespace cura
|
||||
+5
-32
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "settings.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -66,31 +65,12 @@ public:
|
||||
|
||||
Mesh(SettingsBaseVirtual* parent); //!< initializes the settings
|
||||
|
||||
virtual ~Mesh() {} //!< Destructor
|
||||
|
||||
/*!
|
||||
* add a face to the mesh without settings it's connected_faces.
|
||||
*
|
||||
* Don't add a face when the surface is zero mm^2
|
||||
*
|
||||
* \return whether a face has been added
|
||||
*/
|
||||
bool addFace(Point3& v0, Point3& v1, Point3& v2);
|
||||
/*!
|
||||
* add a face to the mesh without settings it's connected_faces.
|
||||
*
|
||||
* Don't add a face when the surface is zero mm^2
|
||||
*
|
||||
* \return whether a face has been added
|
||||
*/
|
||||
bool addFace(int vi0, int vi1, int vi2);
|
||||
void addVertex(const Point3& v);
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
|
||||
void clear(); //!< clears all data
|
||||
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
|
||||
|
||||
Point3 min() const; //!< min (in x,y and z) vertex of the bounding box
|
||||
Point3 max() const; //!< max (in x,y and z) vertex of the bounding box
|
||||
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
|
||||
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
|
||||
|
||||
/*!
|
||||
* Offset the whole mesh (all vertices and the bounding box).
|
||||
@@ -104,20 +84,13 @@ public:
|
||||
aabb.offset(offset);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return Whether a texture line segment has been created
|
||||
*/
|
||||
virtual bool registerFaceSlice(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result);
|
||||
|
||||
virtual float getColor(MatCoord bitmap_coord);
|
||||
|
||||
private:
|
||||
int findIndexOfVertex(const Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
|
||||
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
|
||||
/*!
|
||||
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
|
||||
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
|
||||
*/
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const;
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "MultiVolumes.h"
|
||||
#include "multiVolumes.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef SLICER_MULTIVOLUMES_H
|
||||
#define SLICER_MULTIVOLUMES_H
|
||||
#ifndef MULTIVOLUMES_H
|
||||
#define MULTIVOLUMES_H
|
||||
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "Slicer.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "slicer.h"
|
||||
|
||||
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
|
||||
namespace cura {
|
||||
@@ -17,4 +17,4 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes, int overlap);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_MULTIVOLUMES_H
|
||||
#endif//MULTIVOLUMES_H
|
||||
+106
-97
@@ -2,7 +2,6 @@
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/BucketGrid2D.h"
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
#define INLINE static inline
|
||||
|
||||
@@ -16,16 +15,17 @@ void PathOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
|
||||
{
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = point_idx;
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
@@ -37,50 +37,46 @@ void PathOrderOptimizer::optimize()
|
||||
|
||||
|
||||
Point prev_point = startPoint;
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
{
|
||||
int best_poly_idx = -1;
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
{
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
continue;
|
||||
}
|
||||
|
||||
assert (polygons[poly_idx].size() != 2);
|
||||
assert (polygons[i_polygon].size() != 2);
|
||||
|
||||
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
|
||||
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best_poly_idx = poly_idx;
|
||||
best = i_polygon;
|
||||
bestDist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best_poly_idx].size() != 2);
|
||||
assert(polygons[best].size() != 2);
|
||||
|
||||
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
|
||||
prev_point = polygons[best][polyStart[best]];
|
||||
|
||||
picked[best_poly_idx] = true;
|
||||
polyOrder.push_back(best_poly_idx);
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Failed to find next closest polygon.\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
int poly_idx = polyOrder[order_idx];
|
||||
int poly_idx = polyOrder[n];
|
||||
int point_idx = getPolyStart(prev_point, poly_idx);
|
||||
polyStart[poly_idx] = point_idx;
|
||||
prev_point = polygons[poly_idx][point_idx];
|
||||
@@ -103,23 +99,22 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
|
||||
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
|
||||
int best_point_idx = -1;
|
||||
float best_point_score = std::numeric_limits<float>::infinity();
|
||||
Point p0 = poly.back();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
|
||||
{
|
||||
Point& p1 = poly[point_idx];
|
||||
Point& p2 = poly[(point_idx + 1) % poly.size()];
|
||||
int64_t dist = vSize2(p1 - prev_point);
|
||||
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
|
||||
// this score is in the order of 5 mm
|
||||
if (dist + is_on_inside_corner_score < best_point_score)
|
||||
float dist = vSize2f(poly[i_point] - prev_point);
|
||||
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
|
||||
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
best_point_idx = point_idx;
|
||||
best_point_score = dist + is_on_inside_corner_score;
|
||||
best_point_idx = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
return best_point_idx;
|
||||
}
|
||||
@@ -152,118 +147,132 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
|
||||
*/
|
||||
void LineOrderOptimizer::optimize()
|
||||
{
|
||||
int gridSize = 5000; // the size of the cells in the hash grid. TODO
|
||||
int gridSize = 5000; // the size of the cells in the hash grid.
|
||||
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best_point_idx = -1;
|
||||
float best_point_dist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
|
||||
{
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < best_point_dist)
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best_point_idx = point_idx;
|
||||
best_point_dist = dist;
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best_point_idx);
|
||||
polyStart.push_back(best);
|
||||
|
||||
assert(poly.size() == 2);
|
||||
|
||||
line_bucket_grid.insert(poly[0], poly_idx);
|
||||
line_bucket_grid.insert(poly[1], poly_idx);
|
||||
line_bucket_grid.insert(poly[0], i_polygon);
|
||||
line_bucket_grid.insert(poly[1], i_polygon);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Point incoming_perpundicular_normal(0, 0);
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point prev_point = startPoint;
|
||||
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
{
|
||||
int best_line_idx = -1;
|
||||
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
{
|
||||
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
|
||||
{
|
||||
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
|
||||
continue;
|
||||
}
|
||||
|
||||
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
|
||||
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
}
|
||||
|
||||
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
|
||||
if (best == -1) /// if single-line-polygon hasn't been found yet
|
||||
{
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
{
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
continue;
|
||||
}
|
||||
assert(polygons[poly_idx].size() == 2);
|
||||
assert(polygons[i_polygon].size() == 2);
|
||||
|
||||
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
PolygonRef best_line = polygons[best_line_idx];
|
||||
assert(best_line.size() == 2);
|
||||
assert(polygons[best].size() == 2);
|
||||
|
||||
int line_start_point_idx = polyStart[best_line_idx];
|
||||
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
Point& line_start = best_line[line_start_point_idx];
|
||||
Point& line_end = best_line[line_end_point_idx];
|
||||
prev_point = line_end;
|
||||
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
|
||||
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
prev_point = polygons[best][endIdx];
|
||||
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
|
||||
|
||||
picked[best_line_idx] = true;
|
||||
polyOrder.push_back(best_line_idx);
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
int nr = polyOrder[n];
|
||||
PolygonRef poly = polygons[nr];
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i=0;i<poly.size(); i++)
|
||||
{
|
||||
float dist = vSize2f(polygons[nr][i] - prev_point);
|
||||
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
|
||||
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist + dot_score;
|
||||
}
|
||||
}
|
||||
|
||||
polyStart[nr] = best;
|
||||
assert(poly.size() == 2);
|
||||
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
|
||||
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
|
||||
{
|
||||
Point& p0 = polygons[poly_idx][0];
|
||||
Point& p1 = polygons[poly_idx][1];
|
||||
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
|
||||
{ /// check distance to first point on line (0)
|
||||
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 0;
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 0;
|
||||
}
|
||||
}
|
||||
{ /// check distance to second point on line (1)
|
||||
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 1;
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
|
||||
{
|
||||
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -81,35 +81,8 @@ public:
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
|
||||
*
|
||||
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
|
||||
*
|
||||
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
|
||||
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
|
||||
* in which case the travel move might involve combing, which makes it rather longer.
|
||||
*
|
||||
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
|
||||
* \param best[in, out] The index of current best line
|
||||
* \param best_score[in, out] The distance score for the current best line
|
||||
* \param prev_point[in] The previous point from which to find the next best line
|
||||
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
|
||||
*/
|
||||
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
|
||||
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
|
||||
|
||||
/*!
|
||||
* Get a score to modify the distance score for measuring how good two lines follow each other.
|
||||
*
|
||||
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
|
||||
*
|
||||
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
|
||||
* \param from The one end of the next line
|
||||
* \param to The other end of the next line
|
||||
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
|
||||
*
|
||||
*/
|
||||
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+89
-114
@@ -43,35 +43,21 @@ SettingContainer::SettingContainer(std::string key, std::string label)
|
||||
|
||||
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
|
||||
{
|
||||
children.emplace_back(key, label);
|
||||
children.emplace_back(key, label, nullptr);
|
||||
return &children.back();
|
||||
}
|
||||
|
||||
SettingConfig& SettingContainer::getOrCreateChild(std::string key, std::string label)
|
||||
{
|
||||
auto child_it = std::find_if(children.begin(), children.end(), [&key](SettingConfig& child) { return child.key == key; } );
|
||||
if (child_it == children.end())
|
||||
{
|
||||
children.emplace_back(key, label);
|
||||
return children.back();
|
||||
}
|
||||
else
|
||||
{
|
||||
return *child_it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SettingConfig::SettingConfig(std::string key, std::string label)
|
||||
SettingConfig::SettingConfig(std::string key, std::string label, SettingContainer* parent)
|
||||
: SettingContainer(key, label)
|
||||
, parent(parent)
|
||||
{
|
||||
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
|
||||
}
|
||||
|
||||
void SettingContainer::debugOutputAllSettings() const
|
||||
void SettingContainer::debugOutputAllSettings()
|
||||
{
|
||||
std::cerr << "\nCATEGORY: " << key << std::endl;
|
||||
for (const SettingConfig& child : children)
|
||||
std::cerr << "CATEGORY: " << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
@@ -83,7 +69,7 @@ bool SettingRegistry::settingExists(std::string key) const
|
||||
return settings.find(key) != settings.end();
|
||||
}
|
||||
|
||||
SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
|
||||
SettingConfig* SettingRegistry::getSettingConfig(std::string key)
|
||||
{
|
||||
auto it = settings.find(key);
|
||||
if (it == settings.end())
|
||||
@@ -99,39 +85,12 @@ SettingContainer* SettingRegistry::getCategory(std::string key)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SettingContainer* SettingRegistry::getCategory(std::string key) const
|
||||
{
|
||||
for (const SettingContainer& cat : categories)
|
||||
if (cat.getKey().compare(key) == 0)
|
||||
return &cat;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SettingContainer& SettingRegistry::getOrCreateCategory(std::string cat_name, const rapidjson::Value& category)
|
||||
{
|
||||
std::list<SettingContainer>::iterator category_found = std::find_if(categories.begin(), categories.end(), [&cat_name](SettingContainer& cat) { return cat.getKey().compare(cat_name) == 0; });
|
||||
if (category_found != categories.end())
|
||||
{ // category is already present; add settings to category
|
||||
return *category_found;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string label = cat_name;
|
||||
if (category.IsObject() && category.HasMember("label") && category["label"].IsString())
|
||||
{
|
||||
label = category["label"].GetString();
|
||||
}
|
||||
categories.emplace_back(cat_name, label);
|
||||
return categories.back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SettingRegistry::SettingRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
bool SettingRegistry::settingsLoaded() const
|
||||
bool SettingRegistry::settingsLoaded()
|
||||
{
|
||||
return settings.size() > 0;
|
||||
}
|
||||
@@ -176,6 +135,7 @@ int SettingRegistry::loadJSONsettings(std::string filename)
|
||||
{
|
||||
return loadJSONsettingsFromDoc(json_document, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates)
|
||||
@@ -189,53 +149,83 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
|
||||
if (json_document.HasMember("machine_extruder_trains"))
|
||||
{
|
||||
categories.emplace_back("machine_extruder_trains", "Extruder Trains Settings Objects");
|
||||
SettingContainer* category_trains = &categories.back();
|
||||
const rapidjson::Value& trains = json_document["machine_extruder_trains"];
|
||||
SettingContainer& category_trains = getOrCreateCategory("machine_extruder_trains", trains);
|
||||
|
||||
if (trains.IsObject())
|
||||
if (trains.IsArray())
|
||||
{
|
||||
for (rapidjson::Value::ConstMemberIterator train_iterator = trains.MemberBegin(); train_iterator != trains.MemberEnd(); ++train_iterator)
|
||||
if (trains.Size() > 0 && trains[0].IsObject())
|
||||
{
|
||||
SettingConfig& child = category_trains.getOrCreateChild(train_iterator->name.GetString(), std::string("Extruder ") + train_iterator->name.GetString());
|
||||
const rapidjson::Value& train = train_iterator->value;
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = train.MemberBegin(); setting_iterator != train.MemberEnd(); ++setting_iterator)
|
||||
unsigned int idx = 0;
|
||||
for (auto it = trains.Begin(); it != trains.End(); ++it)
|
||||
{
|
||||
_addSettingToContainer(&child, setting_iterator, warn_duplicates, false);
|
||||
SettingConfig* child = category_trains->addChild(std::to_string(idx), std::to_string(idx));
|
||||
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = it->MemberBegin(); setting_iterator != it->MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(child, setting_iterator, warn_duplicates, false);
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (trains.IsArray())
|
||||
else
|
||||
{
|
||||
int train_nr = 0;
|
||||
for (rapidjson::Value::ConstValueIterator train_iterator = trains.Begin(); train_iterator != trains.End(); ++train_iterator)
|
||||
{
|
||||
SettingConfig& child = category_trains.getOrCreateChild(std::to_string(train_nr), std::string("Extruder ") + std::to_string(train_nr));
|
||||
const rapidjson::Value& train = *train_iterator;
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = train.MemberBegin(); setting_iterator != train.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(&child, setting_iterator, warn_duplicates, false);
|
||||
}
|
||||
train_nr++;
|
||||
}
|
||||
logError("Error: JSON machine_extruder_trains is not an array!\n");
|
||||
}
|
||||
}
|
||||
if (json_document.HasMember("machine_settings"))
|
||||
{
|
||||
const rapidjson::Value& machine_settings = json_document["machine_settings"];
|
||||
SettingContainer& category = getOrCreateCategory("machine_settings", machine_settings);
|
||||
// _addCategory(std::string("machine_settings"), machine_settings, warn_duplicates); // TODO: make machine_settings a category with a settings field and a label field and throw away rest of the code in this code block
|
||||
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = machine_settings.MemberBegin(); setting_iterator != machine_settings.MemberEnd(); ++setting_iterator)
|
||||
categories.emplace_back("machine_settings", "Machine Settings");
|
||||
SettingContainer* category_machine_settings = &categories.back();
|
||||
|
||||
const rapidjson::Value& json_object_container = json_document["machine_settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(&category, setting_iterator, warn_duplicates);
|
||||
_addSettingToContainer(category_machine_settings, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (json_document.HasMember("categories"))
|
||||
{
|
||||
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
|
||||
{
|
||||
_addCategory(category_iterator->name.GetString(), category_iterator->value, warn_duplicates);
|
||||
if (!category_iterator->value.IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::string cat_name = category_iterator->name.GetString();
|
||||
std::list<SettingContainer>::iterator category_found = std::find_if(categories.begin(), categories.end(), [&cat_name](SettingContainer& cat) { return cat.getKey().compare(cat_name) == 0; });
|
||||
if (category_found != categories.end())
|
||||
{ // category is already present; add settings to category
|
||||
SettingContainer* category = &*category_found;
|
||||
|
||||
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(category, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
categories.emplace_back(cat_name, category_iterator->value["label"].GetString());
|
||||
SettingContainer* category = &categories.back();
|
||||
|
||||
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(category, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,11 +236,6 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
{
|
||||
std::string setting = override_iterator->name.GetString();
|
||||
SettingConfig* conf = getSettingConfig(setting);
|
||||
if (!conf) //Setting could not be found.
|
||||
{
|
||||
logWarning("Trying to override unknown setting %s.\n", setting.c_str());
|
||||
continue;
|
||||
}
|
||||
_loadSettingValues(conf, override_iterator, false);
|
||||
}
|
||||
}
|
||||
@@ -258,29 +243,27 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SettingRegistry::_addCategory(std::string cat_name, const rapidjson::Value& fields, bool warn_duplicates)
|
||||
{
|
||||
if (!fields.IsObject())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!fields.HasMember("settings") || !fields["settings"].IsObject())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SettingContainer& category = getOrCreateCategory(cat_name, fields);
|
||||
const rapidjson::Value& json_object_container = fields["settings"];
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
_addSettingToContainer(&category, setting_iterator, warn_duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, const rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
|
||||
|
||||
if (data.HasMember("type") && data["type"].IsString() &&
|
||||
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
|
||||
{
|
||||
logWarning("Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
|
||||
/// When this setting has children, add those children to the parent 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(parent, setting_iterator, warn_duplicates, add_to_settings);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label;
|
||||
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
|
||||
{
|
||||
@@ -292,12 +275,12 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, const rap
|
||||
}
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig& config = parent->getOrCreateChild(json_object_it->name.GetString(), label);
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
|
||||
_loadSettingValues(&config, json_object_it, warn_duplicates, add_to_settings);
|
||||
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
|
||||
}
|
||||
|
||||
void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
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.
|
||||
@@ -328,15 +311,7 @@ void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson:
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
if (data.HasMember("type") && data["type"].IsString() &&
|
||||
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
|
||||
{
|
||||
logWarning("WARNING: Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning("WARNING: Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
}
|
||||
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())
|
||||
|
||||
+16
-53
@@ -15,7 +15,6 @@ namespace cura
|
||||
|
||||
// Forward declaration
|
||||
class SettingConfig;
|
||||
class SettingRegistry;
|
||||
|
||||
/*!
|
||||
* Setting category.
|
||||
@@ -24,11 +23,10 @@ class SettingRegistry;
|
||||
class SettingContainer
|
||||
{
|
||||
friend class SettingConfig;
|
||||
friend class SettingRegistry;
|
||||
private:
|
||||
std::string key;
|
||||
std::string label;
|
||||
std::list<SettingConfig> children; // must be a list cause the pointers to individual children are mapped to in SettingRegistry::settings.
|
||||
std::list<SettingConfig> children;
|
||||
public:
|
||||
std::string getKey() const { return key; }
|
||||
std::string getLabel() const { return label; }
|
||||
@@ -55,18 +53,8 @@ public:
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Get the (direct) child with key \p key, or create one with key \p key and label \p label as well.
|
||||
*
|
||||
* \param key the key
|
||||
* \param label the label for creating a new child
|
||||
* \return The existing or newly created child setting.
|
||||
*/
|
||||
SettingConfig& getOrCreateChild(std::string key, std::string label);
|
||||
public:
|
||||
void debugOutputAllSettings() const;
|
||||
|
||||
void debugOutputAllSettings();
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -80,8 +68,9 @@ private:
|
||||
std::string type;
|
||||
std::string default_value;
|
||||
std::string unit;
|
||||
SettingContainer* parent;
|
||||
public:
|
||||
SettingConfig(std::string key, std::string label);
|
||||
SettingConfig(std::string key, std::string label, SettingContainer* parent);
|
||||
|
||||
/*!
|
||||
* Get the SettingConfig::children.
|
||||
@@ -127,10 +116,10 @@ public:
|
||||
return unit;
|
||||
}
|
||||
|
||||
void debugOutputAllSettings() const
|
||||
void debugOutputAllSettings()
|
||||
{
|
||||
std::cerr << key << "(" << default_value << ")" <<std::endl;
|
||||
for (const SettingConfig& child : children)
|
||||
std::cerr << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
@@ -142,7 +131,6 @@ public:
|
||||
* There is a single global setting registry.
|
||||
* This registry contains all known setting keys.
|
||||
* The registry also contains the settings categories to build up the setting hiarcy from the json file.
|
||||
* Also the default values are stored and retrieved in case a given setting doesn't get a value from the command line or the frontend.
|
||||
*/
|
||||
class SettingRegistry : NoCopy
|
||||
{
|
||||
@@ -164,7 +152,7 @@ public:
|
||||
static SettingRegistry* getInstance() { return &instance; }
|
||||
|
||||
bool settingExists(std::string key) const;
|
||||
SettingConfig* getSettingConfig(std::string key) const;
|
||||
SettingConfig* getSettingConfig(std::string key);
|
||||
|
||||
/*!
|
||||
* Return the first category with the given key as name, or a null pointer.
|
||||
@@ -173,25 +161,8 @@ public:
|
||||
* \return The first category in the list having the \p key
|
||||
*/
|
||||
SettingContainer* getCategory(std::string key);
|
||||
|
||||
/*!
|
||||
* Return the first category with the given key as name, or a null pointer. const style
|
||||
*
|
||||
* \param key the key as it is in the JSON file
|
||||
* \return The first category in the list having the \p key
|
||||
*/
|
||||
const SettingContainer* getCategory(std::string key) const;
|
||||
private:
|
||||
/*!
|
||||
* Return the first category with the given key as name, or a new one.
|
||||
*
|
||||
* \param cat_name the key as it is in the JSON file
|
||||
* \param category the JSON Value associated with the key
|
||||
* \return The first category in the list having the \p key (or a new one)
|
||||
*/
|
||||
SettingContainer& getOrCreateCategory(std::string cat_name, const rapidjson::Value& category);
|
||||
public:
|
||||
bool settingsLoaded() const;
|
||||
|
||||
bool settingsLoaded();
|
||||
/*!
|
||||
* Load settings from a json file and all the parents it inherits from.
|
||||
*
|
||||
@@ -201,9 +172,9 @@ public:
|
||||
* \return an error code or zero of succeeded
|
||||
*/
|
||||
int loadJSONsettings(std::string filename);
|
||||
void debugOutputAllSettings() const
|
||||
void debugOutputAllSettings()
|
||||
{
|
||||
for (const SettingContainer& cat : categories)
|
||||
for (SettingContainer& cat : categories)
|
||||
{
|
||||
cat.debugOutputAllSettings();
|
||||
}
|
||||
@@ -245,17 +216,9 @@ private:
|
||||
/*!
|
||||
* \param warn_duplicates whether to warn for duplicate definitions
|
||||
*/
|
||||
void _addSettingToContainer(SettingContainer* parent, const rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
|
||||
/*!
|
||||
* Adds a category with a given name to the registry.
|
||||
* \param cat_name the key of the category as it is in the JSON file
|
||||
* \param fields The members of the category
|
||||
* \param warn_duplicates whether to warn for duplicate definitions
|
||||
*/
|
||||
void _addCategory(std::string cat_name, const rapidjson::Value& fields, bool warn_duplicates);
|
||||
|
||||
void _loadSettingValues(SettingConfig* config, const rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
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
|
||||
|
||||
+27
-28
@@ -72,28 +72,27 @@ void SettingsBase::setSetting(std::string key, std::string value)
|
||||
}
|
||||
}
|
||||
|
||||
std::string SettingsBase::getSettingString(std::string key) const
|
||||
std::string SettingsBase::getSettingString(std::string key)
|
||||
{
|
||||
if (setting_values.find(key) != setting_values.end())
|
||||
{
|
||||
return setting_values.at(key);
|
||||
return setting_values[key];
|
||||
}
|
||||
if (parent)
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
|
||||
SettingsBase& nonConstThis = const_cast<SettingsBase&>(*this);
|
||||
if (SettingRegistry::getInstance()->settingExists(key))
|
||||
{
|
||||
nonConstThis.setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
|
||||
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
nonConstThis.setting_values[key] = "";
|
||||
setting_values[key] = "";
|
||||
cura::logError("Unregistered setting %s\n", key.c_str());
|
||||
}
|
||||
return setting_values.at(key);
|
||||
return setting_values[key];
|
||||
}
|
||||
|
||||
void SettingsMessenger::setSetting(std::string key, std::string value)
|
||||
@@ -101,7 +100,7 @@ void SettingsMessenger::setSetting(std::string key, std::string value)
|
||||
parent->setSetting(key, value);
|
||||
}
|
||||
|
||||
std::string SettingsMessenger::getSettingString(std::string key) const
|
||||
std::string SettingsMessenger::getSettingString(std::string key)
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
@@ -110,15 +109,15 @@ std::string SettingsMessenger::getSettingString(std::string key) const
|
||||
void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
{
|
||||
const SettingContainer* machine_extruder_trains = SettingRegistry::getInstance()->getCategory(std::string("machine_extruder_trains"));
|
||||
|
||||
|
||||
if (!machine_extruder_trains)
|
||||
{
|
||||
// no machine_extruder_trains setting present; just use defaults for each train..
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const SettingConfig* train = machine_extruder_trains->getChild(extruder_nr);
|
||||
|
||||
|
||||
if (!train)
|
||||
{
|
||||
// not enough machine_extruder_trains settings present; just use defaults for this train..
|
||||
@@ -134,31 +133,31 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
}
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingAsIndex(std::string key) const
|
||||
int SettingsBaseVirtual::getSettingAsIndex(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingAsCount(std::string key) const
|
||||
int SettingsBaseVirtual::getSettingAsCount(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
|
||||
int SettingsBaseVirtual::getSettingInMicrons(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) * 1000.0;
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) / 180.0 * M_PI;
|
||||
}
|
||||
|
||||
bool SettingsBaseVirtual::getSettingBoolean(std::string key) const
|
||||
bool SettingsBaseVirtual::getSettingBoolean(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "on")
|
||||
@@ -171,37 +170,37 @@ bool SettingsBaseVirtual::getSettingBoolean(std::string key) const
|
||||
return num != 0;
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str());
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(1.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInPercentage(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInPercentage(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
|
||||
double SettingsBaseVirtual::getSettingInSeconds(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) const
|
||||
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
|
||||
{
|
||||
FlowTempGraph ret;
|
||||
const char* c_str = getSettingString(key).c_str();
|
||||
@@ -259,7 +258,7 @@ FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) co
|
||||
}
|
||||
|
||||
|
||||
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
|
||||
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "RepRap")
|
||||
@@ -277,7 +276,7 @@ EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
|
||||
return EGCodeFlavor::REPRAP;
|
||||
}
|
||||
|
||||
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
|
||||
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "lines")
|
||||
@@ -293,7 +292,7 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
|
||||
return EFillMethod::NONE;
|
||||
}
|
||||
|
||||
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key) const
|
||||
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "brim")
|
||||
@@ -303,7 +302,7 @@ EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string
|
||||
return EPlatformAdhesion::SKIRT;
|
||||
}
|
||||
|
||||
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key) const
|
||||
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "everywhere")
|
||||
@@ -313,7 +312,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key) const
|
||||
return ESupportType::NONE;
|
||||
}
|
||||
|
||||
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key) const
|
||||
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "random")
|
||||
@@ -325,7 +324,7 @@ EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key) const
|
||||
return EZSeamType::SHORTEST;
|
||||
}
|
||||
|
||||
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
|
||||
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "normal")
|
||||
@@ -337,7 +336,7 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
|
||||
return ESurfaceMode::NORMAL;
|
||||
}
|
||||
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key) const
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "nowhere")
|
||||
|
||||
+24
-24
@@ -155,7 +155,7 @@ class SettingsBaseVirtual
|
||||
protected:
|
||||
SettingsBaseVirtual* parent;
|
||||
public:
|
||||
virtual std::string getSettingString(std::string key) const = 0;
|
||||
virtual std::string getSettingString(std::string key) = 0;
|
||||
|
||||
virtual void setSetting(std::string key, std::string value) = 0;
|
||||
|
||||
@@ -167,29 +167,29 @@ public:
|
||||
void setParent(SettingsBaseVirtual* parent) { this->parent = parent; }
|
||||
SettingsBaseVirtual* getParent() { return parent; }
|
||||
|
||||
int getSettingAsIndex(std::string key) const;
|
||||
int getSettingAsCount(std::string key) const;
|
||||
int getSettingAsIndex(std::string key);
|
||||
int getSettingAsCount(std::string key);
|
||||
|
||||
double getSettingInAngleRadians(std::string key) const;
|
||||
int getSettingInMicrons(std::string key) const;
|
||||
bool getSettingBoolean(std::string key) const;
|
||||
double getSettingInDegreeCelsius(std::string key) const;
|
||||
double getSettingInMillimetersPerSecond(std::string key) const;
|
||||
double getSettingInCubicMillimeters(std::string key) const;
|
||||
double getSettingInPercentage(std::string key) const;
|
||||
double getSettingInSeconds(std::string key) const;
|
||||
double getSettingInAngleRadians(std::string key);
|
||||
int getSettingInMicrons(std::string key);
|
||||
bool getSettingBoolean(std::string key);
|
||||
double getSettingInDegreeCelsius(std::string key);
|
||||
double getSettingInMillimetersPerSecond(std::string key);
|
||||
double getSettingInCubicMillimeters(std::string key);
|
||||
double getSettingInPercentage(std::string key);
|
||||
double getSettingInSeconds(std::string key);
|
||||
|
||||
FlowTempGraph getSettingAsFlowTempGraph(std::string key) const;
|
||||
FlowTempGraph getSettingAsFlowTempGraph(std::string key);
|
||||
|
||||
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key) const;
|
||||
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key);
|
||||
|
||||
EGCodeFlavor getSettingAsGCodeFlavor(std::string key) const;
|
||||
EFillMethod getSettingAsFillMethod(std::string key) const;
|
||||
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key) const;
|
||||
ESupportType getSettingAsSupportType(std::string key) const;
|
||||
EZSeamType getSettingAsZSeamType(std::string key) const;
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key) const;
|
||||
EGCodeFlavor getSettingAsGCodeFlavor(std::string key);
|
||||
EFillMethod getSettingAsFillMethod(std::string key);
|
||||
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key);
|
||||
ESupportType getSettingAsSupportType(std::string key);
|
||||
EZSeamType getSettingAsZSeamType(std::string key);
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key);
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -218,9 +218,9 @@ public:
|
||||
void setExtruderTrainDefaults(unsigned int extruder_nr);
|
||||
|
||||
void setSetting(std::string key, std::string value);
|
||||
std::string getSettingString(std::string key) const; //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
|
||||
std::string getSettingString(std::string key); //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
|
||||
|
||||
std::string getAllLocalSettingsString() const
|
||||
std::string getAllLocalSettingsString()
|
||||
{
|
||||
std::stringstream sstream;
|
||||
for (auto pair : setting_values)
|
||||
@@ -233,7 +233,7 @@ public:
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
void debugOutputAllLocalSettings() const
|
||||
void debugOutputAllLocalSettings()
|
||||
{
|
||||
for (auto pair : setting_values)
|
||||
std::cerr << pair.first << " : " << pair.second << std::endl;
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
SettingsMessenger(SettingsBaseVirtual* parent); //!< construct a SettingsMessenger with a parent settings object
|
||||
|
||||
void setSetting(std::string key, std::string value); //!< Set a setting of the parent SettingsBase to a given value
|
||||
std::string getSettingString(std::string key) const; //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
|
||||
std::string getSettingString(std::string key); //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
|
||||
};
|
||||
|
||||
|
||||
|
||||
+6
-7
@@ -67,20 +67,20 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer_nr >= downSkinCount && downSkinCount > 0)
|
||||
if (layer_nr > 0 && downSkinCount > 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
|
||||
for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
|
||||
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
|
||||
}
|
||||
downskin = downskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - downSkinCount && upSkinCount > 0)
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
|
||||
{
|
||||
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
|
||||
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
|
||||
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
|
||||
{
|
||||
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
|
||||
}
|
||||
@@ -134,7 +134,7 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
|
||||
}
|
||||
}
|
||||
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
@@ -142,10 +142,9 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_e
|
||||
{
|
||||
if (int(part.insets.size()) < wall_line_count)
|
||||
{
|
||||
part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
|
||||
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
|
||||
}
|
||||
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
|
||||
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
|
||||
|
||||
for(SliceLayerPart& part2 : layer.parts)
|
||||
{
|
||||
|
||||
+2
-4
@@ -65,15 +65,13 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
|
||||
*
|
||||
* The walls should already be generated.
|
||||
*
|
||||
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the infill
|
||||
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
|
||||
* \param innermost_wall_extrusion_width width of the innermost wall lines
|
||||
* \param extrusionWidth width of the wall lines
|
||||
* \param infill_skin_overlap overlap distance between infill and skin
|
||||
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
|
||||
*/
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
|
||||
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count);
|
||||
|
||||
/*!
|
||||
* \brief Combines the infill of multiple layers for a specified mesh.
|
||||
|
||||
@@ -59,13 +59,13 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
|
||||
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
|
||||
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
|
||||
retraction_config_per_extruder(initializeRetractionConfigs()),
|
||||
travel_config(&retraction_config, PrintFeatureType::MoveCombing),
|
||||
travel_config(&retraction_config, "MOVE"),
|
||||
skirt_config(initializeSkirtConfigs()),
|
||||
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], PrintFeatureType::Support),
|
||||
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
|
||||
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
|
||||
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], "SUPPORT"),
|
||||
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN"),
|
||||
max_object_height_second_to_last_extruder(-1)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -24,6 +24,19 @@ public:
|
||||
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
|
||||
/*!
|
||||
* A ReinforcementWall is like an insulated wall behind the outer walls.
|
||||
* It consists of an area with (generally more dense) infill and perimeters on the inside.
|
||||
* On the outside it has the outer walls, or the inner walls of another ReinforcementWall.
|
||||
*/
|
||||
class ReinforcementWall
|
||||
{
|
||||
public:
|
||||
Polygons wall_reinforcement_area; //!< The infill of the reinforced wall
|
||||
std::vector<Polygons> wall_reinforcement_axtra_walls; //!< The extra walls on the inside of the reinforcement infill
|
||||
};
|
||||
|
||||
/*!
|
||||
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
|
||||
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
|
||||
@@ -37,6 +50,7 @@ public:
|
||||
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
|
||||
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
|
||||
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
|
||||
std::vector<ReinforcementWall> reinforcement_walls; //!< The reinforcement walls for this part. Order: from outter to inner reinforcement wall.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
|
||||
@@ -117,13 +131,14 @@ public:
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
std::vector<GCodePathConfig> infill_config;
|
||||
GCodePathConfig wall_reinforcement_config;
|
||||
|
||||
SliceMeshStorage(SettingsBaseVirtual* settings)
|
||||
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
|
||||
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
|
||||
{
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
|
||||
infill_config.emplace_back(&retraction_config, "FILL");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,7 +188,7 @@ public:
|
||||
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
|
||||
{
|
||||
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
|
||||
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
|
||||
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,64 +1,13 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SlicerLayer.h"
|
||||
#include "../TextureProcessor.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
GapCloserResult SlicerLayer::findPolygonGapCloser(Point ip0, Point ip1)
|
||||
{
|
||||
GapCloserResult ret;
|
||||
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
|
||||
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
|
||||
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
|
||||
{
|
||||
ret.len = -1;
|
||||
return ret;
|
||||
}
|
||||
ret.polygonIdx = c1.polygonIdx;
|
||||
ret.pointIdxA = c1.pointIdx;
|
||||
ret.pointIdxB = c2.pointIdx;
|
||||
ret.AtoB = true;
|
||||
|
||||
if (ret.pointIdxA == ret.pointIdxB)
|
||||
{
|
||||
//Connection points are on the same line segment.
|
||||
ret.len = vSize(ip0 - ip1);
|
||||
}else{
|
||||
//Find out if we have should go from A to B or the other way around.
|
||||
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
|
||||
int64_t lenA = vSize(p0 - ip0);
|
||||
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
|
||||
{
|
||||
Point p1 = polygonList[ret.polygonIdx][i];
|
||||
lenA += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
lenA += vSize(p0 - ip1);
|
||||
|
||||
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
|
||||
int64_t lenB = vSize(p0 - ip1);
|
||||
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
|
||||
{
|
||||
Point p1 = polygonList[ret.polygonIdx][i];
|
||||
lenB += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
lenB += vSize(p0 - ip0);
|
||||
|
||||
if (lenA < lenB)
|
||||
{
|
||||
ret.AtoB = true;
|
||||
ret.len = lenA;
|
||||
}else{
|
||||
ret.AtoB = false;
|
||||
ret.len = lenB;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#include "slicer.h"
|
||||
#include "debug.h" // TODO remove
|
||||
|
||||
namespace cura {
|
||||
|
||||
void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
|
||||
{
|
||||
@@ -355,9 +304,6 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
|
||||
}
|
||||
}
|
||||
|
||||
// TextureProcessor::processBumpMap(mesh, *this);
|
||||
TextureProcessor::processDualColorTexture(mesh, *this);
|
||||
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
polygonList.simplify();
|
||||
|
||||
@@ -370,40 +316,68 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
|
||||
}
|
||||
}
|
||||
|
||||
ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input)
|
||||
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
|
||||
{
|
||||
ClosePolygonResult ret;
|
||||
for(unsigned int n=0; n<polygonList.size(); n++)
|
||||
assert(layer_count > 0);
|
||||
|
||||
layers.resize(layer_count);
|
||||
|
||||
for(int32_t layer_nr = 0; layer_nr < layer_count; layer_nr++)
|
||||
{
|
||||
Point p0 = polygonList[n][polygonList[n].size()-1];
|
||||
for(unsigned int i=0; i<polygonList[n].size(); i++)
|
||||
layers[layer_nr].z = initial + thickness * layer_nr;
|
||||
}
|
||||
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < mesh->faces.size(); mesh_idx++)
|
||||
{
|
||||
MeshFace& face = mesh->faces[mesh_idx];
|
||||
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
|
||||
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
|
||||
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ) minZ = p1.z;
|
||||
if (p2.z < minZ) minZ = p2.z;
|
||||
if (p1.z > maxZ) maxZ = p1.z;
|
||||
if (p2.z > maxZ) maxZ = p2.z;
|
||||
int32_t layer_max = (maxZ - initial) / thickness;
|
||||
for(int32_t layer_nr = (minZ - initial) / thickness; layer_nr <= layer_max; layer_nr++)
|
||||
{
|
||||
Point p1 = polygonList[n][i];
|
||||
int32_t z = layer_nr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layer_nr < 0) continue;
|
||||
|
||||
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
|
||||
Point pDiff = p1 - p0;
|
||||
int64_t lineLength = vSize(pDiff);
|
||||
if (lineLength > 1)
|
||||
SlicerSegment s;
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
s = project2D(p0, p2, p1, z);
|
||||
else if (p0.z > z && p1.z < z && p2.z < z)
|
||||
s = project2D(p0, p1, p2, z);
|
||||
|
||||
else if (p1.z < z && p0.z >= z && p2.z >= z)
|
||||
s = project2D(p1, p0, p2, z);
|
||||
else if (p1.z > z && p0.z < z && p2.z < z)
|
||||
s = project2D(p1, p2, p0, z);
|
||||
|
||||
else if (p2.z < z && p1.z >= z && p0.z >= z)
|
||||
s = project2D(p2, p1, p0, z);
|
||||
else if (p2.z > z && p1.z < z && p0.z < z)
|
||||
s = project2D(p2, p0, p1, z);
|
||||
else
|
||||
{
|
||||
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
|
||||
if (distOnLine >= 0 && distOnLine <= lineLength)
|
||||
{
|
||||
Point q = p0 + pDiff * distOnLine / lineLength;
|
||||
if (shorterThen(q - input, 100))
|
||||
{
|
||||
ret.intersectionPoint = q;
|
||||
ret.polygonIdx = n;
|
||||
ret.pointIdx = i;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
p0 = p1;
|
||||
layers[layer_nr].face_idx_to_segment_index.insert(std::make_pair(mesh_idx, layers[layer_nr].segmentList.size()));
|
||||
s.faceIndex = mesh_idx;
|
||||
s.addedToPolygon = false;
|
||||
layers[layer_nr].segmentList.push_back(s);
|
||||
}
|
||||
}
|
||||
ret.polygonIdx = -1;
|
||||
return ret;
|
||||
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
|
||||
{
|
||||
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
}//namespace cura
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_H
|
||||
#define SLICER_H
|
||||
|
||||
#include "mesh.h"
|
||||
#include "utils/polygon.h"
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
The result of the Slicer is a list of polygons without any order or structure.
|
||||
*/
|
||||
namespace cura {
|
||||
|
||||
class SlicerSegment
|
||||
{
|
||||
public:
|
||||
Point start, end;
|
||||
int faceIndex;
|
||||
bool addedToPolygon;
|
||||
};
|
||||
|
||||
class ClosePolygonResult
|
||||
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
|
||||
//The line on which the point lays is between pointIdx-1 and pointIdx
|
||||
public:
|
||||
Point intersectionPoint;
|
||||
int polygonIdx;
|
||||
unsigned int pointIdx;
|
||||
};
|
||||
class GapCloserResult
|
||||
{
|
||||
public:
|
||||
int64_t len;
|
||||
int polygonIdx;
|
||||
unsigned int pointIdxA;
|
||||
unsigned int pointIdxB;
|
||||
bool AtoB;
|
||||
};
|
||||
|
||||
class SlicerLayer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerSegment> segmentList;
|
||||
std::unordered_map<int, int> face_idx_to_segment_index; // topology
|
||||
|
||||
int z;
|
||||
Polygons polygonList;
|
||||
Polygons openPolylines;
|
||||
|
||||
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
private:
|
||||
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
|
||||
{
|
||||
GapCloserResult ret;
|
||||
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
|
||||
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
|
||||
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
|
||||
{
|
||||
ret.len = -1;
|
||||
return ret;
|
||||
}
|
||||
ret.polygonIdx = c1.polygonIdx;
|
||||
ret.pointIdxA = c1.pointIdx;
|
||||
ret.pointIdxB = c2.pointIdx;
|
||||
ret.AtoB = true;
|
||||
|
||||
if (ret.pointIdxA == ret.pointIdxB)
|
||||
{
|
||||
//Connection points are on the same line segment.
|
||||
ret.len = vSize(ip0 - ip1);
|
||||
}else{
|
||||
//Find out if we have should go from A to B or the other way around.
|
||||
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
|
||||
int64_t lenA = vSize(p0 - ip0);
|
||||
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
|
||||
{
|
||||
Point p1 = polygonList[ret.polygonIdx][i];
|
||||
lenA += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
lenA += vSize(p0 - ip1);
|
||||
|
||||
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
|
||||
int64_t lenB = vSize(p0 - ip1);
|
||||
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
|
||||
{
|
||||
Point p1 = polygonList[ret.polygonIdx][i];
|
||||
lenB += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
lenB += vSize(p0 - ip0);
|
||||
|
||||
if (lenA < lenB)
|
||||
{
|
||||
ret.AtoB = true;
|
||||
ret.len = lenA;
|
||||
}else{
|
||||
ret.AtoB = false;
|
||||
ret.len = lenB;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ClosePolygonResult findPolygonPointClosestTo(Point input)
|
||||
{
|
||||
ClosePolygonResult ret;
|
||||
for(unsigned int n=0; n<polygonList.size(); n++)
|
||||
{
|
||||
Point p0 = polygonList[n][polygonList[n].size()-1];
|
||||
for(unsigned int i=0; i<polygonList[n].size(); i++)
|
||||
{
|
||||
Point p1 = polygonList[n][i];
|
||||
|
||||
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
|
||||
Point pDiff = p1 - p0;
|
||||
int64_t lineLength = vSize(pDiff);
|
||||
if (lineLength > 1)
|
||||
{
|
||||
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
|
||||
if (distOnLine >= 0 && distOnLine <= lineLength)
|
||||
{
|
||||
Point q = p0 + pDiff * distOnLine / lineLength;
|
||||
if (shorterThen(q - input, 100))
|
||||
{
|
||||
ret.intersectionPoint = q;
|
||||
ret.polygonIdx = n;
|
||||
ret.pointIdx = i;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
ret.polygonIdx = -1;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
|
||||
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
|
||||
{
|
||||
SlicerSegment seg;
|
||||
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
return seg;
|
||||
}
|
||||
|
||||
void dumpSegmentsToHTML(const char* filename);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_H
|
||||
@@ -1,21 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_CLOSE_POLYGON_RESULT_H
|
||||
#define SLICER_CLOSE_POLYGON_RESULT_H
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ClosePolygonResult
|
||||
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
|
||||
//The line on which the point lays is between pointIdx-1 and pointIdx
|
||||
public:
|
||||
Point intersectionPoint;
|
||||
int polygonIdx;
|
||||
unsigned int pointIdx;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SLICER_CLOSE_POLYGON_RESULT_H
|
||||
@@ -1,22 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_GAP_CLOSER_RESULT_H
|
||||
#define SLICER_GAP_CLOSER_RESULT_H
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class GapCloserResult
|
||||
{
|
||||
public:
|
||||
int64_t len;
|
||||
int polygonIdx;
|
||||
unsigned int pointIdxA;
|
||||
unsigned int pointIdxB;
|
||||
bool AtoB;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SLICER_GAP_CLOSER_RESULT_H
|
||||
@@ -1,133 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../utils/gettime.h"
|
||||
#include "../utils/logoutput.h"
|
||||
#include "../MatCoord.h"
|
||||
|
||||
#include "Slicer.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
SlicerSegment Slicer::project2D(unsigned int face_idx, Point3 p[3], unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, int32_t layer_nr)
|
||||
{
|
||||
Point3& p0 = p[idx_shared];
|
||||
Point3& p1 = p[idx_first];
|
||||
Point3& p2 = p[idx_second];
|
||||
SlicerSegment seg;
|
||||
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
MatSegment mat_segment;
|
||||
bool got_texture_coords = mesh->registerFaceSlice(face_idx, idx_shared, idx_first, idx_second, z, seg.start, seg.end, mat_segment);
|
||||
if (got_texture_coords)
|
||||
{
|
||||
SlicerLayer& layer = layers[layer_nr];
|
||||
layer.segment_to_material_segment.emplace(seg, mat_segment);
|
||||
}
|
||||
return seg;
|
||||
}
|
||||
|
||||
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
|
||||
: mesh(mesh)
|
||||
, layer_height_0(initial)
|
||||
, layer_height(thickness)
|
||||
{
|
||||
assert(layer_count > 0);
|
||||
|
||||
layers.resize(layer_count);
|
||||
|
||||
for(int32_t layer_nr = 0; layer_nr < layer_count; layer_nr++)
|
||||
{
|
||||
layers[layer_nr].z = initial + thickness * layer_nr;
|
||||
layers[layer_nr].layer_nr = layer_nr;
|
||||
}
|
||||
for (unsigned int face_idx = 0; face_idx < mesh->faces.size(); face_idx++)
|
||||
{
|
||||
MeshFace& face = mesh->faces[face_idx];
|
||||
Point3 p[3] =
|
||||
{ mesh->vertices[face.vertex_index[0]].p
|
||||
, mesh->vertices[face.vertex_index[1]].p
|
||||
, mesh->vertices[face.vertex_index[2]].p };
|
||||
Point3& p0 = p[0];
|
||||
Point3& p1 = p[1];
|
||||
Point3& p2 = p[2];
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ)
|
||||
{
|
||||
minZ = p1.z;
|
||||
}
|
||||
if (p2.z < minZ)
|
||||
{
|
||||
minZ = p2.z;
|
||||
}
|
||||
if (p1.z > maxZ)
|
||||
{
|
||||
maxZ = p1.z;
|
||||
}
|
||||
if (p2.z > maxZ)
|
||||
{
|
||||
maxZ = p2.z;
|
||||
}
|
||||
int32_t z = 0;
|
||||
for (int32_t layer_nr = (minZ - initial + thickness - 1) / thickness; z <= maxZ; layer_nr++) // + thickness - 1 to get the first layer above or at minZ
|
||||
{
|
||||
SlicerSegment s;
|
||||
|
||||
z = layer_nr * layer_height + layer_height_0;
|
||||
if (layer_nr < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// below code checks the position of the points w.r.t. the layer z
|
||||
// also the direction of the resulting sliced line is determined
|
||||
// p0 is odd one out
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
{
|
||||
s = project2D(face_idx, p, 0, 2, 1, z, layer_nr);
|
||||
}
|
||||
else if (p0.z > z && p1.z < z && p2.z < z)
|
||||
{
|
||||
s = project2D(face_idx, p, 0, 1, 2, z, layer_nr);
|
||||
}
|
||||
// p1 is odd one out
|
||||
else if (p1.z < z && p0.z >= z && p2.z >= z)
|
||||
{
|
||||
s = project2D(face_idx, p, 1, 0, 2, z, layer_nr);
|
||||
}
|
||||
else if (p1.z > z && p0.z < z && p2.z < z)
|
||||
{
|
||||
s = project2D(face_idx, p, 1, 2, 0, z, layer_nr);
|
||||
}
|
||||
// p2 is odd one out
|
||||
else if (p2.z < z && p1.z >= z && p0.z >= z)
|
||||
{
|
||||
s = project2D(face_idx, p, 2, 1, 0, z, layer_nr);
|
||||
}
|
||||
else if (p2.z > z && p1.z < z && p0.z < z)
|
||||
{
|
||||
s = project2D(face_idx, p, 2, 0, 1, z, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
layers[layer_nr].face_idx_to_segment_index.insert(std::make_pair(face_idx, layers[layer_nr].segmentList.size()));
|
||||
s.faceIndex = face_idx;
|
||||
s.addedToPolygon = false;
|
||||
layers[layer_nr].segmentList.push_back(s);
|
||||
}
|
||||
}
|
||||
for (unsigned int layer_nr = 0; layer_nr < layers.size(); layer_nr++)
|
||||
{
|
||||
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -1,47 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_H
|
||||
#define SLICER_SLICER_H
|
||||
|
||||
#include "../mesh.h"
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
#include "SlicerSegment.h"
|
||||
#include "ClosePolygonResult.h"
|
||||
#include "SlicerLayer.h"
|
||||
|
||||
#include "../MatSegment.h"
|
||||
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
The result of the Slicer is a list of polygons without any order or structure.
|
||||
*/
|
||||
namespace cura {
|
||||
|
||||
class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
|
||||
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
void dumpSegmentsToHTML(const char* filename);
|
||||
|
||||
protected:
|
||||
Mesh* mesh;
|
||||
|
||||
int layer_height_0;
|
||||
int layer_height;
|
||||
|
||||
/*!
|
||||
* Create a SlicerSegment along the lines going through p0p1 (Start) and p0p2 (End)
|
||||
*
|
||||
* \warning \p p0 may not have the same z as either \p p1 or \p p2
|
||||
*
|
||||
* \param p The face vertice locations in the order the vertices are given in the face
|
||||
*/
|
||||
SlicerSegment project2D(unsigned int face_idx, Point3 p[3], unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, int32_t layer_nr);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_SLICER_H
|
||||
@@ -1,45 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_LAYER_H
|
||||
#define SLICER_SLICER_LAYER_H
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../mesh.h"
|
||||
#include "../utils/intpoint.h"
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
#include "SlicerSegment.h"
|
||||
#include "GapCloserResult.h"
|
||||
#include "ClosePolygonResult.h"
|
||||
|
||||
#include "../MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class SlicerLayer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerSegment> segmentList;
|
||||
std::unordered_map<int, int> face_idx_to_segment_index; // topology
|
||||
|
||||
int z;
|
||||
int layer_nr;
|
||||
Polygons polygonList;
|
||||
Polygons openPolylines;
|
||||
|
||||
std::unordered_map<SlicerSegment, MatSegment> segment_to_material_segment;
|
||||
|
||||
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
private:
|
||||
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1);
|
||||
|
||||
ClosePolygonResult findPolygonPointClosestTo(Point input);
|
||||
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SLICER_SLICER_LAYER_H
|
||||
@@ -1,52 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_SEGMENT_H
|
||||
#define SLICER_SLICER_SEGMENT_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class SlicerSegment
|
||||
{
|
||||
public:
|
||||
Point start, end;
|
||||
int faceIndex;
|
||||
bool addedToPolygon;
|
||||
|
||||
SlicerSegment() //!< non-initializing constructor
|
||||
{}
|
||||
SlicerSegment(Point start, Point end) //!< partially initializing constructor
|
||||
: start(start)
|
||||
, end(end)
|
||||
{}
|
||||
/*!
|
||||
* equivalence testing irrespective of start/end order
|
||||
*/
|
||||
bool operator==(const SlicerSegment& b) const
|
||||
{
|
||||
return (start == b.start && end == b.end) || (start == b.end && end == b.start);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
namespace std
|
||||
{
|
||||
/*!
|
||||
* hash function irrespective of start/end order
|
||||
*/
|
||||
template<> struct hash<cura::SlicerSegment>
|
||||
{
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(cura::SlicerSegment const& s) const
|
||||
{
|
||||
return std::hash<cura::Point>()(cura::operator+(s.start, s.end));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
|
||||
#endif // SLICER_SLICER_SEGMENT_H
|
||||
+16
-37
@@ -35,7 +35,7 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
|
||||
return joined;
|
||||
}
|
||||
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
|
||||
{
|
||||
// initialization of supportAreasPerLayer
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
@@ -46,11 +46,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
std::vector<Polygons> supportAreas;
|
||||
supportAreas.resize(layer_count, Polygons());
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
|
||||
|
||||
if (mesh.getSettingBoolean("support_roof_enable"))
|
||||
{
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
|
||||
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -80,7 +80,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
*
|
||||
* for support buildplate only: purge all support not connected to buildplate
|
||||
*/
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
|
||||
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
|
||||
@@ -94,6 +94,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
|
||||
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
|
||||
int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
|
||||
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
|
||||
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
|
||||
int join_distance = mesh.getSettingInMicrons("support_join_distance");
|
||||
@@ -113,23 +114,21 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
int layerThickness = mesh.getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
|
||||
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
|
||||
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
|
||||
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
|
||||
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
|
||||
|
||||
if (conical_support_angle == 0)
|
||||
{
|
||||
conical_support = false;
|
||||
}
|
||||
|
||||
// derived settings:
|
||||
|
||||
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
|
||||
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
|
||||
|
||||
|
||||
int supportLayerThickness = layerThickness;
|
||||
|
||||
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
|
||||
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
|
||||
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
|
||||
|
||||
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
|
||||
@@ -137,12 +136,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
int64_t conical_support_offset;
|
||||
if (conical_support_angle > 0)
|
||||
{ // outward ==> wider base than overhang
|
||||
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{
|
||||
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
else
|
||||
{ // inward ==> smaller base than overhang
|
||||
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
{
|
||||
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
||||
}
|
||||
|
||||
unsigned int support_layer_count = layer_count;
|
||||
@@ -253,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
still_in_upper_empty_layers = false;
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
|
||||
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
@@ -264,26 +263,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
{
|
||||
Polygons& supportLayer = supportAreas[layer_idx];
|
||||
|
||||
if (conical_support)
|
||||
{ // with conical support the next layer is allowed to be larger than the previous
|
||||
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
|
||||
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
|
||||
//
|
||||
// conical support can make
|
||||
// layer above layer below
|
||||
// v v
|
||||
// | : |
|
||||
// | ==> : |__
|
||||
// |____ :....
|
||||
//
|
||||
// a miter limit would result in
|
||||
// | : : |
|
||||
// | :.. <== : |__
|
||||
// .\___ :....
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
|
||||
|
||||
supportAreas[layer_idx] = touching_buildplate;
|
||||
@@ -430,7 +409,7 @@ void AreaSupport::handleWallStruts(
|
||||
}
|
||||
|
||||
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
|
||||
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
|
||||
{
|
||||
int roof_layer_count = support_roof_height / layerThickness;
|
||||
|
||||
|
||||
+6
-3
@@ -15,8 +15,9 @@ public:
|
||||
* Generate the support areas and support roof areas for all models.
|
||||
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
|
||||
* \param layer_count total number of layers
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
*/
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -27,8 +28,9 @@ private:
|
||||
* \param storage data storage containing the input layer outline data
|
||||
* \param mesh_idx The index of the object for which to generate support areas
|
||||
* \param layer_count total number of layers
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
*/
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas);
|
||||
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
|
||||
|
||||
|
||||
|
||||
@@ -37,10 +39,11 @@ private:
|
||||
*
|
||||
* \param storage Output storage: support area + support roof area output
|
||||
* \param supportAreas The basic support areas for the current mesh
|
||||
* \param commandSocket Socket over which to report the progress
|
||||
* \param layerThickness The layer height
|
||||
* \param support_roof_height The thickness of the hammock in z directiontt
|
||||
*/
|
||||
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
|
||||
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
|
||||
|
||||
/*!
|
||||
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
|
||||
|
||||
+12
-47
@@ -5,7 +5,6 @@
|
||||
#include "logoutput.h"
|
||||
#include "intpoint.h"
|
||||
#include <unordered_map>
|
||||
#include <functional> // std::function
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -14,31 +13,22 @@ namespace cura
|
||||
* Container for items with location for which the lookup for nearby items is optimized.
|
||||
*
|
||||
* It functions by hashing the items location and lookuping up based on the hash of that location and the hashes of nearby locations.
|
||||
*
|
||||
* We're mapping a cell location multiple times to an object within the cell,
|
||||
* instead of mapping each cell location only once to a vector of objects within the cell.
|
||||
*
|
||||
* The first (current) implementation has the overhead of 'bucket-collisions' where all mappings of two different cells get placed in the same bucket,
|
||||
* which causes findNearby to loop over unneeded elements.
|
||||
* The second (alternative) implementation has the overhead and indirection of creating vectors and all that comes with it."
|
||||
*
|
||||
*/
|
||||
template<typename T>
|
||||
class BucketGrid2D
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Point Cellidx;
|
||||
/*!
|
||||
* Returns a point for which the hash is at a grid position of \p relative_hash relative to \p p.
|
||||
* Returns a point for which the hash is at a grid position of \p relativeHash relative to \p p.
|
||||
*
|
||||
* \param p The point for which to get the relative point to hash
|
||||
* \param relative_hash The relative position - in grid terms - of the relative point.
|
||||
* \return A point for which the hash is at a grid position of \p relative_hash relative to \p p.
|
||||
* \param relativeHash The relative position - in grid terms - of the relative point.
|
||||
* \return A point for which the hash is at a grid position of \p relativeHash relative to \p p.
|
||||
*/
|
||||
inline Point getRelativeForHash(const Point& p, const Cellidx& relative_hash)
|
||||
inline Point getRelativeForHash(const Point& p, const Point& relativeHash)
|
||||
{
|
||||
return p + relative_hash*squareSize;
|
||||
return p + relativeHash*squareSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +43,7 @@ private:
|
||||
* \param p The grid location to hash
|
||||
* \return the hash
|
||||
*/
|
||||
inline uint32_t pointHash_simple(const Cellidx& p) const
|
||||
inline uint32_t pointHash_simple(const Point& p) const
|
||||
{
|
||||
return p.X ^ (p.Y << 8);
|
||||
}
|
||||
@@ -65,7 +55,7 @@ private:
|
||||
*/
|
||||
inline uint32_t pointHash(const Point& point) const
|
||||
{
|
||||
Cellidx p = point/squareSize;
|
||||
Point p = point/squareSize;
|
||||
return pointHash_simple(p);
|
||||
}
|
||||
/*
|
||||
@@ -111,10 +101,6 @@ private:
|
||||
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
*/
|
||||
int squareSize;
|
||||
|
||||
PointHasher point_hasher; //!< The hasher used by the unordered_map
|
||||
|
||||
int max_load_factor; //!< The average number of elements per cell/bucket
|
||||
/*!
|
||||
* The map type used to associate points with their objects.
|
||||
*/
|
||||
@@ -131,17 +117,9 @@ public:
|
||||
* The constructor for a bucket grid.
|
||||
*
|
||||
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
* \param initial_map_size The number of elements to be inserted
|
||||
* \param initial_map_size The minimal number of initial buckets
|
||||
*/
|
||||
BucketGrid2D(int squareSize, unsigned int initial_map_size = 4)
|
||||
: squareSize(squareSize)
|
||||
, point_hasher(squareSize)
|
||||
, max_load_factor(2)
|
||||
, point2object(initial_map_size / max_load_factor, point_hasher)
|
||||
{
|
||||
point2object.max_load_factor(max_load_factor); // we expect each cell to contain at least two points on average
|
||||
point2object.reserve(initial_map_size);
|
||||
}
|
||||
BucketGrid2D(int squareSize, unsigned int initial_map_size = 4) : squareSize(squareSize), point2object(initial_map_size, PointHasher(squareSize)) {};
|
||||
|
||||
/*!
|
||||
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
|
||||
@@ -157,14 +135,10 @@ public:
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
Point relative_point = getRelativeForHash(p, Point(x,y));
|
||||
int bucket_idx = point2object.bucket(relative_point); // when the hash is not a hash of a present item, the bucket_idx returned may be one already encountered
|
||||
int bucket_idx = point2object.bucket(getRelativeForHash(p, Point(x,y))); // when the hash is not a hash of a present item, the bucket_idx returned may be one already encountered
|
||||
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
|
||||
{
|
||||
if (point_hasher(relative_point) == point_hasher(local_it->first))
|
||||
{
|
||||
ret.push_back(local_it->second);
|
||||
}
|
||||
ret.push_back(local_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,17 +159,14 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const std::function<bool(Point, T&)> no_precondition;
|
||||
|
||||
/*!
|
||||
* Find the nearest object to a given lcoation \p p, if there is any in a neighboring cell in the grid.
|
||||
*
|
||||
* \param p The point for which to find the nearest object.
|
||||
* \param nearby Output parameter: the nearest object, if any
|
||||
* \param precondition A precondition which must be satisfied before considering a \p object at a specific \p location as output
|
||||
* \return Whether an object has been found.
|
||||
*/
|
||||
bool findNearestObject(Point& p, T& nearby, std::function<bool(Point location, T& object)> precondition = no_precondition)
|
||||
bool findNearestObject(Point& p, T& nearby)
|
||||
{
|
||||
bool found = false;
|
||||
int64_t bestDist2 = squareSize*9; // 9 > sqrt(2*2 + 2*2)^2 which is the square of the largest distance of a point to a point in a neighboring cell
|
||||
@@ -206,10 +177,6 @@ public:
|
||||
int bucket_idx = point2object.bucket(getRelativeForHash(p, Point(x,y)));
|
||||
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
|
||||
{
|
||||
if (!precondition(local_it->first, local_it->second))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int32_t dist2 = vSize2(local_it->first - p);
|
||||
if (dist2 < bestDist2)
|
||||
{
|
||||
@@ -244,8 +211,6 @@ public:
|
||||
|
||||
|
||||
};
|
||||
template<typename T>
|
||||
const std::function<bool(Point, T&)> BucketGrid2D<T>::no_precondition = [](Point loc, T&) { return true; };
|
||||
|
||||
}//namespace cura
|
||||
#endif//BUCKET_GRID_2D_H
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef UTILS_F_POINT_H
|
||||
#define UTILS_F_POINT_H
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <iostream> // auto-serialization / auto-toString() '<<'
|
||||
|
||||
namespace cura
|
||||
{
|
||||
/*!
|
||||
* 2D coordinates represented by floats
|
||||
*/
|
||||
class FPoint
|
||||
{
|
||||
public:
|
||||
float x, y; //!< Coordinates
|
||||
FPoint() //!< non-initializing constructor
|
||||
{}
|
||||
FPoint(float x, float y) //!< constructor
|
||||
: x(x)
|
||||
, y(y)
|
||||
{}
|
||||
|
||||
FPoint operator+(const FPoint& p) const { return FPoint(x+p.x, y+p.y); }
|
||||
FPoint operator-(const FPoint& p) const { return FPoint(x-p.x, y-p.y); }
|
||||
FPoint operator/(const float i) const { return FPoint(x/i, y/i); }
|
||||
FPoint operator*(const float i) const { return FPoint(x*i, y*i); }
|
||||
|
||||
FPoint& operator += (const FPoint& p) { x += p.x; y += p.y; return *this; }
|
||||
FPoint& operator -= (const FPoint& p) { x -= p.x; y -= p.y; return *this; }
|
||||
|
||||
bool operator==(const FPoint& p) const { return x == p.x && y == p.y; }
|
||||
bool operator!=(const FPoint& p) const { return x != p.x || y != p.y; }
|
||||
|
||||
/*!
|
||||
* output to string stream in standard format
|
||||
*/
|
||||
template<class CharT, class TraitsT>
|
||||
friend
|
||||
std::basic_ostream<CharT, TraitsT>&
|
||||
operator <<(std::basic_ostream<CharT, TraitsT>& os, const FPoint& p)
|
||||
{
|
||||
return os << "(" << p.x << ", " << p.y << ")";
|
||||
}
|
||||
|
||||
/*!
|
||||
* squared vector size
|
||||
*/
|
||||
float vSize2() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/*!
|
||||
* vector size
|
||||
*/
|
||||
float vSize() const
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
/*!
|
||||
* dot product
|
||||
*/
|
||||
float dot(const FPoint& p) const
|
||||
{
|
||||
return x * p.x + y * p.y;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // UTILS_F_POINT_H
|
||||
@@ -1,29 +0,0 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "linearAlg2D.h"
|
||||
|
||||
#include <cmath> // atan2
|
||||
|
||||
#include "intpoint.h" // dot
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
|
||||
{
|
||||
Point ba = a - b;
|
||||
Point bc = c - b;
|
||||
int64_t dott = dot(ba, bc); // dot product
|
||||
int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
float angle = -atan2(det, dott); // from -pi to pi
|
||||
if (angle >= 0 )
|
||||
{
|
||||
return angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return M_PI * 2 + angle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -35,34 +35,34 @@ public:
|
||||
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
|
||||
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
|
||||
|
||||
float max() const
|
||||
float max()
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(float len) const
|
||||
bool testLength(float len)
|
||||
{
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
float vSize2() const
|
||||
float vSize2()
|
||||
{
|
||||
return x*x+y*y+z*z;
|
||||
}
|
||||
|
||||
float vSize() const
|
||||
float vSize()
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
inline FPoint3 normalized() const
|
||||
inline FPoint3 normalized()
|
||||
{
|
||||
return (*this)/vSize();
|
||||
}
|
||||
|
||||
FPoint3 cross(const FPoint3& p) const
|
||||
FPoint3 cross(const FPoint3& p)
|
||||
{
|
||||
return FPoint3(
|
||||
y*p.z-z*p.y,
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
m[2][2] = 1.0;
|
||||
}
|
||||
|
||||
Point3 apply(const FPoint3& p) const
|
||||
Point3 apply(const FPoint3& p)
|
||||
{
|
||||
return Point3(
|
||||
MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]),
|
||||
|
||||
@@ -75,14 +75,14 @@ public:
|
||||
}
|
||||
|
||||
|
||||
int32_t max() const
|
||||
int32_t max()
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(int32_t len) const
|
||||
bool testLength(int32_t len)
|
||||
{
|
||||
if (x > len || x < -len)
|
||||
return false;
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
x*p.y-y*p.x);
|
||||
}
|
||||
|
||||
int64_t dot(const Point3& p) const
|
||||
int64_t dot(const Point3& p)
|
||||
{
|
||||
return x*p.x + y*p.y + z*p.z;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
|
||||
return p0 * len / _len;
|
||||
}
|
||||
|
||||
INLINE Point turn90CCW(const Point& p0)
|
||||
INLINE Point crossZ(const Point& p0)
|
||||
{
|
||||
return Point(-p0.Y, p0.X);
|
||||
}
|
||||
|
||||
@@ -171,44 +171,7 @@ public:
|
||||
// return vSize2(ab) - ax_size*ax_size; // less accurate
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks whether the minimal distance between two line segments is at most \p max_dist
|
||||
* The first line semgent is given by end points \p a and \p b, the second by \p c and \p d.
|
||||
*
|
||||
* \param a One end point of the first line segment
|
||||
* \param b Another end point of the first line segment
|
||||
* \param c One end point of the second line segment
|
||||
* \param d Another end point of the second line segment
|
||||
* \param max_dist The maximal distance between the two line segments for which this function will return true.
|
||||
*/
|
||||
static bool lineSegmentsAreCloserThan(const Point& a, const Point& b, const Point& c, const Point& d, int64_t max_dist)
|
||||
{
|
||||
int64_t max_dist2 = max_dist * max_dist;
|
||||
|
||||
return getDist2FromLineSegment(a, c, b) <= max_dist2
|
||||
|| getDist2FromLineSegment(a, d, b) <= max_dist2
|
||||
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|
||||
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compute the angle between two consecutive line segments.
|
||||
*
|
||||
* The angle is computed from the left side of b when looking from a.
|
||||
*
|
||||
* c
|
||||
* \ .
|
||||
* \ b
|
||||
* angle|
|
||||
* |
|
||||
* a
|
||||
*
|
||||
* \param a start of first line segment
|
||||
* \param b end of first segment and start of second line segment
|
||||
* \param c end of second line segment
|
||||
* \return the angle in radians between 0 and 2 * pi of the corner in \p b
|
||||
*/
|
||||
static float getAngleLeft(const Point& a, const Point& b, const Point& c);
|
||||
};
|
||||
|
||||
|
||||
|
||||
+19
-9
@@ -21,6 +21,20 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
enum PolygonType
|
||||
{
|
||||
NoneType,
|
||||
Inset0Type,
|
||||
InsetXType,
|
||||
SkinType,
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType,
|
||||
MoveCombingType,
|
||||
MoveRetractionType
|
||||
};
|
||||
|
||||
|
||||
class PartsView;
|
||||
|
||||
@@ -65,9 +79,7 @@ public:
|
||||
}
|
||||
|
||||
PolygonRef& operator=(const PolygonRef& other) { polygon = other.polygon; return *this; }
|
||||
|
||||
bool operator==(const PolygonRef& other) const =delete;
|
||||
|
||||
|
||||
ClipperLib::Path& operator*() { return *polygon; }
|
||||
|
||||
template <typename... Args>
|
||||
@@ -350,9 +362,6 @@ public:
|
||||
|
||||
Polygons(const Polygons& other) { polygons = other.polygons; }
|
||||
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
|
||||
|
||||
bool operator==(const Polygons& other) const =delete;
|
||||
|
||||
Polygons difference(const Polygons& other) const
|
||||
{
|
||||
Polygons ret;
|
||||
@@ -396,12 +405,13 @@ public:
|
||||
clipper.Execute(ClipperLib::ctXor, ret.polygons);
|
||||
return ret;
|
||||
}
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
|
||||
{
|
||||
Polygons ret;
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
double miterLimit = 1.2;
|
||||
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
|
||||
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.MiterLimit = miterLimit;
|
||||
clipper.Execute(ret.polygons, distance);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int poi
|
||||
Point p1 = poly[point_idx];
|
||||
Point p2 = poly[(point_idx < (poly.size() - 1)) ? (point_idx + 1) : 0];
|
||||
|
||||
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
|
||||
Point n = normal(off0 + off1, -offset);
|
||||
|
||||
return p1 + n;
|
||||
@@ -85,7 +85,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
Point ret = from;
|
||||
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
|
||||
unsigned int bestPoly = NO_INDEX;
|
||||
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
|
||||
bool is_inside = false;
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
@@ -125,10 +125,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
|
||||
// MM2INT(10.0) to retain precision for the eventual normalization
|
||||
ret = x + normal(inward_dir, distance);
|
||||
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
|
||||
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
|
||||
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
|
||||
is_inside = dot(inward_dir, p - x) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +147,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
|
||||
{
|
||||
projected_p_beyond_prev_segment = false;
|
||||
Point x = a + ab * ax_length / ab_length;
|
||||
|
||||
@@ -160,9 +159,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
if (distance == 0) { ret = x; }
|
||||
else
|
||||
{
|
||||
Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
|
||||
Point inward_dir = crossZ(normal(ab, distance));
|
||||
ret = x + inward_dir;
|
||||
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
|
||||
is_inside = dot(inward_dir, p - x) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +171,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
p1 = p2;
|
||||
}
|
||||
}
|
||||
if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
|
||||
if (is_inside)
|
||||
{
|
||||
if (bestDist2 < distance * distance)
|
||||
{
|
||||
@@ -192,6 +191,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
|
||||
return NO_INDEX;
|
||||
}
|
||||
|
||||
|
||||
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
|
||||
{
|
||||
PolygonRef poly1 = poly1_result.poly;
|
||||
|
||||
@@ -66,10 +66,10 @@ public:
|
||||
* \param polygons The polygons onto which to move the point
|
||||
* \param from The point to move.
|
||||
* \param distance The distance by which to move the point.
|
||||
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
|
||||
* \param maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
|
||||
* \return The index to the polygon onto which we have moved the point.
|
||||
*/
|
||||
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits<int64_t>::max());
|
||||
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max());
|
||||
|
||||
/*!
|
||||
* Find the two points in two polygons with the smallest distance.
|
||||
|
||||
@@ -79,7 +79,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
Point& last_point = *last_it;
|
||||
Point& point = *it;
|
||||
|
||||
if (&from_it.poly == &to_list_poly
|
||||
if ( from_it.poly == to_list_poly
|
||||
&& (
|
||||
(from_it.it == last_it || from_it.it == it) // we currently consider a linesegment directly connected to [from]
|
||||
|| (from_it.prev().it == it || from_it.next().it == last_it) // line segment from [last_point] to [point] is connected to line segment of which [from] is the other end
|
||||
@@ -94,7 +94,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
int64_t dist2 = vSize2(closest - from);
|
||||
|
||||
if (dist2 > line_width * line_width
|
||||
|| (&from_it.poly == &to_list_poly
|
||||
|| ( from_it.poly == to_list_poly
|
||||
&& dot(from_it.next().p() - from, point - last_point) > 0
|
||||
&& dot(from - from_it.prev().p(), point - last_point) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
|
||||
)
|
||||
|
||||
+1
-10
@@ -74,16 +74,7 @@ class WallOverlapComputation
|
||||
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
|
||||
: poly(poly), it(it) { }
|
||||
Point& p() const { return *it; }
|
||||
/*!
|
||||
* Test whether two iterators refer to the same polygon in the same polygon list.
|
||||
*
|
||||
* \param other The ListPolyIt to test for equality
|
||||
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
|
||||
*/
|
||||
bool operator==(const ListPolyIt& other) const
|
||||
{
|
||||
return &poly == &other.poly && it == other.it;
|
||||
}
|
||||
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.it; }
|
||||
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
|
||||
//!< move the iterator forward (and wrap around at the end)
|
||||
ListPolyIt& operator++()
|
||||
|
||||
@@ -25,9 +25,9 @@ void GCodePlannerTest::setUp()
|
||||
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
|
||||
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
|
||||
fan_speed_layer_time_settings.cool_min_speed = 0.5;
|
||||
// Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
|
||||
// storage nr height position extruder time settings combing offset avoid distance
|
||||
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
|
||||
// Command Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
|
||||
// socket storage nr height position extruder time settings combing offset avoid distance
|
||||
gCodePlanner = new GCodePlanner(nullptr, *storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
|
||||
}
|
||||
|
||||
void GCodePlannerTest::tearDown()
|
||||
@@ -46,7 +46,7 @@ void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
|
||||
|
||||
GCodeExport gcode;
|
||||
GCodePathConfig configuration = storage->travel_config;
|
||||
gCodePlanner->addExtrusionMove(Point(0, 0), &configuration, SpaceFillType::Lines, 1.0f); //Need to have at least one path to have a configuration.
|
||||
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
|
||||
TimeMaterialEstimates before_retract = gCodePlanner->computeNaiveTimeEstimates();
|
||||
gCodePlanner->writeRetraction(gcode,(unsigned int)0,(unsigned int)0); //Make a retract.
|
||||
TimeMaterialEstimates after_retract = gCodePlanner->computeNaiveTimeEstimates();
|
||||
|
||||
@@ -215,56 +215,4 @@ void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(100, 1), 1.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle45CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -100), 1.75);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, -100), 1.5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, 100), .5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightBackTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, 1), 0.0);
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -1), 2.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAABTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(100, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftABBTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(100, 0), Point(100, 100)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAAATest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(0, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds)
|
||||
{
|
||||
float actual_angle = actual_angle_in_half_rounds * M_PI;
|
||||
float supposed_angle = LinearAlg2D::getAngleLeft(a, b, c);
|
||||
std::stringstream ss;
|
||||
ss << "Corner in " << a << "-" << b << "-" << c << " was computed to have an angle of " << supposed_angle << " instead of " << actual_angle << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::fabs(actual_angle - supposed_angle) <= maximum_error_angle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,15 +42,6 @@ class LinearAlg2DTest : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
|
||||
|
||||
CPPUNIT_TEST(getAngleStraightTest);
|
||||
CPPUNIT_TEST(getAngle90CcwTest);
|
||||
CPPUNIT_TEST(getAngle90CwTest);
|
||||
CPPUNIT_TEST(getAngle45CcwTest);
|
||||
CPPUNIT_TEST(getAngleStraightBackTest);
|
||||
CPPUNIT_TEST(getAngleLeftAABTest);
|
||||
CPPUNIT_TEST(getAngleLeftABBTest);
|
||||
CPPUNIT_TEST(getAngleLeftAAATest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -99,15 +90,6 @@ public:
|
||||
void getDist2FromLineSegmentDiagonal2LargeTest();
|
||||
void getDist2FromLineSegmentZeroNearTest();
|
||||
void getDist2FromLineSegmentZeroOnTest();
|
||||
|
||||
void getAngleStraightTest();
|
||||
void getAngle90CcwTest();
|
||||
void getAngle90CwTest();
|
||||
void getAngle45CcwTest();
|
||||
void getAngleStraightBackTest();
|
||||
void getAngleLeftAABTest();
|
||||
void getAngleLeftABBTest();
|
||||
void getAngleLeftAAATest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -129,21 +111,6 @@ private:
|
||||
* \param actual_is_beyond Whether the point is actually beyond the line.
|
||||
*/
|
||||
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
|
||||
|
||||
/*!
|
||||
* \brief The maximum allowed error in angle measurements.
|
||||
*/
|
||||
static constexpr float maximum_error_angle = 1.0;
|
||||
|
||||
/*!
|
||||
* Performs the assertion of the getAngle tests
|
||||
*
|
||||
* \param a the a parameter of getAngle
|
||||
* \param b the b parameter of getAngle
|
||||
* \param c the c parameter of getAngle
|
||||
* \param actual_angle_in_half_rounds the actual angle where 0.5 equals ???
|
||||
*/
|
||||
void getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds);
|
||||
};
|
||||
|
||||
}
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário