Comparar commits
165 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| afefa64505 | |||
| c41ffa669f | |||
| beb2199b4f | |||
| 005bfa5b02 | |||
| 4f5c9d235f | |||
| e2f3540d6b | |||
| 1ccded78cc | |||
| e47318bdbd | |||
| ca5a8904fc | |||
| 6c1e833d06 | |||
| 9c1b21184f | |||
| 4feddf5da8 | |||
| 672d12105e | |||
| 8f3af9a6cc | |||
| 9eed417b31 | |||
| 51a670ee99 | |||
| 3833ed3cf2 | |||
| 5ea109af98 | |||
| 05f7d7fd61 | |||
| ac41b10c05 | |||
| 91a2a0c8e9 | |||
| 795c5a24ef | |||
| c89476a9ac | |||
| c457bbefe4 | |||
| 027e26b3a6 | |||
| 732822d3fb | |||
| 6507ef5fa0 | |||
| 489ca9e644 | |||
| e12272e2fa | |||
| 367f51be27 | |||
| 6af639b092 | |||
| 94234c6d2f | |||
| 055135a740 | |||
| 27a9bbfa4c | |||
| 78e1595b83 | |||
| 510ddca188 | |||
| e2fe742df7 | |||
| 844024b089 | |||
| e04714ae40 | |||
| 763fa1f17b | |||
| b86d82068f | |||
| 8574563d76 | |||
| 3879cc9485 | |||
| 5f1bf1bb10 | |||
| 49bc2ec79c | |||
| d8fec42059 | |||
| fa19e9a5a3 | |||
| 081c3be70c | |||
| 85b2fb7c26 | |||
| a1880b371e | |||
| cd033ef6ab | |||
| e909af9abd | |||
| dd5fbf14e4 | |||
| 0985b97c54 | |||
| 98a78e1844 | |||
| 37c8ad3061 | |||
| d2187fedbc | |||
| 5b0a50456f | |||
| 66f4b51a3e | |||
| 3ac6ee1b37 | |||
| 4d8b22a224 | |||
| 3a0143ff4c | |||
| c6a4945469 | |||
| abc6514b6d | |||
| dc26358747 | |||
| 3485e5a4ad | |||
| bd47fd2c67 | |||
| d7d957d8f7 | |||
| bec8c235ea | |||
| eb1bbd41b0 | |||
| 49f2f21c08 | |||
| 9ed25b95ee | |||
| c8b8abd4c6 | |||
| 37c461fa86 | |||
| 7d9c8ee1b1 | |||
| 5df73e0e30 | |||
| 5d8926e3e7 | |||
| 277c478e3b | |||
| 6f654241f7 | |||
| 62910c9d0a | |||
| f92ee17577 | |||
| dc3d94e0a8 | |||
| 6c9db68573 | |||
| f7cdd63f1d | |||
| 3400439f5d | |||
| 5fbf9a8907 | |||
| df6d2fc592 | |||
| 7fc18c2057 | |||
| c7bf1e087a | |||
| cffd6ac860 | |||
| 205c4f8cc9 | |||
| c797163536 | |||
| d31acdb244 | |||
| 810f689418 | |||
| 7270290fe3 | |||
| 37e3114311 | |||
| c3ef64fe18 | |||
| 5d592553a6 | |||
| 38fad10453 | |||
| 0dbf80587b | |||
| d7e966ad83 | |||
| 593dd03987 | |||
| 59e6a075e8 | |||
| 3f348ab1ba | |||
| 44fedbae7a | |||
| 77a378ba1c | |||
| 4f408847fb | |||
| 5941d2252c | |||
| 6897a87584 | |||
| cc2bb36fb4 | |||
| 0ea387a6f0 | |||
| 14a01a6253 | |||
| e177303c4b | |||
| 4c1c43649d | |||
| 4f524613fd | |||
| a9fdad71b4 | |||
| 9fb6a217a4 | |||
| c480c96066 | |||
| 1bcb38dcb6 | |||
| 0e7b164532 | |||
| 0f6bdfd36e | |||
| 4d79ea3e9e | |||
| 199fa070d6 | |||
| ef1dece5d2 | |||
| 87e42fd9bd | |||
| 8d0a75779d | |||
| e882b23d76 | |||
| 4361dbf8fb | |||
| 81ae074b86 | |||
| 3171bd4dcb | |||
| 199007fa76 | |||
| d0858bbdb6 | |||
| cc23d73532 | |||
| 4c139d6441 | |||
| 13a18549bf | |||
| bd2f66e2eb | |||
| 267eb7aef0 | |||
| 8cd48bf5a2 | |||
| a766455ec8 | |||
| af08b57799 | |||
| dbecb29dc8 | |||
| 7df0a34464 | |||
| 925d50fc5d | |||
| e3163586af | |||
| f58441a6ad | |||
| 04b4b2c057 | |||
| 0f5fd8d6ca | |||
| 645b06271d | |||
| ef61337ef8 | |||
| b8d5474811 | |||
| 4c60339695 | |||
| e1a594ad3e | |||
| 2b3f22872e | |||
| fa03c3823f | |||
| 2cf0d40775 | |||
| d20df39d5f | |||
| 57bc411bc6 | |||
| 0c30d27892 | |||
| 0c9562b273 | |||
| 742eedda06 | |||
| bc6a139d1f | |||
| 9770e91961 | |||
| 9e3dfb5b12 | |||
| c05c3ddaed | |||
| 804c288353 |
+47
-9
@@ -2,7 +2,14 @@ project(CuraEngine)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
find_package(Arcus REQUIRED)
|
||||
option (ENABLE_ARCUS
|
||||
"Enable support for ARCUS" ON)
|
||||
|
||||
if (ENABLE_ARCUS)
|
||||
message(STATUS "Building with Arcus")
|
||||
find_package(Arcus REQUIRED)
|
||||
add_definitions(-DARCUS)
|
||||
endif ()
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
@@ -22,8 +29,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
|
||||
@@ -48,12 +55,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
|
||||
@@ -63,9 +70,10 @@ 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
|
||||
@@ -76,6 +84,11 @@ set(engine_SRCS # Except main.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
|
||||
@@ -85,16 +98,26 @@ 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
|
||||
GCodePlannerTest
|
||||
)
|
||||
set(engine_TEST_INFILL
|
||||
)
|
||||
set(engine_TEST_UTILS
|
||||
BucketGrid2DTest
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
# Generating ProtoBuf protocol.
|
||||
# Generating ProtoBuf protocol
|
||||
if (ENABLE_ARCUS)
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
endif ()
|
||||
|
||||
# Compiling CuraEngine itself.
|
||||
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
|
||||
target_link_libraries(_CuraEngine clipper Arcus)
|
||||
target_link_libraries(_CuraEngine clipper)
|
||||
if (ENABLE_ARCUS)
|
||||
target_link_libraries(_CuraEngine Arcus)
|
||||
endif ()
|
||||
|
||||
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
|
||||
@@ -113,8 +136,23 @@ 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})
|
||||
|
||||
+26
-3
@@ -2,18 +2,20 @@ syntax = "proto3";
|
||||
|
||||
package cura.proto;
|
||||
|
||||
message ObjectList
|
||||
|
||||
message ObjectList
|
||||
{
|
||||
repeated Object objects = 1;
|
||||
repeated Setting settings = 2;
|
||||
}
|
||||
|
||||
// typeid 1
|
||||
message Slice
|
||||
{
|
||||
repeated ObjectList object_lists = 1;
|
||||
}
|
||||
|
||||
message Object
|
||||
message Object
|
||||
{
|
||||
int64 id = 1;
|
||||
bytes vertices = 2; //An array of 3 floats.
|
||||
@@ -22,13 +24,28 @@ message Object
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
message Progress
|
||||
// typeid 3
|
||||
message Progress
|
||||
{
|
||||
float amount = 1;
|
||||
}
|
||||
|
||||
// typeid 2
|
||||
message SlicedObjectList
|
||||
{
|
||||
repeated SlicedObject objects = 1;
|
||||
}
|
||||
|
||||
message SlicedObject
|
||||
{
|
||||
int64 id = 1;
|
||||
|
||||
repeated Layer layers = 2;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
|
||||
@@ -53,16 +70,20 @@ message Polygon {
|
||||
float line_width = 3;
|
||||
}
|
||||
|
||||
// typeid 4
|
||||
message GCodeLayer {
|
||||
int64 id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 5
|
||||
message ObjectPrintTime {
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
}
|
||||
|
||||
// typeid 6
|
||||
message SettingList {
|
||||
repeated Setting settings = 1;
|
||||
}
|
||||
@@ -73,9 +94,11 @@ message Setting {
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// typeid 7
|
||||
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 = NO
|
||||
QT_AUTOBRIEF = YES
|
||||
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-28
@@ -12,8 +12,6 @@ namespace cura
|
||||
|
||||
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
PrimeTower primetower();
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
if (meshgroup_number == 1)
|
||||
@@ -22,7 +20,9 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
}
|
||||
|
||||
setConfigFanSpeedLayerTime();
|
||||
|
||||
@@ -153,8 +153,8 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
{ // support
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"));
|
||||
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_lines"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
|
||||
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_infill"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
|
||||
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
@@ -178,8 +178,15 @@ void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
std::string prefix = gcode.getFileHeader();
|
||||
gcode.writeCode(prefix.c_str());
|
||||
std::ostringstream prefix;
|
||||
prefix << "FLAVOR:" << toString(gcode.getFlavor());
|
||||
gcode.writeComment(prefix.str().c_str());
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
gcode.writeComment("TIME:666");
|
||||
gcode.writeComment("MATERIAL:666");
|
||||
gcode.writeComment("MATERIAL2:-1");
|
||||
}
|
||||
}
|
||||
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
@@ -274,7 +281,9 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
|
||||
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);
|
||||
@@ -303,7 +312,9 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
@@ -358,7 +369,17 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
|
||||
{
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
|
||||
int max_inner_wall_width = 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"));
|
||||
}
|
||||
}
|
||||
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.
|
||||
@@ -541,7 +562,16 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
return;
|
||||
}
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
|
||||
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);
|
||||
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
@@ -560,14 +590,18 @@ 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) && layer_nr & 1)
|
||||
if ((infill_pattern == EFillMethod::LINES || infill_pattern == EFillMethod::ZIG_ZAG))
|
||||
{
|
||||
infill_angle += 90;
|
||||
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;
|
||||
}
|
||||
}
|
||||
int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
|
||||
int infill_overlap = mesh->getSettingInMicrons("infill_overlap");
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
@@ -611,7 +645,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
|
||||
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
if (infill_line_distance > 0)
|
||||
{
|
||||
@@ -629,7 +663,7 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
|
||||
if (infill_line_distance == 0 || part.infill_area.size() == 0)
|
||||
@@ -691,7 +725,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
|
||||
}
|
||||
|
||||
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
|
||||
{
|
||||
@@ -773,9 +807,9 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
return;
|
||||
|
||||
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
|
||||
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
|
||||
|| support_roof_extruder_nr == extruder_nr_before;
|
||||
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
|
||||
|
||||
@@ -788,24 +822,24 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
|
||||
|
||||
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
{
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
@@ -819,10 +853,10 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
|
||||
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
|
||||
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
|
||||
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
|
||||
|
||||
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
|
||||
|
||||
@@ -839,7 +873,7 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
|
||||
|
||||
double infill_overlap = 0; // support infill should not be expanded outward
|
||||
int infill_overlap = 0; // support infill should not be expanded outward
|
||||
|
||||
int offset_from_outline = 0;
|
||||
bool remove_overlapping_perimeters = false;
|
||||
@@ -849,7 +883,7 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
|
||||
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
|
||||
offset_from_outline = -extrusion_width;
|
||||
infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
|
||||
infill_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);
|
||||
Polygons support_polygons;
|
||||
@@ -889,7 +923,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
|
||||
{
|
||||
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
|
||||
}
|
||||
double infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
int infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
int outline_offset = 0;
|
||||
|
||||
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
|
||||
@@ -942,8 +976,15 @@ void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix);
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
|
||||
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
|
||||
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
|
||||
}
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix.str());
|
||||
}
|
||||
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
|
||||
+152
-62
@@ -37,23 +37,45 @@ class FffGcodeWriter : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
|
||||
private:
|
||||
int max_object_height;
|
||||
int meshgroup_number; //!< used for sequential printing of objects
|
||||
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
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.
|
||||
*/
|
||||
GCodeExport gcode;
|
||||
|
||||
/*!
|
||||
* The gcode file to write to when using CuraEngine as command line tool.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //!< The settings used relating to minimal layer time and fan speeds.
|
||||
|
||||
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:
|
||||
@@ -66,11 +88,22 @@ public:
|
||||
meshgroup_number = 1;
|
||||
max_object_height = 0;
|
||||
}
|
||||
void resetFileNumber()
|
||||
|
||||
/*!
|
||||
* Reset the meshgroup number to process the next slicing.
|
||||
*/
|
||||
void resetMeshGroupNumber()
|
||||
{
|
||||
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.
|
||||
*/
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
output_file.open(filename);
|
||||
@@ -81,59 +114,108 @@ 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return gcode.getTotalFilamentUsed(e);
|
||||
return gcode.getTotalFilamentUsed(extruder_nr);
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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);
|
||||
|
||||
//Setup the retraction parameters.
|
||||
/*!
|
||||
* Set the retraction config globally, per extruder and per mesh.
|
||||
*
|
||||
* \param[out] storage The data storage to which to save the configurations
|
||||
*/
|
||||
void setConfigRetraction(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* initialize GcodePathConfig config parameters which don't change over all layers
|
||||
* 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
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void processStartingCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Move up and over the just printed model to print the next model.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Move up and over the already printed meshgroups to print the next meshgroup.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
*/
|
||||
void processNextMeshGroupCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Add raft gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Add a layer to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* 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.
|
||||
* \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.
|
||||
@@ -141,24 +223,27 @@ private:
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Add the skirt to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Add the skirt to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage 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 print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Adds the ooze shield to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage 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 print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Adds the draft protection screen to the layer plan \p gcodeLayer.
|
||||
*
|
||||
* \param[in] storage 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.
|
||||
*/
|
||||
@@ -166,16 +251,18 @@ private:
|
||||
|
||||
/*!
|
||||
* Calculate in which order to print the meshes.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*
|
||||
* \param[in] storage 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 GCode in mesh surface mode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh to add to the gcode.
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -183,9 +270,10 @@ 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 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.
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
|
||||
*
|
||||
* \param[in] storage where the slice data is stored.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -193,9 +281,10 @@ private:
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
* 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.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
@@ -203,35 +292,36 @@ 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.
|
||||
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
|
||||
*
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \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 fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int 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 gcode.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \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 fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* 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 gcode.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \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
|
||||
@@ -242,34 +332,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 gcode.
|
||||
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
|
||||
/*!
|
||||
* Add the support to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Add the support to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage 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 gcode, or after.
|
||||
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, 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 gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage 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 addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
/*!
|
||||
* Add the support roofs to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* Add the support roofs to the layer plan \p gcodeLayer of the current layer.
|
||||
* \param[in] storage 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.
|
||||
*/
|
||||
@@ -280,7 +370,7 @@ private:
|
||||
*
|
||||
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
|
||||
*
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param[in] storage 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
|
||||
@@ -289,7 +379,7 @@ private:
|
||||
|
||||
/*!
|
||||
* Add the prime tower gcode for the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param[in] storage 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.
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "support.h"
|
||||
#include "multiVolumes.h"
|
||||
#include "layerPart.h"
|
||||
#include "slicer/MultiVolumes.h"
|
||||
#include "slicer/LayerPart.h"
|
||||
#include "TextureProcessor.h"
|
||||
#include "inset.h"
|
||||
#include "skirt.h"
|
||||
#include "skin.h"
|
||||
@@ -24,6 +25,9 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->beginSendSlicedObject();
|
||||
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
@@ -70,7 +74,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
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);
|
||||
/*
|
||||
@@ -90,14 +94,15 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
//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];
|
||||
|
||||
@@ -235,7 +240,7 @@ 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("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"));
|
||||
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"));
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,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";
|
||||
@@ -103,7 +103,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
CommandSocket::getInstance()->sendLayerData();
|
||||
CommandSocket::getInstance()->endSendSlicedObject();
|
||||
}
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
|
||||
+98
-13
@@ -19,6 +19,9 @@ namespace cura {
|
||||
class FffProcessor : public SettingsBase , NoCopy
|
||||
{
|
||||
private:
|
||||
/*!
|
||||
* The FffProcessor used for the (current) slicing (The instance of this singleton)
|
||||
*/
|
||||
static FffProcessor instance;
|
||||
|
||||
FffProcessor()
|
||||
@@ -28,58 +31,140 @@ private:
|
||||
{
|
||||
}
|
||||
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.
|
||||
*/
|
||||
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
|
||||
|
||||
void resetFileNumber()
|
||||
|
||||
/*!
|
||||
* Reset the meshgroup number to the first meshgroup to start a new slicing.
|
||||
*/
|
||||
void resetMeshGroupNumber()
|
||||
{
|
||||
gcode_writer.resetFileNumber();
|
||||
gcode_writer.resetMeshGroupNumber();
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return gcode_writer.getTotalFilamentUsed(e);
|
||||
return gcode_writer.getTotalFilamentUsed(extruder_nr);
|
||||
}
|
||||
|
||||
/*!
|
||||
* 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ void LayerPlanBuffer::flush()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,9 @@ public:
|
||||
{
|
||||
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();
|
||||
}
|
||||
return buffer.back();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,27 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,33 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,30 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,42 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,27 @@
|
||||
/** 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
|
||||
+186
-7
@@ -22,13 +22,17 @@ 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, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
char buffer[1024];
|
||||
@@ -61,7 +65,7 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
|
||||
@@ -114,7 +118,7 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
if (f == nullptr)
|
||||
@@ -168,19 +172,194 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
return loadMeshSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
|
||||
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)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
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...
|
||||
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...
|
||||
{
|
||||
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
|
||||
|
||||
+21
-16
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "TexturedMesh.h"
|
||||
#include "ExtruderTrain.h"
|
||||
|
||||
namespace cura
|
||||
@@ -43,6 +44,10 @@ public:
|
||||
delete extruders[extruder];
|
||||
}
|
||||
}
|
||||
for (Mesh* mesh : meshes)
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -63,34 +68,34 @@ public:
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<Mesh*> meshes;
|
||||
|
||||
Point3 min() //! minimal corner of bounding box
|
||||
Point3 min() const //! 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() //! maximal corner of bounding box
|
||||
Point3 max() const //! 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);
|
||||
@@ -100,9 +105,9 @@ public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(Mesh& m : meshes)
|
||||
for(Mesh* m : meshes)
|
||||
{
|
||||
m.clear();
|
||||
m->clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,17 +122,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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -141,7 +146,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, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
|
||||
|
||||
}//namespace cura
|
||||
#endif//MESH_GROUP_H
|
||||
|
||||
+9
-13
@@ -61,9 +61,9 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
std::max( max_object_height_per_extruder[extr_nr]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_extruder_nr]
|
||||
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_infill_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
|
||||
, storage.support.layer_nr_max_filled_layer );
|
||||
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_roof_extruder_nr] =
|
||||
@@ -104,7 +104,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; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int tower_distance = 0;
|
||||
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,9 +117,7 @@ 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_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
{
|
||||
generatePaths3(storage);
|
||||
}
|
||||
@@ -127,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_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
if (storage.max_object_height_second_to_last_extruder >= 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; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int tower_distance = 0;
|
||||
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));
|
||||
@@ -170,7 +168,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
{
|
||||
|
||||
int n_patterns = 2; // alternating patterns between layers
|
||||
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
|
||||
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
|
||||
|
||||
generateGroundpoly(storage);
|
||||
|
||||
@@ -198,9 +196,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
{
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#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
|
||||
@@ -0,0 +1,32 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,137 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,78 @@
|
||||
/** 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
|
||||
+4
-3
@@ -23,9 +23,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);
|
||||
}
|
||||
|
||||
@@ -86,8 +86,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 (chainified.size() > 0)
|
||||
{
|
||||
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygon.h"
|
||||
|
||||
@@ -550,7 +550,7 @@ void Wireframe2gcode::processStartingCode()
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
gcode.writeCode(gcode.getFileHeader().c_str());
|
||||
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "Weaver.h"
|
||||
|
||||
+83
-19
@@ -6,9 +6,11 @@
|
||||
#include <thread>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef ARCUS
|
||||
#include <Arcus/Socket.h>
|
||||
#include <Arcus/SocketListener.h>
|
||||
#include <Arcus/Error.h>
|
||||
#endif
|
||||
|
||||
#include <string> // stoi
|
||||
|
||||
@@ -27,6 +29,7 @@ namespace cura {
|
||||
|
||||
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
|
||||
|
||||
#ifdef ARCUS
|
||||
class Listener : public Arcus::SocketListener
|
||||
{
|
||||
public:
|
||||
@@ -57,18 +60,25 @@ public:
|
||||
Private()
|
||||
: socket(nullptr)
|
||||
, object_count(0)
|
||||
, current_sliced_object(nullptr)
|
||||
, sliced_objects(0)
|
||||
, current_layer_count(0)
|
||||
, current_layer_offset(0)
|
||||
{ }
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
|
||||
cura::proto::Layer* getLayerById(int id);
|
||||
|
||||
Arcus::Socket* socket;
|
||||
|
||||
// Number of objects that need to be sliced
|
||||
int object_count;
|
||||
|
||||
|
||||
// Message that holds a list of sliced objects
|
||||
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
|
||||
|
||||
// Message that holds the currently sliced object (to be added to sliced_object_list)
|
||||
cura::proto::SlicedObject* current_sliced_object;
|
||||
|
||||
// Number of sliced objects for this sliced object list
|
||||
int sliced_objects;
|
||||
|
||||
@@ -76,6 +86,9 @@ public:
|
||||
// Used for incrementing the current layer in one at a time mode
|
||||
int current_layer_count;
|
||||
int current_layer_offset;
|
||||
|
||||
// Ids of the sliced objects
|
||||
std::vector<int64_t> object_ids;
|
||||
|
||||
std::string temp_gcode_file;
|
||||
std::ostringstream gcode_output_stream;
|
||||
@@ -83,12 +96,17 @@ 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, std::shared_ptr<cura::proto::Layer>> sliced_layers;
|
||||
std::unordered_map<int, cura::proto::Layer*> sliced_layers;
|
||||
};
|
||||
#endif
|
||||
|
||||
CommandSocket::CommandSocket()
|
||||
#ifdef ARCUS
|
||||
: private_data(new Private)
|
||||
#endif
|
||||
{
|
||||
#ifdef ARCUS
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandSocket* CommandSocket::getInstance()
|
||||
@@ -109,12 +127,13 @@ bool CommandSocket::isInstantiated()
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
private_data->socket = new Arcus::Socket();
|
||||
private_data->socket->addListener(new Listener());
|
||||
|
||||
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::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());
|
||||
@@ -157,6 +176,7 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
// Reset object counts
|
||||
private_data->object_count = 0;
|
||||
private_data->object_ids.clear();
|
||||
for(auto object : slice->object_lists())
|
||||
{
|
||||
handleObjectList(&object);
|
||||
@@ -166,7 +186,7 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
//If there is an object to slice, do so.
|
||||
if(private_data->objects_to_slice.size())
|
||||
{
|
||||
FffProcessor::getInstance()->resetFileNumber();
|
||||
FffProcessor::getInstance()->resetMeshGroupNumber();
|
||||
for(auto object : private_data->objects_to_slice)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
@@ -190,11 +210,12 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
log("Closing connection\n");
|
||||
private_data->socket->close();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARCUS
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
if(list->objects_size() <= 0)
|
||||
@@ -240,8 +261,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
}
|
||||
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
for(int i = 0; i < face_count; ++i)
|
||||
{
|
||||
@@ -269,6 +290,7 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
private_data->object_ids.push_back(object.id());
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
@@ -283,20 +305,32 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
|
||||
#ifdef ARCUS
|
||||
if(!private_data->current_sliced_object)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cura::proto::Layer* layer = private_data->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)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!private_data->current_sliced_object)
|
||||
return;
|
||||
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
|
||||
cura::proto::Layer* proto_layer = private_data->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
@@ -307,15 +341,18 @@ void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons&
|
||||
p->set_points(polydata);
|
||||
p->set_line_width(line_width);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::Progress>();
|
||||
amount /= private_data->object_count;
|
||||
amount += private_data->sliced_objects * (1. / private_data->object_count);
|
||||
message->set_amount(amount);
|
||||
private_data->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
@@ -325,10 +362,12 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
|
||||
void CommandSocket::sendPrintTime()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
|
||||
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
|
||||
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
|
||||
private_data->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
@@ -340,68 +379,92 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
|
||||
// socket.sendFloat32(print_time);
|
||||
}
|
||||
|
||||
void CommandSocket::sendLayerData()
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if(!private_data->sliced_object_list)
|
||||
{
|
||||
private_data->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]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
{
|
||||
#ifdef ARCUS
|
||||
private_data->sliced_objects++;
|
||||
private_data->current_layer_offset = private_data->current_layer_count;
|
||||
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
|
||||
std::cout << "End sliced object called. Sliced objects " << private_data->sliced_objects << " object count: " << private_data->object_count << std::endl;
|
||||
|
||||
if(private_data->sliced_objects >= private_data->object_count)
|
||||
{
|
||||
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
|
||||
{
|
||||
private_data->socket->sendMessage(entry.second); //Send the actual layers.
|
||||
}
|
||||
private_data->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);
|
||||
}
|
||||
#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);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::flushGcode()
|
||||
{
|
||||
#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);
|
||||
|
||||
private_data->gcode_output_stream.str("");
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
private_data->socket->sendMessage(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
|
||||
#ifdef ARCUS
|
||||
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
id += current_layer_offset;
|
||||
|
||||
auto itr = sliced_layers.find(id);
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> layer;
|
||||
cura::proto::Layer* layer = nullptr;
|
||||
if(itr != sliced_layers.end())
|
||||
{
|
||||
layer = itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = std::make_shared<cura::proto::Layer>();
|
||||
layer = current_sliced_object->add_layers();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
sliced_layers[id] = layer;
|
||||
@@ -409,5 +472,6 @@ std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
|
||||
|
||||
return layer;
|
||||
}
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+16
-8
@@ -9,10 +9,12 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef ARCUS
|
||||
#include "Cura.pb.h"
|
||||
#endif
|
||||
|
||||
namespace cura {
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class CommandSocket
|
||||
{
|
||||
@@ -35,7 +37,8 @@ public:
|
||||
* \param port int of the port to connect with.
|
||||
*/
|
||||
void connect(const std::string& ip, int port);
|
||||
|
||||
|
||||
#ifdef ARCUS
|
||||
/*!
|
||||
* Handler for ObjectList message.
|
||||
* Loads all objects from the message and starts the slicing process
|
||||
@@ -47,6 +50,7 @@ public:
|
||||
* This simply sets all the settings by using key value pair
|
||||
*/
|
||||
void handleSettingList(cura::proto::SettingList* list);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
|
||||
@@ -82,14 +86,16 @@ public:
|
||||
* Does nothing at the moment
|
||||
*/
|
||||
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
|
||||
|
||||
/*!
|
||||
* Start the slicing of a new meshgroup
|
||||
*/
|
||||
void beginSendSlicedObject();
|
||||
|
||||
/*!
|
||||
* Send the sliced layer data to the GUI.
|
||||
*
|
||||
* The GUI may use this to visualise the g-code, so that the user can
|
||||
* inspect the result of slicing.
|
||||
* Conclude the slicing of the current meshgroup, so that we can start the next
|
||||
*/
|
||||
void sendLayerData();
|
||||
void endSendSlicedObject();
|
||||
|
||||
/*!
|
||||
* \brief Sends a message to indicate that all the slicing is done.
|
||||
@@ -107,9 +113,11 @@ public:
|
||||
void flushGcode();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
#ifdef ARCUS
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> private_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+73
-116
@@ -29,23 +29,6 @@ GCodeExport::~GCodeExport()
|
||||
{
|
||||
}
|
||||
|
||||
std::string GCodeExport::getFileHeader(double print_time, int filament_used_0, int filament_used_1)
|
||||
{
|
||||
std::ostringstream prefix;
|
||||
prefix << ";FLAVOR:" << toString(flavor) << new_line;
|
||||
prefix << ";TIME:" << int(print_time) << new_line;
|
||||
if (flavor == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
prefix << ";MATERIAL:" << int(filament_used_0) << new_line;
|
||||
prefix << ";MATERIAL2:" << int(filament_used_1) << new_line;
|
||||
|
||||
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
|
||||
// prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(1))) << new_line; // TODO: the second nozzle size isn't always initiated!
|
||||
}
|
||||
return prefix.str();
|
||||
}
|
||||
|
||||
|
||||
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
|
||||
layer_nr = layer_nr_;
|
||||
}
|
||||
@@ -56,11 +39,6 @@ void GCodeExport::setOutputStream(std::ostream* stream)
|
||||
*output_stream << std::fixed;
|
||||
}
|
||||
|
||||
int GCodeExport::getNozzleSize(int extruder_idx)
|
||||
{
|
||||
return extruder_attr[extruder_idx].nozzle_size;
|
||||
}
|
||||
|
||||
Point GCodeExport::getExtruderOffset(int id)
|
||||
{
|
||||
return extruder_attr[id].nozzle_offset;
|
||||
@@ -155,48 +133,12 @@ double GCodeExport::getCurrentExtrudedVolume()
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::eToMm(double e)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return e / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::mm3ToE(double mm3)
|
||||
double GCodeExport::getTotalFilamentUsed(int extruder_nr)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return mm3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mm3 / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
}
|
||||
|
||||
double GCodeExport::mmToE(double mm)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return mm * extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double GCodeExport::getTotalFilamentUsed(int e)
|
||||
{
|
||||
if (e == current_extruder)
|
||||
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
|
||||
return extruder_attr[e].totalFilament;
|
||||
if (extruder_nr == current_extruder)
|
||||
return extruder_attr[extruder_nr].totalFilament + getCurrentExtrudedVolume();
|
||||
return extruder_attr[extruder_nr].totalFilament;
|
||||
}
|
||||
|
||||
double GCodeExport::getTotalPrintTime()
|
||||
@@ -234,12 +176,12 @@ void GCodeExport::writeComment(std::string comment)
|
||||
*output_stream << comment[i];
|
||||
}
|
||||
}
|
||||
*output_stream << new_line;
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
{
|
||||
*output_stream << ";TYPE:" << type << new_line;
|
||||
*output_stream << ";TYPE:" << type << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeTypeComment(PrintFeatureType type)
|
||||
@@ -247,25 +189,25 @@ void GCodeExport::writeTypeComment(PrintFeatureType type)
|
||||
switch (type)
|
||||
{
|
||||
case PrintFeatureType::OuterWall:
|
||||
*output_stream << ";TYPE:WALL-OUTER" << new_line;
|
||||
*output_stream << ";TYPE:WALL-OUTER\n";
|
||||
break;
|
||||
case PrintFeatureType::InnerWall:
|
||||
*output_stream << ";TYPE:WALL-INNER" << new_line;
|
||||
*output_stream << ";TYPE:WALL-INNER\n";
|
||||
break;
|
||||
case PrintFeatureType::Skin:
|
||||
*output_stream << ";TYPE:SKIN" << new_line;
|
||||
*output_stream << ";TYPE:SKIN\n";
|
||||
break;
|
||||
case PrintFeatureType::Support:
|
||||
*output_stream << ";TYPE:SUPPORT" << new_line;
|
||||
*output_stream << ";TYPE:SUPPORT\n";
|
||||
break;
|
||||
case PrintFeatureType::Skirt:
|
||||
*output_stream << ";TYPE:SKIRT" << new_line;
|
||||
*output_stream << ";TYPE:SKIRT\n";
|
||||
break;
|
||||
case PrintFeatureType::Infill:
|
||||
*output_stream << ";TYPE:FILL" << new_line;
|
||||
*output_stream << ";TYPE:FILL\n";
|
||||
break;
|
||||
case PrintFeatureType::SupportInfill:
|
||||
*output_stream << ";TYPE:SUPPORT" << new_line;
|
||||
*output_stream << ";TYPE:SUPPORT\n";
|
||||
break;
|
||||
case PrintFeatureType::MoveCombing:
|
||||
case PrintFeatureType::MoveRetraction:
|
||||
@@ -278,24 +220,24 @@ void GCodeExport::writeTypeComment(PrintFeatureType type)
|
||||
|
||||
void GCodeExport::writeLayerComment(int layer_nr)
|
||||
{
|
||||
*output_stream << ";LAYER:" << layer_nr << new_line;
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLayerCountComment(int layer_count)
|
||||
{
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
|
||||
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << new_line;
|
||||
*output_stream << line << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::resetExtrusionValue()
|
||||
{
|
||||
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
|
||||
{
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
|
||||
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
|
||||
double current_extruded_volume = getCurrentExtrudedVolume();
|
||||
extruder_attr[current_extruder].totalFilament += current_extruded_volume;
|
||||
for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
|
||||
@@ -309,7 +251,7 @@ void GCodeExport::resetExtrusionValue()
|
||||
|
||||
void GCodeExport::writeDelay(double timeAmount)
|
||||
{
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
|
||||
estimateCalculator.addTime(timeAmount);
|
||||
}
|
||||
|
||||
@@ -325,7 +267,11 @@ void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
|
||||
|
||||
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
@@ -342,11 +288,11 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
|
||||
{
|
||||
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
|
||||
//fprintf(f, "M108 S%0.1f\r\n", rpm);
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
|
||||
currentSpeed = double(rpm);
|
||||
}
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
|
||||
}
|
||||
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
|
||||
@@ -363,17 +309,17 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
|
||||
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
*output_stream << "M103" << new_line;
|
||||
*output_stream << "M103\r\n";
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
|
||||
}
|
||||
}
|
||||
*output_stream << std::setprecision(3) <<
|
||||
"G1 X" << INT2MM(gcode_pos.X) <<
|
||||
" Y" << INT2MM(gcode_pos.Y) <<
|
||||
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
|
||||
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
|
||||
@@ -396,7 +342,11 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
return;
|
||||
}
|
||||
|
||||
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
|
||||
}
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
|
||||
@@ -405,30 +355,30 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = 0;
|
||||
}
|
||||
double prime_volume = extruder_attr[current_extruder].prime_volume;
|
||||
current_e_value += mm3ToE(prime_volume);
|
||||
current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
{
|
||||
if (firmware_retract)
|
||||
{ // note that BFB is handled differently
|
||||
*output_stream << "G11" << new_line;
|
||||
*output_stream << "G11\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
if (prime_volume > 0)
|
||||
{
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
}
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
|
||||
{
|
||||
@@ -438,9 +388,9 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
}
|
||||
else if (prime_volume > 0.0)
|
||||
{
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
extruder_attr[current_extruder].prime_volume = 0.0;
|
||||
current_e_value += extrusion_per_mm * diff.vSizeMM();
|
||||
@@ -474,10 +424,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
*output_stream << " Z" << INT2MM(z + isZHopped);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
|
||||
*output_stream << new_line;
|
||||
*output_stream << "\n";
|
||||
|
||||
currentPosition = Point3(x, y, z);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
@@ -486,7 +436,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -522,19 +472,19 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
|
||||
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
|
||||
|
||||
double retraction_e_amount = mmToE(config->distance);
|
||||
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
if (firmware_retract)
|
||||
{
|
||||
*output_stream << "G10" << new_line;
|
||||
*output_stream << "G10\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value - retraction_e_amount)), 25); // TODO: hardcoded values!
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value -= retraction_e_amount;
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
|
||||
}
|
||||
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
|
||||
@@ -543,7 +493,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
if (config->zHop > 0)
|
||||
{
|
||||
isZHopped = config->zHop;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,13 +502,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
if (!extruder_attr[current_extruder].retraction_e_amount_current)
|
||||
*output_stream << "M103" << new_line;
|
||||
*output_stream << "M103\r\n";
|
||||
|
||||
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
|
||||
return;
|
||||
}
|
||||
|
||||
double retraction_e_amount = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
|
||||
double retraction_e_amount = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
|
||||
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
|
||||
{
|
||||
return;
|
||||
@@ -574,13 +524,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
|
||||
{
|
||||
return;
|
||||
}
|
||||
*output_stream << "G10 S1" << new_line;
|
||||
*output_stream << "G10 S1\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
current_e_value -= retraction_e_amount;
|
||||
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
|
||||
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
|
||||
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
|
||||
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
|
||||
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
|
||||
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
|
||||
@@ -608,11 +558,11 @@ void GCodeExport::switchExtruder(int new_extruder)
|
||||
writeCode(extruder_attr[old_extruder].end_code.c_str());
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
{
|
||||
*output_stream << "M135 T" << current_extruder << new_line;
|
||||
*output_stream << "M135 T" << current_extruder << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
*output_stream << "T" << current_extruder << new_line;
|
||||
*output_stream << "T" << current_extruder << "\n";
|
||||
}
|
||||
writeCode(extruder_attr[new_extruder].start_code.c_str());
|
||||
|
||||
@@ -622,7 +572,11 @@ void GCodeExport::switchExtruder(int new_extruder)
|
||||
|
||||
void GCodeExport::writeCode(const char* str)
|
||||
{
|
||||
*output_stream << str << new_line;
|
||||
*output_stream << str;
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
*output_stream << "\r\n";
|
||||
else
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeFanCommand(double speed)
|
||||
@@ -632,16 +586,16 @@ void GCodeExport::writeFanCommand(double speed)
|
||||
if (speed > 0)
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
else
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flavor == EGCodeFlavor::MAKERBOT)
|
||||
*output_stream << "M127 T0" << new_line;
|
||||
*output_stream << "M127 T0\n";
|
||||
else
|
||||
*output_stream << "M107" << new_line;
|
||||
*output_stream << "M107\n";
|
||||
}
|
||||
currentFanSpeed = speed;
|
||||
}
|
||||
@@ -657,7 +611,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
|
||||
*output_stream << "M104";
|
||||
if (extruder != current_extruder)
|
||||
*output_stream << " T" << extruder;
|
||||
*output_stream << " S" << temperature << new_line;
|
||||
*output_stream << " S" << temperature << "\n";
|
||||
extruder_attr[extruder].currentTemperature = temperature;
|
||||
}
|
||||
|
||||
@@ -667,15 +621,18 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
|
||||
*output_stream << "M190 S";
|
||||
else
|
||||
*output_stream << "M140 S";
|
||||
*output_stream << temperature << new_line;
|
||||
*output_stream << temperature << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::finalize(double moveSpeed, const char* endCode)
|
||||
{
|
||||
writeFanCommand(0);
|
||||
writeCode(endCode);
|
||||
log("Print time: %d\n", int(getTotalPrintTime()));
|
||||
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
|
||||
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);
|
||||
for(int n=1; n<MAX_EXTRUDERS; n++)
|
||||
if (getTotalFilamentUsed(n) > 0)
|
||||
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
|
||||
|
||||
+25
-79
@@ -40,8 +40,8 @@ public:
|
||||
class GCodePathConfig
|
||||
{
|
||||
private:
|
||||
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
|
||||
double speed; //!< current movement speed (mm/s) (modified by layer_nr etc.)
|
||||
double speed_base; //!< movement speed (mm/s) specific to this print feature
|
||||
double speed_current; //!< current movement speed (mm/s) (modified by layer_nr etc.)
|
||||
int line_width; //!< width of the line extruded
|
||||
double flow; //!< extrusion flow in %
|
||||
int layer_thickness; //!< layer height
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
RetractionConfig *const retraction_config;
|
||||
|
||||
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type) : speed_iconic(0), speed(0), line_width(0), extrusion_mm3_per_mm(0.0), type(type), spiralize(false), retraction_config(retraction_config) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, 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) {}
|
||||
|
||||
/*!
|
||||
* Initialize some of the member variables.
|
||||
@@ -61,8 +61,8 @@ public:
|
||||
*/
|
||||
void init(double speed, int line_width, double flow)
|
||||
{
|
||||
speed_iconic = speed;
|
||||
this->speed = speed;
|
||||
speed_base = speed;
|
||||
this->speed_current = speed;
|
||||
this->line_width = line_width;
|
||||
this->flow = flow;
|
||||
}
|
||||
@@ -87,15 +87,7 @@ public:
|
||||
*/
|
||||
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
|
||||
{
|
||||
speed = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the speed to the iconic speed, i.e. the normal speed of the feature type for which this is a config.
|
||||
*/
|
||||
void setSpeedIconic()
|
||||
{
|
||||
speed = speed_iconic;
|
||||
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -111,7 +103,7 @@ public:
|
||||
*/
|
||||
double getSpeed()
|
||||
{
|
||||
return speed;
|
||||
return speed_current;
|
||||
}
|
||||
|
||||
int getLineWidth()
|
||||
@@ -143,7 +135,6 @@ class GCodeExport : public NoCopy
|
||||
private:
|
||||
struct ExtruderTrainAttributes
|
||||
{
|
||||
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
|
||||
Point nozzle_offset;
|
||||
char extruderCharacter;
|
||||
std::string start_code;
|
||||
@@ -184,10 +175,8 @@ private:
|
||||
};
|
||||
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
|
||||
bool use_extruder_offset_to_offset_coords;
|
||||
|
||||
|
||||
std::ostream* output_stream;
|
||||
std::string new_line;
|
||||
|
||||
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
|
||||
Point3 currentPosition;
|
||||
double currentSpeed; //!< The current speed (F values / 60) in mm/s
|
||||
@@ -198,7 +187,7 @@ private:
|
||||
int currentFanSpeed;
|
||||
EGCodeFlavor flavor;
|
||||
|
||||
double totalPrintTime;
|
||||
double totalPrintTime; //!< The total estimated print time in seconds
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
@@ -206,61 +195,15 @@ private:
|
||||
|
||||
unsigned int layer_nr; //!< for sending travel data
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param e the value to convert
|
||||
* \return the value converted to mm
|
||||
*/
|
||||
double eToMm(double e);
|
||||
|
||||
/*!
|
||||
* Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param mm3 the value to convert
|
||||
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
|
||||
*/
|
||||
double mm3ToE(double mm3);
|
||||
|
||||
/*!
|
||||
* Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
|
||||
*
|
||||
* E values are either in mm or in mm^3
|
||||
* The current extruder is used to determine the filament area to make the conversion.
|
||||
*
|
||||
* \param mm the value to convert
|
||||
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
|
||||
*/
|
||||
double mmToE(double mm);
|
||||
|
||||
public:
|
||||
|
||||
GCodeExport();
|
||||
~GCodeExport();
|
||||
|
||||
/*!
|
||||
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
|
||||
*
|
||||
* \param print_time The total print time of the whole file (if known)
|
||||
* \param filament_used_0 The total mm^3 filament used for the primary extruder (if known)
|
||||
* \param filament_used_1 The total mm^3 filament used for the secondary extruder (if used and if known)
|
||||
* \return The string representing the file header
|
||||
*/
|
||||
std::string getFileHeader(double print_time = 666, int filament_used_0 = 666, int filament_used_1 = 0);
|
||||
|
||||
|
||||
void setLayerNr(unsigned int layer_nr);
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
int getNozzleSize(int extruder_idx);
|
||||
|
||||
|
||||
Point getExtruderOffset(int id);
|
||||
|
||||
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
|
||||
@@ -287,8 +230,21 @@ public:
|
||||
|
||||
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();
|
||||
@@ -340,7 +296,6 @@ public:
|
||||
ExtruderTrain* train = settings->getExtruderTrain(n);
|
||||
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
|
||||
|
||||
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
|
||||
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
|
||||
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
|
||||
@@ -355,15 +310,6 @@ public:
|
||||
|
||||
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
|
||||
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
|
||||
|
||||
if (flavor == EGCodeFlavor::BFB)
|
||||
{
|
||||
new_line = "\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
new_line = "\n";
|
||||
}
|
||||
}
|
||||
void finalize(double moveSpeed, const char* endCode);
|
||||
|
||||
|
||||
+13
-22
@@ -114,7 +114,6 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
{ // handle end position of the prev extruder
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
|
||||
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
|
||||
if (!end_pos_absolute)
|
||||
{
|
||||
@@ -122,18 +121,25 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
}
|
||||
else
|
||||
{
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
end_pos += extruder_offset; // absolute end pos is given as a head position
|
||||
}
|
||||
addTravel(end_pos); // + extruder_offset cause it
|
||||
}
|
||||
extruder_plans.emplace_back(extruder);
|
||||
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
|
||||
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
|
||||
extruder_plans.back().extruder = extruder;
|
||||
}
|
||||
else
|
||||
{
|
||||
extruder_plans.emplace_back(extruder);
|
||||
}
|
||||
|
||||
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
|
||||
|
||||
{ // handle starting pos of the new extruder
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
|
||||
if (!start_pos_absolute)
|
||||
{
|
||||
@@ -141,6 +147,7 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
}
|
||||
else
|
||||
{
|
||||
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
|
||||
start_pos += extruder_offset; // absolute start pos is given as a head position
|
||||
}
|
||||
lastPosition = start_pos;
|
||||
@@ -311,7 +318,6 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
|
||||
orderOptimizer.optimize();
|
||||
for (int poly_idx : orderOptimizer.polyOrder)
|
||||
{
|
||||
// addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config); // adds line as polygon; old code
|
||||
PolygonRef polygon = polygons[poly_idx];
|
||||
int start = orderOptimizer.polyStart[poly_idx];
|
||||
int end = 1 - start;
|
||||
@@ -686,39 +692,24 @@ void GCodePlanner::completeConfigs()
|
||||
|
||||
void GCodePlanner::processInitialLayersSpeedup()
|
||||
{
|
||||
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
|
||||
if (static_cast<int>(layer_nr) < initial_speedup_layers)
|
||||
{
|
||||
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
|
||||
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (static_cast<int>(layer_nr) == initial_speedup_layers)
|
||||
{
|
||||
storage.support_config.setSpeedIconic();
|
||||
storage.support_roof_config.setSpeedIconic();
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.setSpeedIconic();
|
||||
mesh.insetX_config.setSpeedIconic();
|
||||
mesh.skin_config.setSpeedIconic();
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setSpeedIconic();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after)
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
|
||||
outline = in_outline;
|
||||
}
|
||||
|
||||
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
|
||||
outline = outline.offset(infill_overlap);
|
||||
|
||||
if (outline.size() == 0)
|
||||
{
|
||||
|
||||
+2
-2
@@ -26,13 +26,13 @@ class Infill
|
||||
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
|
||||
int infill_line_width; //!< The line width of the infill lines to generate
|
||||
int line_distance; //!< The distance between two infill lines / polygons
|
||||
double infill_overlap; //!< the percentage (of infill_line_width) to overlap with the actual area within which to generate infill
|
||||
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, double infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
|
||||
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)
|
||||
|
||||
+9
-21
@@ -3,7 +3,7 @@
|
||||
#include "utils/polygonUtils.h"
|
||||
namespace cura {
|
||||
|
||||
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateInsets(SliceLayerPart* part, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
if (insetCount == 0)
|
||||
{
|
||||
@@ -16,26 +16,14 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
|
||||
part->insets.push_back(Polygons());
|
||||
if (i == 0)
|
||||
{
|
||||
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
|
||||
{
|
||||
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);
|
||||
}
|
||||
PolygonUtils::offsetSafe(part->outline, -line_width_0 / 2 - wall_0_inset, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
|
||||
} else if (i == 1)
|
||||
{
|
||||
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
} else
|
||||
{
|
||||
PolygonUtils::offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
@@ -52,11 +40,11 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
|
||||
}
|
||||
}
|
||||
|
||||
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
|
||||
void generateInsets(SliceLayer* layer, int wall_0_inset, 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], nozzle_width, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
|
||||
generateInsets(&layer->parts[partNr], wall_0_inset, 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,
|
||||
|
||||
+4
-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 nozzle_width The diameter of the hole in the nozzle
|
||||
* \param wall_0_inset The offset applied to the outer wall
|
||||
* \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 nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateInsets(SliceLayerPart* part, int wall_0_inset, 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,14 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, 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 nozzle_width The diameter of the hole in the nozzle
|
||||
* \param wall_0_inset The offset applied to the outer wall
|
||||
* \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 nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
void generateInsets(SliceLayer* layer, int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+3
-2
@@ -194,7 +194,7 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
|
||||
last_settings_object = meshgroup->meshes.back();
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
@@ -240,7 +240,8 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_train_nr++)
|
||||
int extruder_count = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
|
||||
for (extruder_train_nr = 0; extruder_train_nr < 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
|
||||
}
|
||||
|
||||
+39
-8
@@ -5,7 +5,7 @@ namespace cura
|
||||
{
|
||||
|
||||
const int vertex_meld_distance = MM2INT(0.03);
|
||||
static inline uint32_t pointHash(Point3& p)
|
||||
static inline uint32_t pointHash(const 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,12 +15,21 @@ Mesh::Mesh(SettingsBaseVirtual* parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
bool Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
{
|
||||
int vi0 = findIndexOfVertex(v0);
|
||||
int vi1 = findIndexOfVertex(v1);
|
||||
int vi2 = findIndexOfVertex(v2);
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2) return; // the face has two vertices which get assigned the same location. Don't add the face.
|
||||
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;
|
||||
}
|
||||
|
||||
int idx = faces.size(); // index of face to be added
|
||||
faces.emplace_back();
|
||||
@@ -31,6 +40,8 @@ void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
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()
|
||||
@@ -55,16 +66,24 @@ void Mesh::finish()
|
||||
}
|
||||
}
|
||||
|
||||
Point3 Mesh::min()
|
||||
Point3 Mesh::min() const
|
||||
{
|
||||
return aabb.min;
|
||||
}
|
||||
Point3 Mesh::max()
|
||||
Point3 Mesh::max() const
|
||||
{
|
||||
return aabb.max;
|
||||
}
|
||||
|
||||
int Mesh::findIndexOfVertex(Point3& v)
|
||||
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)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
|
||||
@@ -107,7 +126,7 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
|
||||
|
||||
|
||||
*/
|
||||
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const
|
||||
{
|
||||
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
|
||||
@@ -179,4 +198,16 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
return bestIdx;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
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
|
||||
|
||||
+32
-5
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "settings.h"
|
||||
#include "utils/AABB.h"
|
||||
#include "MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -65,12 +66,31 @@ public:
|
||||
|
||||
Mesh(SettingsBaseVirtual* parent); //!< initializes the settings
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
|
||||
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 clear(); //!< clears all data
|
||||
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
/*!
|
||||
* Offset the whole mesh (all vertices and the bounding box).
|
||||
@@ -84,13 +104,20 @@ 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(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
|
||||
int findIndexOfVertex(const 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);
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const;
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -152,7 +152,7 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
|
||||
*/
|
||||
void LineOrderOptimizer::optimize()
|
||||
{
|
||||
int gridSize = 5000; // the size of the cells in the hash grid.
|
||||
int gridSize = 5000; // the size of the cells in the hash grid. TODO
|
||||
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
+108
-88
@@ -43,21 +43,35 @@ SettingContainer::SettingContainer(std::string key, std::string label)
|
||||
|
||||
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
|
||||
{
|
||||
children.emplace_back(key, label, nullptr);
|
||||
children.emplace_back(key, label);
|
||||
return &children.back();
|
||||
}
|
||||
|
||||
SettingConfig::SettingConfig(std::string key, std::string label, SettingContainer* parent)
|
||||
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)
|
||||
: SettingContainer(key, label)
|
||||
, parent(parent)
|
||||
{
|
||||
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
|
||||
}
|
||||
|
||||
void SettingContainer::debugOutputAllSettings()
|
||||
void SettingContainer::debugOutputAllSettings() const
|
||||
{
|
||||
std::cerr << "CATEGORY: " << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
std::cerr << "\nCATEGORY: " << key << std::endl;
|
||||
for (const SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
@@ -69,7 +83,7 @@ bool SettingRegistry::settingExists(std::string key) const
|
||||
return settings.find(key) != settings.end();
|
||||
}
|
||||
|
||||
SettingConfig* SettingRegistry::getSettingConfig(std::string key)
|
||||
SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
|
||||
{
|
||||
auto it = settings.find(key);
|
||||
if (it == settings.end())
|
||||
@@ -85,12 +99,39 @@ 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()
|
||||
bool SettingRegistry::settingsLoaded() const
|
||||
{
|
||||
return settings.size() > 0;
|
||||
}
|
||||
@@ -135,7 +176,6 @@ int SettingRegistry::loadJSONsettings(std::string filename)
|
||||
{
|
||||
return loadJSONsettingsFromDoc(json_document, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates)
|
||||
@@ -149,83 +189,53 @@ 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"];
|
||||
if (trains.IsArray())
|
||||
SettingContainer& category_trains = getOrCreateCategory("machine_extruder_trains", trains);
|
||||
|
||||
if (trains.IsObject())
|
||||
{
|
||||
if (trains.Size() > 0 && trains[0].IsObject())
|
||||
for (rapidjson::Value::ConstMemberIterator train_iterator = trains.MemberBegin(); train_iterator != trains.MemberEnd(); ++train_iterator)
|
||||
{
|
||||
unsigned int idx = 0;
|
||||
for (auto it = trains.Begin(); it != trains.End(); ++it)
|
||||
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)
|
||||
{
|
||||
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++;
|
||||
_addSettingToContainer(&child, setting_iterator, warn_duplicates, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (trains.IsArray())
|
||||
{
|
||||
logError("Error: JSON machine_extruder_trains is not an array!\n");
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (json_document.HasMember("machine_settings"))
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
_addSettingToContainer(category_machine_settings, setting_iterator, warn_duplicates);
|
||||
_addSettingToContainer(&category, 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
_addCategory(category_iterator->name.GetString(), category_iterator->value, warn_duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +248,7 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
SettingConfig* conf = getSettingConfig(setting);
|
||||
if (!conf) //Setting could not be found.
|
||||
{
|
||||
logWarning("Trying to override unknown setting %s.", setting.c_str());
|
||||
logWarning("Trying to override unknown setting %s.\n", setting.c_str());
|
||||
continue;
|
||||
}
|
||||
_loadSettingValues(conf, override_iterator, false);
|
||||
@@ -248,27 +258,29 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
|
||||
void SettingRegistry::_addCategory(std::string cat_name, const rapidjson::Value& fields, bool warn_duplicates)
|
||||
{
|
||||
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")))
|
||||
if (!fields.IsObject())
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
|
||||
std::string label;
|
||||
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
|
||||
{
|
||||
@@ -280,12 +292,12 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson
|
||||
}
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
|
||||
SettingConfig& config = parent->getOrCreateChild(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, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool 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)
|
||||
{
|
||||
const rapidjson::Value& data = json_object_it->value;
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
@@ -316,7 +328,15 @@ void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::Gener
|
||||
} // arrays are ignored because machine_extruder_trains needs to be handled separately
|
||||
else
|
||||
{
|
||||
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
|
||||
if (data.HasMember("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());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
|
||||
+53
-16
@@ -15,6 +15,7 @@ namespace cura
|
||||
|
||||
// Forward declaration
|
||||
class SettingConfig;
|
||||
class SettingRegistry;
|
||||
|
||||
/*!
|
||||
* Setting category.
|
||||
@@ -23,10 +24,11 @@ class SettingConfig;
|
||||
class SettingContainer
|
||||
{
|
||||
friend class SettingConfig;
|
||||
friend class SettingRegistry;
|
||||
private:
|
||||
std::string key;
|
||||
std::string label;
|
||||
std::list<SettingConfig> children;
|
||||
std::list<SettingConfig> children; // must be a list cause the pointers to individual children are mapped to in SettingRegistry::settings.
|
||||
public:
|
||||
std::string getKey() const { return key; }
|
||||
std::string getLabel() const { return label; }
|
||||
@@ -53,8 +55,18 @@ public:
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void debugOutputAllSettings();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -68,9 +80,8 @@ private:
|
||||
std::string type;
|
||||
std::string default_value;
|
||||
std::string unit;
|
||||
SettingContainer* parent;
|
||||
public:
|
||||
SettingConfig(std::string key, std::string label, SettingContainer* parent);
|
||||
SettingConfig(std::string key, std::string label);
|
||||
|
||||
/*!
|
||||
* Get the SettingConfig::children.
|
||||
@@ -116,10 +127,10 @@ public:
|
||||
return unit;
|
||||
}
|
||||
|
||||
void debugOutputAllSettings()
|
||||
void debugOutputAllSettings() const
|
||||
{
|
||||
std::cerr << key << std::endl;
|
||||
for (SettingConfig& child : children)
|
||||
std::cerr << key << "(" << default_value << ")" <<std::endl;
|
||||
for (const SettingConfig& child : children)
|
||||
{
|
||||
child.debugOutputAllSettings();
|
||||
}
|
||||
@@ -131,6 +142,7 @@ 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
|
||||
{
|
||||
@@ -152,7 +164,7 @@ public:
|
||||
static SettingRegistry* getInstance() { return &instance; }
|
||||
|
||||
bool settingExists(std::string key) const;
|
||||
SettingConfig* getSettingConfig(std::string key);
|
||||
SettingConfig* getSettingConfig(std::string key) const;
|
||||
|
||||
/*!
|
||||
* Return the first category with the given key as name, or a null pointer.
|
||||
@@ -161,8 +173,25 @@ public:
|
||||
* \return The first category in the list having the \p key
|
||||
*/
|
||||
SettingContainer* getCategory(std::string key);
|
||||
|
||||
bool settingsLoaded();
|
||||
|
||||
/*!
|
||||
* 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;
|
||||
/*!
|
||||
* Load settings from a json file and all the parents it inherits from.
|
||||
*
|
||||
@@ -172,9 +201,9 @@ public:
|
||||
* \return an error code or zero of succeeded
|
||||
*/
|
||||
int loadJSONsettings(std::string filename);
|
||||
void debugOutputAllSettings()
|
||||
void debugOutputAllSettings() const
|
||||
{
|
||||
for (SettingContainer& cat : categories)
|
||||
for (const SettingContainer& cat : categories)
|
||||
{
|
||||
cat.debugOutputAllSettings();
|
||||
}
|
||||
@@ -216,9 +245,17 @@ private:
|
||||
/*!
|
||||
* \param warn_duplicates whether to warn for duplicate definitions
|
||||
*/
|
||||
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
|
||||
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
|
||||
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);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+28
-27
@@ -72,27 +72,28 @@ void SettingsBase::setSetting(std::string key, std::string value)
|
||||
}
|
||||
}
|
||||
|
||||
std::string SettingsBase::getSettingString(std::string key)
|
||||
std::string SettingsBase::getSettingString(std::string key) const
|
||||
{
|
||||
if (setting_values.find(key) != setting_values.end())
|
||||
{
|
||||
return setting_values[key];
|
||||
return setting_values.at(key);
|
||||
}
|
||||
if (parent)
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
|
||||
SettingsBase& nonConstThis = const_cast<SettingsBase&>(*this);
|
||||
if (SettingRegistry::getInstance()->settingExists(key))
|
||||
{
|
||||
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
|
||||
nonConstThis.setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
setting_values[key] = "";
|
||||
nonConstThis.setting_values[key] = "";
|
||||
cura::logError("Unregistered setting %s\n", key.c_str());
|
||||
}
|
||||
return setting_values[key];
|
||||
return setting_values.at(key);
|
||||
}
|
||||
|
||||
void SettingsMessenger::setSetting(std::string key, std::string value)
|
||||
@@ -100,7 +101,7 @@ void SettingsMessenger::setSetting(std::string key, std::string value)
|
||||
parent->setSetting(key, value);
|
||||
}
|
||||
|
||||
std::string SettingsMessenger::getSettingString(std::string key)
|
||||
std::string SettingsMessenger::getSettingString(std::string key) const
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
@@ -109,15 +110,15 @@ std::string SettingsMessenger::getSettingString(std::string key)
|
||||
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..
|
||||
@@ -133,31 +134,31 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
|
||||
}
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingAsIndex(std::string key)
|
||||
int SettingsBaseVirtual::getSettingAsIndex(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingAsCount(std::string key)
|
||||
int SettingsBaseVirtual::getSettingAsCount(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
int SettingsBaseVirtual::getSettingInMicrons(std::string key)
|
||||
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) * 1000.0;
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) / 180.0 * M_PI;
|
||||
}
|
||||
|
||||
bool SettingsBaseVirtual::getSettingBoolean(std::string key)
|
||||
bool SettingsBaseVirtual::getSettingBoolean(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "on")
|
||||
@@ -170,37 +171,37 @@ bool SettingsBaseVirtual::getSettingBoolean(std::string key)
|
||||
return num != 0;
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str());
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(1.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInPercentage(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInPercentage(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBaseVirtual::getSettingInSeconds(std::string key)
|
||||
double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
|
||||
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) const
|
||||
{
|
||||
FlowTempGraph ret;
|
||||
const char* c_str = getSettingString(key).c_str();
|
||||
@@ -258,7 +259,7 @@ FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
|
||||
}
|
||||
|
||||
|
||||
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
|
||||
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "RepRap")
|
||||
@@ -276,7 +277,7 @@ EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
|
||||
return EGCodeFlavor::REPRAP;
|
||||
}
|
||||
|
||||
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
|
||||
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "lines")
|
||||
@@ -292,7 +293,7 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
|
||||
return EFillMethod::NONE;
|
||||
}
|
||||
|
||||
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key)
|
||||
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "brim")
|
||||
@@ -302,7 +303,7 @@ EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string
|
||||
return EPlatformAdhesion::SKIRT;
|
||||
}
|
||||
|
||||
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
|
||||
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "everywhere")
|
||||
@@ -312,7 +313,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
|
||||
return ESupportType::NONE;
|
||||
}
|
||||
|
||||
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
|
||||
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "random")
|
||||
@@ -324,7 +325,7 @@ EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
|
||||
return EZSeamType::SHORTEST;
|
||||
}
|
||||
|
||||
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
|
||||
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "normal")
|
||||
@@ -336,7 +337,7 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
|
||||
return ESurfaceMode::NORMAL;
|
||||
}
|
||||
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
|
||||
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key) const
|
||||
{
|
||||
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) = 0;
|
||||
virtual std::string getSettingString(std::string key) const = 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);
|
||||
int getSettingAsCount(std::string key);
|
||||
int getSettingAsIndex(std::string key) const;
|
||||
int getSettingAsCount(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);
|
||||
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;
|
||||
|
||||
FlowTempGraph getSettingAsFlowTempGraph(std::string key);
|
||||
FlowTempGraph getSettingAsFlowTempGraph(std::string key) const;
|
||||
|
||||
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key);
|
||||
std::vector<std::pair<double, double>> getSettingAsPointVector(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);
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -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); //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
|
||||
std::string getSettingString(std::string key) const; //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
|
||||
|
||||
std::string getAllLocalSettingsString()
|
||||
std::string getAllLocalSettingsString() const
|
||||
{
|
||||
std::stringstream sstream;
|
||||
for (auto pair : setting_values)
|
||||
@@ -233,7 +233,7 @@ public:
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
void debugOutputAllLocalSettings()
|
||||
void debugOutputAllLocalSettings() const
|
||||
{
|
||||
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); //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
|
||||
std::string getSettingString(std::string key) const; //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
|
||||
};
|
||||
|
||||
|
||||
|
||||
+3
-6
@@ -42,16 +42,13 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
|
||||
if (upSkinCount == 0) upskin = Polygons();
|
||||
|
||||
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
|
||||
auto getInsidePolygons = [&part](SliceLayer& layer2)
|
||||
{
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
|
||||
result.add(part2.insets[wall_idx]);
|
||||
}
|
||||
result.add(part2.insets.back());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -80,7 +77,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
|
||||
downskin = downskin.difference(not_air); // skin overlaps with the walls
|
||||
}
|
||||
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
|
||||
if (layer_nr < static_cast<int>(storage.layers.size()) - downSkinCount && 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++)
|
||||
|
||||
@@ -64,7 +64,7 @@ SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(mes
|
||||
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
|
||||
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_extruder_nr")], PrintFeatureType::Support),
|
||||
support_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),
|
||||
max_object_height_second_to_last_extruder(-1)
|
||||
{
|
||||
@@ -161,6 +161,93 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
|
||||
}
|
||||
|
||||
|
||||
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr)
|
||||
{
|
||||
std::vector<bool> ret;
|
||||
ret.resize(meshgroup->getExtruderCount(), false);
|
||||
if (layer_nr < 0)
|
||||
{
|
||||
ret[getSettingAsIndex("adhesion_extruder_nr")] = true; // raft
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layer_nr == 0)
|
||||
{ // process brim/skirt
|
||||
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
if (skirt[extr_nr].size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ooze shield, draft shield
|
||||
|
||||
// support
|
||||
if (support.supportLayers[layer_nr].supportAreas.size() > 0)
|
||||
{
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret[getSettingAsIndex("support_extruder_nr")] = true;
|
||||
}
|
||||
}
|
||||
if (support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
if (layer.parts.size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector< bool > SliceDataStorage::getExtrudersUsed()
|
||||
{
|
||||
|
||||
std::vector<bool> ret;
|
||||
ret.resize(meshgroup->getExtruderCount(), false);
|
||||
|
||||
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
|
||||
{ // process brim/skirt
|
||||
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
|
||||
{
|
||||
if (skirt[extr_nr].size() > 0)
|
||||
{
|
||||
ret[extr_nr] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ooze shield, draft shield ..?
|
||||
|
||||
// support
|
||||
// support is presupposed to be present...
|
||||
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
|
||||
ret[getSettingAsIndex("support_extruder_nr")] = true;
|
||||
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
|
||||
|
||||
// all meshes are presupposed to actually have content
|
||||
for (SliceMeshStorage& mesh : meshes)
|
||||
{
|
||||
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -212,6 +212,21 @@ public:
|
||||
* \param include_helper_parts whether to include support and prime tower
|
||||
*/
|
||||
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts);
|
||||
|
||||
/*!
|
||||
* Get the extruder numbers of all extruders used in a given layer.
|
||||
*
|
||||
* \param layer_nr the index of the layer for which to get the extruders used (negative layer numbers indicate the raft)
|
||||
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
|
||||
*/
|
||||
std::vector<bool> getExtrudersUsed(int layer_nr);
|
||||
|
||||
/*!
|
||||
* Get the extruders used.
|
||||
*
|
||||
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
|
||||
*/
|
||||
std::vector<bool> getExtrudersUsed();
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,21 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,22 @@
|
||||
/** 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,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 LAYERPART_H
|
||||
#define LAYERPART_H
|
||||
#ifndef SLICER_LAYERPART_H
|
||||
#define SLICER_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//LAYERPART_H
|
||||
#endif//SLICER_LAYERPART_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "multiVolumes.h"
|
||||
#include "MultiVolumes.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef MULTIVOLUMES_H
|
||||
#define MULTIVOLUMES_H
|
||||
#ifndef SLICER_MULTIVOLUMES_H
|
||||
#define SLICER_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//MULTIVOLUMES_H
|
||||
#endif//SLICER_MULTIVOLUMES_H
|
||||
@@ -0,0 +1,133 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,47 @@
|
||||
/** 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,13 +1,64 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "SlicerLayer.h"
|
||||
#include "../TextureProcessor.h"
|
||||
|
||||
#include "slicer.h"
|
||||
#include "debug.h" // TODO remove
|
||||
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;
|
||||
}
|
||||
|
||||
namespace cura {
|
||||
|
||||
void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
|
||||
{
|
||||
@@ -304,6 +355,9 @@ 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();
|
||||
|
||||
@@ -316,68 +370,40 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
|
||||
ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input)
|
||||
{
|
||||
assert(layer_count > 0);
|
||||
|
||||
layers.resize(layer_count);
|
||||
|
||||
for(int32_t layer_nr = 0; layer_nr < layer_count; layer_nr++)
|
||||
ClosePolygonResult ret;
|
||||
for(unsigned int n=0; n<polygonList.size(); n++)
|
||||
{
|
||||
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 p0 = polygonList[n][polygonList[n].size()-1];
|
||||
for(unsigned int i=0; i<polygonList[n].size(); i++)
|
||||
{
|
||||
int32_t z = layer_nr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layer_nr < 0) continue;
|
||||
Point p1 = polygonList[n][i];
|
||||
|
||||
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
|
||||
//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)
|
||||
{
|
||||
//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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
|
||||
{
|
||||
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
ret.polygonIdx = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,45 @@
|
||||
/** 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
|
||||
@@ -0,0 +1,52 @@
|
||||
/** 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
|
||||
+1
-1
@@ -249,7 +249,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
|
||||
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
|
||||
{
|
||||
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
|
||||
storage.support.layer_nr_max_filled_layer = layer_idx;
|
||||
still_in_upper_empty_layers = false;
|
||||
}
|
||||
|
||||
|
||||
+47
-12
@@ -5,6 +5,7 @@
|
||||
#include "logoutput.h"
|
||||
#include "intpoint.h"
|
||||
#include <unordered_map>
|
||||
#include <functional> // std::function
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -13,22 +14,31 @@ 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 relativeHash relative to \p p.
|
||||
* Returns a point for which the hash is at a grid position of \p relative_hash relative to \p p.
|
||||
*
|
||||
* \param p The point for which to get the relative point to hash
|
||||
* \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.
|
||||
* \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.
|
||||
*/
|
||||
inline Point getRelativeForHash(const Point& p, const Point& relativeHash)
|
||||
inline Point getRelativeForHash(const Point& p, const Cellidx& relative_hash)
|
||||
{
|
||||
return p + relativeHash*squareSize;
|
||||
return p + relative_hash*squareSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +53,7 @@ private:
|
||||
* \param p The grid location to hash
|
||||
* \return the hash
|
||||
*/
|
||||
inline uint32_t pointHash_simple(const Point& p) const
|
||||
inline uint32_t pointHash_simple(const Cellidx& p) const
|
||||
{
|
||||
return p.X ^ (p.Y << 8);
|
||||
}
|
||||
@@ -55,7 +65,7 @@ private:
|
||||
*/
|
||||
inline uint32_t pointHash(const Point& point) const
|
||||
{
|
||||
Point p = point/squareSize;
|
||||
Cellidx p = point/squareSize;
|
||||
return pointHash_simple(p);
|
||||
}
|
||||
/*
|
||||
@@ -101,6 +111,10 @@ 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.
|
||||
*/
|
||||
@@ -117,9 +131,17 @@ 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 minimal number of initial buckets
|
||||
* \param initial_map_size The number of elements to be inserted
|
||||
*/
|
||||
BucketGrid2D(int squareSize, unsigned int initial_map_size = 4) : squareSize(squareSize), point2object(initial_map_size, PointHasher(squareSize)) {};
|
||||
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);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
|
||||
@@ -135,10 +157,14 @@ public:
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
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
|
||||
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
|
||||
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
|
||||
{
|
||||
ret.push_back(local_it->second);
|
||||
if (point_hasher(relative_point) == point_hasher(local_it->first))
|
||||
{
|
||||
ret.push_back(local_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,14 +185,17 @@ 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)
|
||||
bool findNearestObject(Point& p, T& nearby, std::function<bool(Point location, T& object)> precondition = no_precondition)
|
||||
{
|
||||
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
|
||||
@@ -177,6 +206,10 @@ 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)
|
||||
{
|
||||
@@ -211,6 +244,8 @@ 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
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/** 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
|
||||
@@ -23,12 +23,7 @@ float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
|
||||
{
|
||||
return M_PI * 2 + angle;
|
||||
}
|
||||
// Point ba = a - b;
|
||||
// Point bc = c - b;
|
||||
// int64_t dott = dot(ba, bc); // dot product
|
||||
// int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
// return -atan2(det, dott); // from -pi to pi
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
} // 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()
|
||||
float max() const
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(float len)
|
||||
bool testLength(float len) const
|
||||
{
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
float vSize2()
|
||||
float vSize2() const
|
||||
{
|
||||
return x*x+y*y+z*z;
|
||||
}
|
||||
|
||||
float vSize()
|
||||
float vSize() const
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
inline FPoint3 normalized()
|
||||
inline FPoint3 normalized() const
|
||||
{
|
||||
return (*this)/vSize();
|
||||
}
|
||||
|
||||
FPoint3 cross(const FPoint3& p)
|
||||
FPoint3 cross(const FPoint3& p) const
|
||||
{
|
||||
return FPoint3(
|
||||
y*p.z-z*p.y,
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
m[2][2] = 1.0;
|
||||
}
|
||||
|
||||
Point3 apply(const FPoint3& p)
|
||||
Point3 apply(const FPoint3& p) const
|
||||
{
|
||||
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()
|
||||
int32_t max() const
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(int32_t len)
|
||||
bool testLength(int32_t len) const
|
||||
{
|
||||
if (x > len || x < -len)
|
||||
return false;
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
x*p.y-y*p.x);
|
||||
}
|
||||
|
||||
int64_t dot(const Point3& p)
|
||||
int64_t dot(const Point3& p) const
|
||||
{
|
||||
return x*p.x + y*p.y + z*p.z;
|
||||
}
|
||||
|
||||
@@ -146,16 +146,25 @@ public:
|
||||
|
||||
if (ax_size < 0)
|
||||
{// b is 'before' segment ac
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = -1; }
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = -1;
|
||||
}
|
||||
return vSize2(ab);
|
||||
}
|
||||
if (ax_size > ac_size)
|
||||
{// b is 'after' segment ac
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = 1; }
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = 1;
|
||||
}
|
||||
return vSize2(b - c);
|
||||
}
|
||||
|
||||
if (b_is_beyond_ac) { *b_is_beyond_ac = 0; }
|
||||
if (b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = 0;
|
||||
}
|
||||
Point ax = ac * ax_size / ac_size;
|
||||
Point bx = ab - ax;
|
||||
return vSize2(bx);
|
||||
|
||||
+3
-14
@@ -213,7 +213,7 @@ public:
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
* Smoothing is performed by removing line segments smaller than \p remove_length
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
@@ -223,26 +223,15 @@ public:
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.polygon;
|
||||
if (size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
}
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
Point& last = thiss[poly_idx - 1];
|
||||
Point& now = thiss[poly_idx];
|
||||
Point& next = thiss[(poly_idx + 1) % size()];
|
||||
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
|
||||
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
|
||||
{
|
||||
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
|
||||
if (poly_idx < size())
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
} else poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-7
@@ -65,6 +65,12 @@ class TestResults():
|
||||
self._testsuites.append(suite)
|
||||
return suite
|
||||
|
||||
def getFailureCount(self):
|
||||
result = 0
|
||||
for testsuite in self._testsuites:
|
||||
result += testsuite.getFailureCount()
|
||||
return result
|
||||
|
||||
## Save the test results to the file given in the filename.
|
||||
def saveXML(self, filename):
|
||||
xml = ElementTree.Element("testsuites")
|
||||
@@ -330,9 +336,9 @@ def main(engine, model_path):
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Slicing took: %f" % (time.time() - t))
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="CuraEngine testing script")
|
||||
parser.add_argument("--simple", action="store_true", help="Only run the single test, exit")
|
||||
parser.add_argument("json", type=str, help="Machine JSON file to use")
|
||||
parser.add_argument("engine", type=str, help="Engine executable")
|
||||
parser.add_argument("models", type=str, nargs="+", help="List of models to use for testing")
|
||||
@@ -340,10 +346,13 @@ if __name__ == "__main__":
|
||||
|
||||
et = EngineTest(args.json, args.engine, args.models)
|
||||
if et.testDefaults() == 0:
|
||||
et.testSingleChanges()
|
||||
if et.testSingleRandom() == 0:
|
||||
et.testDualRandom()
|
||||
if et.testAllRandom(10) == 0:
|
||||
et.testAllRandom(100)
|
||||
if not args.simple:
|
||||
et.testSingleChanges()
|
||||
if et.testSingleRandom() == 0:
|
||||
et.testDualRandom()
|
||||
if et.testAllRandom(10) == 0:
|
||||
et.testAllRandom(100)
|
||||
et.getResults().saveXML("output.xml")
|
||||
|
||||
if args.simple:
|
||||
if et.getResults().getFailureCount() > 0:
|
||||
sys.exit(1)
|
||||
|
||||
-19658
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,220 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "BucketGrid2DTest.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(BucketGrid2DTest);
|
||||
|
||||
void BucketGrid2DTest::setUp()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::tearDown()
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsFarTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(0, 100);
|
||||
const Point target(100, 100);
|
||||
std::unordered_set<Point> near;
|
||||
std::unordered_set<Point> far;
|
||||
far.emplace(0, 100);
|
||||
findNearbyObjectsAssert(input, target, 10, near, far);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsLine2Test()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
for (long long x = 0; x < 200; x++)
|
||||
{
|
||||
input.emplace_back(x, 95);
|
||||
}
|
||||
const Point target(99, 100); //Slightly shifted.
|
||||
const unsigned long long grid_size = 10;
|
||||
std::unordered_set<Point> near;
|
||||
std::unordered_set<Point> far;
|
||||
for (const Point point : input)
|
||||
{
|
||||
unsigned long long distance = vSize(point - target);
|
||||
if (distance < grid_size)
|
||||
{
|
||||
near.insert(point);
|
||||
}
|
||||
else if (distance > grid_size * 2) //Grid size * 2 are guaranteed to be considered "far".
|
||||
{
|
||||
far.insert(point);
|
||||
}
|
||||
}
|
||||
findNearbyObjectsAssert(input, target, grid_size, near, far);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsLineTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
for (long long x = 0; x < 200; x++)
|
||||
{
|
||||
input.emplace_back(x, 95);
|
||||
}
|
||||
const Point target(100, 100);
|
||||
const unsigned long long grid_size = 10;
|
||||
std::unordered_set<Point> near;
|
||||
std::unordered_set<Point> far;
|
||||
for (const Point point : input)
|
||||
{
|
||||
unsigned long long distance = vSize(point - target);
|
||||
if (distance < grid_size)
|
||||
{
|
||||
near.insert(point);
|
||||
}
|
||||
else if (distance > grid_size * 2) //Grid size * 2 are guaranteed to be considered "far".
|
||||
{
|
||||
far.insert(point);
|
||||
}
|
||||
}
|
||||
findNearbyObjectsAssert(input, target, grid_size, near, far);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsNearTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(95, 100);
|
||||
const Point target(100, 100);
|
||||
std::unordered_set<Point> near;
|
||||
near.emplace(95, 100);
|
||||
std::unordered_set<Point> far;
|
||||
findNearbyObjectsAssert(input, target, 10, near, far);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsSameTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(100, 100);
|
||||
const Point target(100, 100);
|
||||
std::unordered_set<Point> near;
|
||||
near.emplace(100, 100);
|
||||
std::unordered_set<Point> far;
|
||||
findNearbyObjectsAssert(input, target, 10, near, far);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectChoiceTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(95, 100);
|
||||
input.emplace_back(103, 100);
|
||||
input.emplace_back(200, 100);
|
||||
findNearestObjectAssert(input, Point(100, 100), 10, new Point(103, 100));
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectEqualTest()
|
||||
{
|
||||
std::vector<Point> registered_points;
|
||||
registered_points.emplace_back(95, 100);
|
||||
registered_points.emplace_back(105, 100);
|
||||
Point target = Point(100, 100);
|
||||
const unsigned long long grid_size = 10;
|
||||
const Point expected1 = Point(95, 100);
|
||||
const Point expected2 = Point(105, 100);
|
||||
|
||||
BucketGrid2D<Point> grid(grid_size);
|
||||
for (Point point : registered_points)
|
||||
{
|
||||
grid.insert(point, point);
|
||||
}
|
||||
|
||||
Point result;
|
||||
const bool success = grid.findNearestObject(target, result, BucketGrid2D<Point>::no_precondition); //The acutal call to test.
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "findNearestObject returned " << success << " but should've returned true.";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), success);
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "findNearestObject reported the nearest point to be " << result << " (distance " << vSize(target - result) << "), but it should've been " << expected1 << " (distance " << vSize(expected1 - target) << ") or " << expected2 << " (distance " << vSize(expected2 - target) << ").";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), result == expected1 || result == expected2);
|
||||
}
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectFilterTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(95, 100);
|
||||
input.emplace_back(98, 100);
|
||||
input.emplace_back(106, 100);
|
||||
std::function<bool(Point, Point&)> filter = [&] (Point position, Point& object) -> bool { return position.X > 100; };
|
||||
findNearestObjectAssert(input, Point(100, 100), 10, new Point(106, 100), filter);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectNoneTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
findNearestObjectAssert(input, Point(100, 100), 10, nullptr);
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectSameTest()
|
||||
{
|
||||
std::vector<Point> input;
|
||||
input.emplace_back(100, 100);
|
||||
findNearestObjectAssert(input, Point(100, 100), 10, new Point(100, 100));
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearbyObjectsAssert(const std::vector<Point>& registered_points, Point target, const unsigned long long grid_size, const std::unordered_set<Point>& expected_near, const std::unordered_set<Point>& expected_far)
|
||||
{
|
||||
BucketGrid2D<Point> grid(grid_size);
|
||||
for(Point point : registered_points)
|
||||
{
|
||||
grid.insert(point, point);
|
||||
}
|
||||
|
||||
const std::vector<Point> result = grid.findNearbyObjects(target); //The actual call to test.
|
||||
|
||||
//Note that the result may contain the same point more than once. This test is robust against that.
|
||||
for (const Point point : expected_near) //Are all near points reported as near?
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Point " << point << " is near " << target << " (distance " << vSize(point - target) << "), but findNearbyObjects didn't report it as such. Grid size: " << grid_size;
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::find(result.begin(), result.end(), point) != result.end()); //Must be in result.
|
||||
}
|
||||
for (const Point point : expected_far) //Are all far points NOT reported as near?
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Point " << point << " is far from " << target << " (distance " << vSize(point - target) << "), but findNearbyObjects thought it was near. Grid size: " << grid_size;
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::find(result.begin(), result.end(), point) == result.end()); //Must not be in result.
|
||||
}
|
||||
}
|
||||
|
||||
void BucketGrid2DTest::findNearestObjectAssert(const std::vector<Point>& registered_points, Point target, const unsigned long long grid_size, Point* expected, std::function<bool(Point location, Point& object)> precondition)
|
||||
{
|
||||
BucketGrid2D<Point> grid(grid_size);
|
||||
for (Point point : registered_points)
|
||||
{
|
||||
grid.insert(point, point);
|
||||
}
|
||||
|
||||
Point result;
|
||||
const bool success = grid.findNearestObject(target, result, precondition); //The acutal call to test.
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "findNearestObject returned " << success << " but should've returned " << (expected != nullptr) << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), success == (expected != nullptr));
|
||||
}
|
||||
if (expected)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "findNearestObject reported the nearest point to be " << result << " (distance " << vSize(target - result) << "), but it was " << *expected << " (distance " << vSize(*expected - target) << ").";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), result == *expected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//Copyright (c) 2015 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#ifndef BUCKETGRID2DTEST_H
|
||||
#define BUCKETGRID2DTEST_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <../src/utils/intpoint.h> //For Point.
|
||||
#include <../src/utils/BucketGrid2D.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class BucketGrid2DTest : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(BucketGrid2DTest);
|
||||
CPPUNIT_TEST(findNearbyObjectsFarTest);
|
||||
CPPUNIT_TEST(findNearbyObjectsLine2Test);
|
||||
CPPUNIT_TEST(findNearbyObjectsLineTest);
|
||||
CPPUNIT_TEST(findNearbyObjectsNearTest);
|
||||
CPPUNIT_TEST(findNearbyObjectsSameTest);
|
||||
CPPUNIT_TEST(findNearestObjectChoiceTest);
|
||||
CPPUNIT_TEST(findNearestObjectEqualTest);
|
||||
CPPUNIT_TEST(findNearestObjectFilterTest);
|
||||
CPPUNIT_TEST(findNearestObjectNoneTest);
|
||||
CPPUNIT_TEST(findNearestObjectSameTest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets up the test suite to prepare for testing.
|
||||
*/
|
||||
void setUp();
|
||||
|
||||
/*!
|
||||
* \brief Tears down the test suite when testing is done.
|
||||
*/
|
||||
void tearDown();
|
||||
|
||||
//These are the actual test cases. The name of the function sort of describes what it tests but I refuse to document all of these, sorry.
|
||||
void findNearbyObjectsFarTest();
|
||||
void findNearbyObjectsLine2Test();
|
||||
void findNearbyObjectsLineTest();
|
||||
void findNearbyObjectsNearTest();
|
||||
void findNearbyObjectsSameTest();
|
||||
void findNearestObjectChoiceTest();
|
||||
void findNearestObjectEqualTest();
|
||||
void findNearestObjectFilterTest();
|
||||
void findNearestObjectNoneTest();
|
||||
void findNearestObjectSameTest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief The maximum allowed error in distance measurements.
|
||||
*/
|
||||
static const int64_t maximum_error = 10;
|
||||
|
||||
/*!
|
||||
* \brief Performs the actual assertion for the findNearbyObjects tests.
|
||||
*
|
||||
* This is essentially a parameterised version of all unit tests pertaining
|
||||
* to the findNearbyObjects tests.
|
||||
* It tests for some points whether they are near and for some whether they
|
||||
* are far. This also allows for points to be indeterminate, where either
|
||||
* answer is allowed.
|
||||
*
|
||||
* \param registered_points The points already in the grid, from which a
|
||||
* subset must be found that is near the target point.
|
||||
* \param target The target point, near which we must find points.
|
||||
* \param grid_size The grid size of the BucketGrid2D to use.
|
||||
* \param expected_near The expected set of points which is near.
|
||||
* \param expected_far The expected set of points which is far.
|
||||
*/
|
||||
void findNearbyObjectsAssert(const std::vector<Point>& registered_points, Point target, unsigned long long grid_size, const std::unordered_set<Point>& expected_near, const std::unordered_set<Point>& expected_far);
|
||||
|
||||
/*!
|
||||
* \brief Performs the actual assertion for the findNearestObject tests.
|
||||
*
|
||||
* This is essentially a parameterised version of all unit tests pertaining
|
||||
* to the findNearestObject tests.
|
||||
*
|
||||
* \param registered_points The points already in the grid, from which the
|
||||
* nearest point must be selected.
|
||||
* \param target The target point, to which the nearest point must be
|
||||
* selected.
|
||||
* \param grid_size The grid size of the BucketGrid2D to use.
|
||||
* \param expected The expected closest point, or <em>nullptr</em> if the
|
||||
* call is expected to return <em>false</em>.
|
||||
* \param precondition A boolean function on Points to filter by. Leave this
|
||||
* parameter out if you don't wish to filter.
|
||||
*/
|
||||
void findNearestObjectAssert(const std::vector<Point>& registered_points, Point target, const unsigned long long grid_size, Point* expected, std::function<bool(Point location, Point& object)> precondition = BucketGrid2D<Point>::no_precondition);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LINEARALG2DTEST_H
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário