Comparar commits

...

87 Commits

Autor SHA1 Mensagem Data
Tim Kuipers ecd832744c fix: raft outline now uses rounded offset (CURA-1835) 2016-07-04 17:19:40 +02:00
Tim Kuipers 03b654af3e feat: layer_0_z_overlap (CURA-1549) 2016-05-11 17:15:28 +02:00
Tim Kuipers c79a7f1819 fix: spiralize would leave the z at the next layer for the next polygon on the same layer (CURA-1541) 2016-05-11 12:57:47 +02:00
Tim Kuipers e01f18c7d4 lil: doc 2016-05-11 08:52:06 +02:00
Tim Kuipers 42891874f4 fix: multiple polygons & spiralize resulted in extrusions where travels should have been (CURA-1513) 2016-05-10 17:37:16 +02:00
Tim Kuipers 257d6a6635 fix: spiralize each polygon of a spiralized mesh even though that might cause a print head crash (CURA-1443) 2016-05-10 15:58:34 +02:00
Tim Kuipers bec8bef455 fix: consecutive spiralize paths are handled as one (CURA-1443 CURA-1541) 2016-05-10 15:32:09 +02:00
Tim Kuipers 4efeaa7083 fix: let bool spiralize bubble down from FffGcodeWriter into GCodePath instead of going via the GCodePathConfig (CURA-1443)
Because of the LayerPlanBuffer the GCodePathConfig changed a couple of layers too early, resulting in spiralization in the first layers which include the bottom skin.
2016-05-10 15:14:50 +02:00
Tim Kuipers 95fd9d6685 lil: doc 2016-05-10 13:25:11 +02:00
Tim Kuipers 2db37c6018 fix: erase with impossible iterator when trying to simplify an empty polygon (CURA-1430) 2016-05-02 18:28:00 +02:00
Tim Kuipers 41b0966d26 fix: constructor prime tower called incorrectly 2016-05-02 17:33:00 +02:00
Tim Kuipers 93485cd0df Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-04-06 17:09:16 +02:00
Tim Kuipers 2d3382874a fix: polygon smoothing removed vertices which were connected to a small as well as a large line segment (CURA-1361) 2016-04-06 17:09:05 +02:00
Tim Kuipers 1e78397e18 fix: support max layer was set after each object (CURA-1360) 2016-04-06 17:05:33 +02:00
Ghostkeeper 6bcdd94f7e Copy protocol file from front-end
This synchronises the whitespace and comments from the front-end. The typeids are not necessary any more.

Contributes to issue CURA-1210.
2016-04-05 17:14:09 +02:00
Ghostkeeper accd28db64 Update documentation of sendLayerData
Contributes to issue CURA-1210.
2016-04-05 17:13:57 +02:00
Ghostkeeper 1c0f4c42d9 Remove superfluous set to nullptr
Shared pointers are already set to nullptr by default.

Contributes to issue CURA-1210.
2016-04-05 17:13:47 +02:00
Ghostkeeper 5da1632d9f Remove sliced object lists from protocol
Each layer is now sent individually, instead of grouped by object and grouping those objects in a sliced object list. The list was sometimes too large to send in one message. The objects weren't used by the front-end anyway.

Contributes to issue CURA-1210.
2016-04-05 17:13:37 +02:00
Ghostkeeper cbf1152f56 Remove object ID from layer
Turns out that it is not needed in the front-end either any more. If we need it in the future, we'll add it again.

Contributes to issue CURA-1210.
2016-04-05 17:13:22 +02:00
Ghostkeeper 2273c5aefe Alter protocol to no longer group sliced objects
They will be sent with one message per layer from now on.

Contributes to issue CURA-1210.
2016-04-05 17:13:09 +02:00
Tim Kuipers 46c793e73d fix: lil string vs c_str bug (CURA-1231) 2016-04-04 14:15:14 +02:00
Tim Kuipers fd4969887b refactor: replaced all gcode output \n by new_line (CURA-1231) 2016-04-04 14:13:32 +02:00
Tim Kuipers 6620a050a5 feat: new_line string used for BFB machines (CURA-1231) 2016-04-04 14:13:10 +02:00
Tim Kuipers 6325197fce feat/refactor: moved file header generation to gcodeExport and introduced the NOZZLE_SIZE header comment (CURA-1231) 2016-04-04 14:03:38 +02:00
Tim Kuipers f828d44365 Merge branch 'feature_better_time_estimates' into 2.1 2016-04-04 12:14:19 +02:00
Tim Kuipers 29564a23e0 refactor: introduced mm, mm3, E value conversion functions instead of inline is_volumetric checks (CURA-1293) 2016-03-30 12:35:11 +02:00
Tim Kuipers 1fdda3319f fix: volumetric time estimation was bugged (CURA-1293) 2016-03-30 12:24:27 +02:00
sean041 be113eceb4 Fix typo. downSkinCount -> upSkinCount (CURA-1299)
This typo causes flaky crash when downSkinCount < upSkinCount and ignore small z gaps is disabled.
2016-03-30 10:24:58 +02:00
Tim Kuipers 0c42ff9bfa fix: inner (2nd) wall was refered to even if it wasn't generated (CURA-1294) 2016-03-29 15:18:47 +02:00
Tim Kuipers 168e041c42 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-03-24 15:58:16 +01:00
Ghostkeeper 670ae6dd8c Spaces around minus operator
Conforming to code style.

Contributes to issue CURA-863.
2016-03-24 15:58:11 +01:00
Tim Kuipers 20adfa751f doc+refactor: fan speed calc more clear (CURA-863) 2016-03-24 15:57:54 +01:00
Tim Kuipers bf8776b112 optimization: removed superluous recalculation of line direction in LineOrderOptimizer (CURA-1170) 2016-03-23 16:25:10 +01:00
Tim Kuipers 1d0f3f519a fix syntax mistake (CURA-1170) 2016-03-23 16:25:01 +01:00
Tim Kuipers 07fef8668c calculate incoming_perpundicular_normal in end stage of line order optimizer (CURA-1170) 2016-03-23 16:24:51 +01:00
Tim Kuipers 1c06fc49fc refactor: factor out getAngleScore from line order optimizer (CURA-1170) 2016-03-23 16:24:39 +01:00
Tim Kuipers beb9422d9b refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-23 16:24:27 +01:00
Tim Kuipers a8359b9a68 refactor: small optimization of line order optimizer dot score (CURA-1170) 2016-03-23 16:24:17 +01:00
Tim Kuipers 5ccfe2d1aa refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-23 16:24:07 +01:00
Tim Kuipers 74577759b4 refactor: expand complicated code in pathOrderOptimizer (CURA-1170) 2016-03-23 16:23:54 +01:00
Tim Kuipers 45eb026777 refactor: rewrite line order optimizer dot score stuff (CURA-1170) 2016-03-23 16:23:42 +01:00
Tim Kuipers aabb07fd81 refactor: simple renaming of incoming_perpendicular_normal (CURA-1170)
Conflicts:
	src/pathOrderOptimizer.cpp
2016-03-23 16:23:21 +01:00
Ghostkeeper 6377ec63e1 Add edge-case tests for getAngleLeft
These test what happens when two or more points are equal. There is nothing about this in the function specification, so it allows any output, but at least it shouldn't give like a divide by zero error.

Contributes to issue CURA-1170.
2016-03-23 16:14:11 +01:00
Tim Kuipers eab2d8e667 fix: improved dot-score for preferring the z-seam on inside corners (CURA-1170) 2016-03-23 16:13:58 +01:00
Tim Kuipers 8d41003c67 feat: linearAlg2D::getAngleLeft CMAKE (CURA-1170) 2016-03-23 16:13:48 +01:00
Tim Kuipers ecfae4d75c feat: linearAlg2D::getAngleLeft (CURA-1170) 2016-03-23 16:13:37 +01:00
Tim Kuipers a2208f6b69 fix: pathOrderOptimizer was bugged (CURA-1170)
polyStart indices used wrongly
2016-03-23 16:13:26 +01:00
Tim Kuipers bacacb01dc refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-23 16:13:15 +01:00
Tim Kuipers 94c9399f2c refactor: intpoint::crossZ ==> turn90CCW (CURA-1170) 2016-03-23 16:13:03 +01:00
Tim Kuipers 168dc3c12b refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-23 16:12:53 +01:00
Tim Kuipers 235af65b00 refactor: code cleanup helper functions of PathOrderOptimizer (CURA-1170) 2016-03-23 16:12:43 +01:00
Tim Kuipers f3f3be74cc refactor: code cleanup of PathOrderOptimizer - lines (CURA-1170) 2016-03-23 16:12:32 +01:00
Tim Kuipers dca0bc80b5 fix: PathOrderOptimizar::polyStart had wrong indexing (CURA-1170) 2016-03-23 16:12:18 +01:00
Tim Kuipers c0e57622d0 cleanup: more clarification of pathOrderOptimizer for polygons code (CURA-1170) 2016-03-23 16:12:04 +01:00
Tim Kuipers b7a8fbe798 cleanup: pathOrderOptimizer got cleaned up (CURA-1170) 2016-03-23 16:11:49 +01:00
Tim Kuipers 4353980e78 refactor: made code more explicit: after smoothing speed (towards bottom layer speed) the speeds are set to their iconic speeds once and for all (CURA-1248) 2016-03-22 17:20:38 +01:00
Tim Kuipers 18ae9cf41d bugfix: alternate extra wall had skin overlapping with inner wall (CURA-1233)
To see if there was infill above, we looked at the innermost wall instead of the wall with index [wall line count]
2016-03-22 13:42:32 +01:00
Tim Kuipers 04edf35331 bugfix: bottom layer speed influenced all layers (CURA-1248)
smoothSpeed never got called for the final speed
2016-03-22 13:21:03 +01:00
Tim Kuipers 6718a8b2f3 fix: made engine not depend on support_z_distance, which is a parent setting value (CURA-1171) 2016-03-16 09:36:05 +01:00
Ghostkeeper 08a5ec7dee Codestyle: Whitespace around binary operators
Contributes to issue CURA-1097.
2016-03-14 16:45:56 +01:00
Ghostkeeper 3de01763c1 Codestyle: Whitespace around binary operators.
Contributes to issue CURA-1097.
2016-03-14 16:41:34 +01:00
Tim Kuipers bc7ee74d19 made combing depend on inner wall line width instead of nozzle_size (CURA-1097) 2016-03-14 11:02:32 +01:00
Tim Kuipers fc20a6661b made rafts' combing not depend on nozzle_size (CURA-1097) 2016-03-14 10:37:46 +01:00
Tim Kuipers 81d521a58b quickfix: remove outer wall offset based on nozzle size (CURA-1097) 2016-03-10 09:56:18 +01:00
Tim Kuipers c4eb1d9f27 bugfix: uninitialised infill (CURA-1084)
not always did the basic infill_area get initialised, notably when there was no infill, which should be an empty polygon instead of no polygon
2016-03-08 17:52:12 +01:00
Tim Kuipers 32d1bb6d75 bugfix: one-at-a-time printing went to z height of last position planned on the previous object (CURA-988) 2016-03-02 13:55:46 +01:00
Tim Kuipers afdb552f63 bugfix: retraction limitation segfaulted when retraction count == 1, cause it handled retraction count as if it was [retraction count - 1] (CURA-977) 2016-03-01 13:18:53 +01:00
Tim Kuipers a400ba28f2 fix: supportOnByuildplateOnly didn't account for conical support (CURA-914) 2016-03-01 12:06:54 +01:00
Tim Kuipers e0a7818d9e fix: infill wipe distance also on skin (CURA-964) 2016-02-29 13:03:42 +01:00
Tim Kuipers 277b5dce75 fix: get infill sparse combine per mesh (CURA-949) 2016-02-29 12:21:11 +01:00
Tim Kuipers 462a6e8c16 bugfix: skin didn't overlap with walls (CURA-941) 2016-02-29 12:16:02 +01:00
Tim Kuipers c20d35e293 fix: used adress comparison on ListPolygons instead of in depth comparison (CURA-934) 2016-02-23 16:23:09 +01:00
Tim Kuipers 9e56841cd2 fix: explicitly deleted polygon comparison, cause it is inefficient, and you most likely need to compare either the memory adress or need to account for the fact that two different polygons might have a different starting point (CURA-934) 2016-02-23 16:21:30 +01:00
Tim Kuipers c39e43c161 bugfix: combing shortcuts to end of comb move (CURA-893) 2016-02-23 15:38:33 +01:00
Tim Kuipers 421a6d4095 removed line from wrong cherry-pick (CURA-894) 2016-02-23 15:38:23 +01:00
Tim Kuipers 8497e46542 bugfix: moveInside always moved in the positive direction for the corner case (CURA-579) 2016-02-23 15:33:04 +01:00
Tim Kuipers 6510ebbd92 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-02-22 17:27:06 +01:00
Tim Kuipers c1b4a5398b bugfix: small zgaps no-heuristic bugfixes (CURA-921)
It didn't create the right amount of top and bottom layers and didn't work correctly on the topmost and bottom most layers.
2016-02-22 17:26:57 +01:00
Arjen Hiemstra 11c4b9339a Store layer messages in a hash map to speed up lookups for layers 2016-02-22 17:23:17 +01:00
Tim Kuipers 75efbac68e fix: cone angle sign inverted: more sensible fix (CURA-869) 2016-02-22 17:08:04 +01:00
Tim Kuipers cd199dc43e fix: cone angle sign inverted (CURA-869) 2016-02-22 16:50:27 +01:00
Ghostkeeper 2372a78c9b Fix setting-crash test with function evaluation
The minimum value, maximum value, minimum warning value and maximum warning value of each setting is now evaluated as a function, preventing casting errors. Runtest.py runs without errors or even test failures again now.

Contributes to issue CURA-814.
2016-02-15 12:54:44 +01:00
Tim Kuipers 142f4d519f Merge branch 'bugfix_coasting_prime' into 2.1 2016-02-08 16:28:50 +01:00
Arjen Hiemstra 9ea43e7fc1 Move addListener call to before message registration
This makes it possible to receive debug information about message registration
2016-02-07 18:56:28 +01:00
Arjen Hiemstra 9b92de9b8b Add an Arcus::SocketListener subclass so we can log all errors and debug 2016-02-04 12:16:30 +01:00
Tim Kuipers f0f14b0be3 fix: prime too slow after coasting due to unset speed (CURA-796) 2016-01-27 17:40:04 +01:00
Tim Kuipers a82c00bead fix: prime too much after coasting (CURA-796) 2016-01-27 17:39:37 +01:00
33 arquivos alterados com 901 adições e 436 exclusões
+1
Ver Arquivo
@@ -77,6 +77,7 @@ set(engine_SRCS # Except main.cpp.
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/utils/gettime.cpp
src/utils/LinearAlg2D.cpp
src/utils/logoutput.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
+3 -26
Ver Arquivo
@@ -2,20 +2,18 @@ syntax = "proto3";
package cura.proto;
message ObjectList
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
}
// typeid 1
message Slice
{
repeated ObjectList object_lists = 1;
}
message Object
message Object
{
int64 id = 1;
bytes vertices = 2; //An array of 3 floats.
@@ -24,28 +22,13 @@ message Object
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
}
// typeid 3
message Progress
message Progress
{
float amount = 1;
}
// typeid 2
message SlicedObjectList
{
repeated SlicedObject objects = 1;
}
message SlicedObject
{
int64 id = 1;
repeated Layer layers = 2;
}
message Layer {
int32 id = 1;
float height = 2;
float thickness = 3;
@@ -70,20 +53,16 @@ message Polygon {
float line_width = 3;
}
// typeid 4
message GCodeLayer {
int64 id = 1;
bytes data = 2;
}
// typeid 5
message ObjectPrintTime {
int64 id = 1;
float time = 2;
float material_amount = 3;
}
// typeid 6
message SettingList {
repeated Setting settings = 1;
}
@@ -94,11 +73,9 @@ message Setting {
bytes value = 2;
}
// typeid 7
message GCodePrefix {
bytes data = 2;
}
// typeid 8
message SlicingFinished {
}
+43 -37
Ver Arquivo
@@ -12,7 +12,7 @@ namespace cura
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
PrimeTower primetower();
PrimeTower primetower;
gcode.preSetup(storage.meshgroup);
@@ -178,15 +178,8 @@ void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
{
if (!CommandSocket::isInstantiated())
{
std::ostringstream prefix;
prefix << "FLAVOR:" << toString(gcode.getFlavor());
gcode.writeComment(prefix.str().c_str());
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
gcode.writeComment("TIME:666");
gcode.writeComment("MATERIAL:666");
gcode.writeComment("MATERIAL2:-1");
}
std::string prefix = gcode.getFileHeader();
gcode.writeCode(prefix.c_str());
}
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
{
@@ -238,7 +231,8 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
gcode.resetExtrusionValue();
gcode.setZ(max_object_height + 5000);
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
gcode.writeMove(last_position_planned, getSettingInMillimetersPerSecond("speed_travel"), 0);
}
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
@@ -275,7 +269,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_nr = -n_raft_surface_layers - 2;
int layer_height = getSettingInMicrons("raft_base_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
@@ -303,7 +298,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_nr = -n_raft_surface_layers - 1;
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (CommandSocket::isInstantiated())
@@ -329,7 +325,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
{ // raft surface layers
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (CommandSocket::isInstantiated())
@@ -361,8 +358,10 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
{
layer_thickness = getSettingInMicrons("layer_height_0");
}
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(current_extruder_planned)->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
ExtruderTrain* current_extruder_train = storage.meshgroup->getExtruderTrain(current_extruder_planned);
int64_t comb_offset_from_outlines = current_extruder_train->getSettingInMicrons((current_extruder_train->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 2; // TODO: only used when there is no second wall.
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
@@ -501,11 +500,10 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
{
polygons.add(layer->parts[partNr].outline);
}
if (mesh->getSettingBoolean("magic_spiralize"))
mesh->inset0_config.spiralize = true;
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config);
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, mesh->getSettingBoolean("magic_spiralize"));
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
}
@@ -593,12 +591,14 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
skin_angle -= 45;
int64_t skin_overlap = 0;
int64_t skin_overlap = infill_overlap;
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons("machine_nozzle_size") * 1);
{
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
}
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -659,12 +659,17 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
if (mesh->getSettingAsCount("wall_line_count") > 0)
{
bool spiralize = false;
if (mesh->getSettingBoolean("magic_spiralize"))
{
if (static_cast<int>(layer_nr) >= mesh->getSettingAsCount("bottom_layers"))
mesh->inset0_config.spiralize = true;
{
spiralize = true;
}
if (static_cast<int>(layer_nr) == mesh->getSettingAsCount("bottom_layers") && part.insets.size() > 0)
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config);
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, nullptr, EZSeamType::SHORTEST, false);
}
}
for(int inset_number=part.insets.size()-1; inset_number>-1; inset_number--)
{
@@ -672,13 +677,13 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
{
if (!compensate_overlap)
{
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type);
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type, spiralize);
}
else
{
Polygons& outer_wall = part.insets[0];
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type);
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, spiralize);
}
}
else
@@ -739,7 +744,15 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
}
else
{
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
}
// handle gaps between perimeters etc.
@@ -754,7 +767,7 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
}
}
@@ -933,15 +946,8 @@ void FffGcodeWriter::finalize()
{
if (CommandSocket::isInstantiated())
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
}
CommandSocket::getInstance()->sendGCodePrefix(prefix.str());
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
CommandSocket::getInstance()->sendGCodePrefix(prefix);
}
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
+8 -6
Ver Arquivo
@@ -24,9 +24,6 @@ namespace cura
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->beginSendSlicedObject();
if (!sliceModel(meshgroup, timeKeeper, storage))
{
return false;
@@ -118,7 +115,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
meshStorage.getSettingInMicrons("raft_base_thickness")
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap");
+ meshStorage.getSettingInMicrons("raft_airgap")
- meshStorage.getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
{
layer.printZ += meshStorage.getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
}
}
@@ -199,9 +201,9 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
}
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
for(SliceMeshStorage& mesh : storage.meshes)
{
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
}
@@ -439,7 +441,7 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
{
int r = rand() % (fuzziness * 2) - fuzziness;
Point perp_to_p0p1 = crossZ(p0p1);
Point perp_to_p0p1 = turn90CCW(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
result.add(pa);
+1 -1
Ver Arquivo
@@ -103,7 +103,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->endSendSlicedObject();
CommandSocket::getInstance()->sendLayerData();
}
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
+2 -2
Ver Arquivo
@@ -141,7 +141,7 @@ bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point
(a + b) / 2;
second_middle = (c + d) / 2;
Point dir_vector_perp = crossZ(second_middle - first_middle);
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0)
{
@@ -167,7 +167,7 @@ bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
Point ac = c - first_middle;
Point infill_vector_perp = crossZ(infill_vector);
Point infill_vector_perp = turn90CCW(infill_vector);
int64_t perp_proj = dot(ac, infill_vector_perp);
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
+6 -6
Ver Arquivo
@@ -550,7 +550,7 @@ void Wireframe2gcode::processStartingCode()
{
if (!CommandSocket::isInstantiated())
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
gcode.writeCode(gcode.getFileHeader().c_str());
}
}
else
@@ -600,14 +600,14 @@ void Wireframe2gcode::processSkirt()
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
{
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
PolygonRef poly = skirt[actual_poly_idx];
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
unsigned int poly_idx = order.polyOrder[poly_order_idx];
PolygonRef poly = skirt[poly_idx];
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
}
}
+1
Ver Arquivo
@@ -385,6 +385,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
}
}
}
optimized_comb_path.push_back(comb_path.back());
return true;
}
+52 -56
Ver Arquivo
@@ -7,6 +7,8 @@
#include <cinttypes>
#include <Arcus/Socket.h>
#include <Arcus/SocketListener.h>
#include <Arcus/Error.h>
#include <string> // stoi
@@ -25,31 +27,48 @@ namespace cura {
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
class Listener : public Arcus::SocketListener
{
public:
void stateChanged(Arcus::SocketState::SocketState newState) override
{
}
void messageReceived() override
{
}
void error(const Arcus::Error & error) override
{
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
{
log("%s\n", error.toString().c_str());
}
else
{
logError("%s\n", error.toString().c_str());
}
}
};
class CommandSocket::Private
{
public:
Private()
: socket(nullptr)
, object_count(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
cura::proto::Layer* getLayerById(int id);
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
// Message that holds a list of sliced objects
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
// Message that holds the currently sliced object (to be added to sliced_object_list)
cura::proto::SlicedObject* current_sliced_object;
// Number of sliced objects for this sliced object list
int sliced_objects;
@@ -57,15 +76,14 @@ public:
// Used for incrementing the current layer in one at a time mode
int current_layer_count;
int current_layer_offset;
// Ids of the sliced objects
std::vector<int64_t> object_ids;
std::string temp_gcode_file;
std::ostringstream gcode_output_stream;
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
};
CommandSocket::CommandSocket()
@@ -92,9 +110,11 @@ bool CommandSocket::isInstantiated()
void CommandSocket::connect(const std::string& ip, int port)
{
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::SlicedObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
@@ -104,14 +124,14 @@ void CommandSocket::connect(const std::string& ip, int port)
private_data->socket->connect(ip, port);
log("Connecting to %s:%i", ip.c_str(), port);
log("Connecting to %s:%i\n", ip.c_str(), port);
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
log("Connected to %s:%i", ip.c_str(), port);
log("Connected to %s:%i\n", ip.c_str(), port);
bool slice_another_time = true;
@@ -137,7 +157,6 @@ 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);
@@ -169,15 +188,10 @@ void CommandSocket::connect(const std::string& ip, int port)
//sendPrintTime();
}
if(private_data->socket->getLastError().isValid())
{
logError("%s\n", private_data->socket->getLastError().toString().c_str());
private_data->socket->clearError();
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
log("Closing connection\n");
private_data->socket->close();
}
@@ -255,7 +269,6 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
mesh.setSetting(setting.name(), setting.value());
}
private_data->object_ids.push_back(object.id());
mesh.finish();
}
@@ -273,25 +286,17 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
if(!private_data->current_sliced_object)
{
return;
}
cura::proto::Layer* layer = private_data->getLayerById(layer_nr);
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
}
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
{
if(!private_data->current_sliced_object)
return;
if (polygons.size() == 0)
return;
cura::proto::Layer* proto_layer = private_data->getLayerById(layer_nr);
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
@@ -335,31 +340,22 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
// socket.sendFloat32(print_time);
}
void CommandSocket::beginSendSlicedObject()
{
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]);
}
void CommandSocket::endSendSlicedObject()
void CommandSocket::sendLayerData()
{
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
std::cout << "End sliced object called. Sliced objects " << private_data->sliced_objects << " object count: " << private_data->object_count << std::endl;
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
if(private_data->sliced_objects >= private_data->object_count)
{
private_data->socket->sendMessage(private_data->sliced_object_list);
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
private_data->sliced_objects = 0;
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_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);
}
@@ -379,7 +375,6 @@ void CommandSocket::beginGCode()
void CommandSocket::flushGcode()
{
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);
@@ -393,22 +388,23 @@ void CommandSocket::sendGCodePrefix(std::string prefix)
private_data->socket->sendMessage(message);
}
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
auto itr = sliced_layers.find(id);
cura::proto::Layer* layer = nullptr;
if(itr != current_sliced_object->mutable_layers()->end())
std::shared_ptr<cura::proto::Layer> layer;
if(itr != sliced_layers.end())
{
layer = &(*itr);
layer = itr->second;
}
else
{
layer = current_sliced_object->add_layers();
layer = std::make_shared<cura::proto::Layer>();
layer->set_id(id);
current_layer_count++;
sliced_layers[id] = layer;
}
return layer;
+5 -7
Ver Arquivo
@@ -82,16 +82,14 @@ 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();
/*!
* Conclude the slicing of the current meshgroup, so that we can start the next
* Send the sliced layer data to the GUI.
*
* The GUI may use this to visualise the g-code, so that the user can
* inspect the result of slicing.
*/
void endSendSlicedObject();
void sendLayerData();
/*!
* \brief Sends a message to indicate that all the slicing is done.
+113 -68
Ver Arquivo
@@ -29,6 +29,23 @@ 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_;
}
@@ -39,6 +56,11 @@ void GCodeExport::setOutputStream(std::ostream* stream)
*output_stream << std::fixed;
}
int GCodeExport::getNozzleSize(int extruder_idx)
{
return extruder_attr[extruder_idx].nozzle_size;
}
Point GCodeExport::getExtruderOffset(int id)
{
return extruder_attr[id].nozzle_offset;
@@ -133,6 +155,42 @@ double GCodeExport::getCurrentExtrudedVolume()
}
}
double GCodeExport::eToMm(double e)
{
if (is_volumatric)
{
return e / extruder_attr[current_extruder].filament_area;
}
else
{
return e;
}
}
double GCodeExport::mm3ToE(double mm3)
{
if (is_volumatric)
{
return mm3;
}
else
{
return mm3 / extruder_attr[current_extruder].filament_area;
}
}
double GCodeExport::mmToE(double mm)
{
if (is_volumatric)
{
return mm * extruder_attr[current_extruder].filament_area;
}
else
{
return mm;
}
}
double GCodeExport::getTotalFilamentUsed(int e)
{
@@ -176,12 +234,12 @@ void GCodeExport::writeComment(std::string comment)
*output_stream << comment[i];
}
}
*output_stream << "\n";
*output_stream << new_line;
}
void GCodeExport::writeTypeComment(const char* type)
{
*output_stream << ";TYPE:" << type << "\n";
*output_stream << ";TYPE:" << type << new_line;
}
void GCodeExport::writeTypeComment(PrintFeatureType type)
@@ -189,25 +247,25 @@ void GCodeExport::writeTypeComment(PrintFeatureType type)
switch (type)
{
case PrintFeatureType::OuterWall:
*output_stream << ";TYPE:WALL-OUTER\n";
*output_stream << ";TYPE:WALL-OUTER" << new_line;
break;
case PrintFeatureType::InnerWall:
*output_stream << ";TYPE:WALL-INNER\n";
*output_stream << ";TYPE:WALL-INNER" << new_line;
break;
case PrintFeatureType::Skin:
*output_stream << ";TYPE:SKIN\n";
*output_stream << ";TYPE:SKIN" << new_line;
break;
case PrintFeatureType::Support:
*output_stream << ";TYPE:SUPPORT\n";
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::Skirt:
*output_stream << ";TYPE:SKIRT\n";
*output_stream << ";TYPE:SKIRT" << new_line;
break;
case PrintFeatureType::Infill:
*output_stream << ";TYPE:FILL\n";
*output_stream << ";TYPE:FILL" << new_line;
break;
case PrintFeatureType::SupportInfill:
*output_stream << ";TYPE:SUPPORT\n";
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::MoveCombing:
case PrintFeatureType::MoveRetraction:
@@ -220,24 +278,24 @@ void GCodeExport::writeTypeComment(PrintFeatureType type)
void GCodeExport::writeLayerComment(int layer_nr)
{
*output_stream << ";LAYER:" << layer_nr << "\n";
*output_stream << ";LAYER:" << layer_nr << new_line;
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << "\n";
*output_stream << line << new_line;
}
void GCodeExport::resetExtrusionValue()
{
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
double current_extruded_volume = getCurrentExtrudedVolume();
extruder_attr[current_extruder].totalFilament += current_extruded_volume;
for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
@@ -251,7 +309,7 @@ void GCodeExport::resetExtrusionValue()
void GCodeExport::writeDelay(double timeAmount)
{
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
estimateCalculator.addTime(timeAmount);
}
@@ -267,11 +325,7 @@ void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
Point gcode_pos = getGcodePos(x,y, current_extruder);
@@ -288,11 +342,11 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
@@ -309,17 +363,17 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!extruder_attr[current_extruder].retraction_e_amount_current)
{
*output_stream << "M103\r\n";
*output_stream << "M103" << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
}
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
@@ -342,11 +396,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
return;
}
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
Point gcode_pos = getGcodePos(x,y, current_extruder);
@@ -355,30 +405,30 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
isZHopped = 0;
}
double prime_volume = extruder_attr[current_extruder].prime_volume;
current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
current_e_value += mm3ToE(prime_volume);
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
if (firmware_retract)
{ // note that BFB is handled differently
*output_stream << "G11\n";
*output_stream << "G11" << new_line;
//Assume default UM2 retraction settings.
if (prime_volume > 0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0);
}
else
{
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
{
@@ -388,10 +438,9 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
}
else if (prime_volume > 0.0)
{
current_e_value += prime_volume;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
extruder_attr[current_extruder].prime_volume = 0.0;
current_e_value += extrusion_per_mm * diff.vSizeMM();
@@ -425,10 +474,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
*output_stream << " Z" << INT2MM(z + isZHopped);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
*output_stream << "\n";
*output_stream << new_line;
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
@@ -437,7 +486,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
return;
}
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
{
return;
}
@@ -449,7 +498,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) >= config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
{
// extruder switch could have introduced data which falls outside the retraction window
// also the retraction_count_max could have changed between the last retraction and this
@@ -459,13 +508,13 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
return;
}
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].filament_area)
{
return;
}
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max)
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
@@ -473,19 +522,19 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
double retraction_e_amount = mmToE(config->distance);
if (firmware_retract)
{
*output_stream << "G10\n";
*output_stream << "G10" << new_line;
//Assume default UM2 retraction settings.
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value - retraction_e_amount)), 25); // TODO: hardcoded values!
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
@@ -494,7 +543,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
if (config->zHop > 0)
{
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
}
}
@@ -503,13 +552,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
if (flavor == EGCodeFlavor::BFB)
{
if (!extruder_attr[current_extruder].retraction_e_amount_current)
*output_stream << "M103\r\n";
*output_stream << "M103" << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
return;
}
double retraction_e_amount = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
double retraction_e_amount = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
{
return;
@@ -525,13 +574,13 @@ void GCodeExport::writeRetraction_extruderSwitch()
{
return;
}
*output_stream << "G10 S1\n";
*output_stream << "G10 S1" << new_line;
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
@@ -559,11 +608,11 @@ void GCodeExport::switchExtruder(int new_extruder)
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
{
*output_stream << "M135 T" << current_extruder << "\n";
*output_stream << "M135 T" << current_extruder << new_line;
}
else
{
*output_stream << "T" << current_extruder << "\n";
*output_stream << "T" << current_extruder << new_line;
}
writeCode(extruder_attr[new_extruder].start_code.c_str());
@@ -573,11 +622,7 @@ void GCodeExport::switchExtruder(int new_extruder)
void GCodeExport::writeCode(const char* str)
{
*output_stream << str;
if (flavor == EGCodeFlavor::BFB)
*output_stream << "\r\n";
else
*output_stream << "\n";
*output_stream << str << new_line;
}
void GCodeExport::writeFanCommand(double speed)
@@ -587,16 +632,16 @@ void GCodeExport::writeFanCommand(double speed)
if (speed > 0)
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
}
else
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M127 T0\n";
*output_stream << "M127 T0" << new_line;
else
*output_stream << "M107\n";
*output_stream << "M107" << new_line;
}
currentFanSpeed = speed;
}
@@ -612,7 +657,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
*output_stream << "M104";
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << "\n";
*output_stream << " S" << temperature << new_line;
extruder_attr[extruder].currentTemperature = temperature;
}
@@ -622,7 +667,7 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << "\n";
*output_stream << temperature << new_line;
}
void GCodeExport::finalize(double moveSpeed, const char* endCode)
+84 -9
Ver Arquivo
@@ -48,11 +48,17 @@ private:
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
public:
PrintFeatureType type; //!< name of the feature type
bool spiralize;
RetractionConfig *const retraction_config;
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type) : speed_iconic(0), speed(0), line_width(0), extrusion_mm3_per_mm(0.0), type(type), spiralize(false), retraction_config(retraction_config) {}
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type)
: speed_iconic(0)
, speed(0)
, line_width(0)
, extrusion_mm3_per_mm(0.0)
, type(type)
, retraction_config(retraction_config)
{
}
/*!
* Initialize some of the member variables.
@@ -90,6 +96,14 @@ public:
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;
}
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
@@ -135,6 +149,7 @@ class GCodeExport : public NoCopy
private:
struct ExtruderTrainAttributes
{
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string start_code;
@@ -170,13 +185,15 @@ private:
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
, last_retraction_prime_speed(1.0)
, last_retraction_prime_speed(0.0)
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
bool use_extruder_offset_to_offset_coords;
std::ostream* output_stream;
std::string new_line;
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
Point3 currentPosition;
double currentSpeed; //!< The current speed (F values / 60) in mm/s
@@ -195,15 +212,61 @@ 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);
@@ -229,7 +292,7 @@ public:
void setFilamentDiameter(unsigned int n, int diameter);
double getCurrentExtrudedVolume();
double getTotalFilamentUsed(int e);
double getTotalPrintTime();
@@ -283,6 +346,7 @@ public:
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
@@ -291,10 +355,21 @@ public:
extruder_attr[n].extruder_switch_retraction_distance = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
}
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
}
else
{
new_line = "\n";
}
}
void finalize(double moveSpeed, const char* endCode);
+109 -52
Ver Arquivo
@@ -22,10 +22,10 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
return *this;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow)
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow) // spiralize can only change when a travel path is in between
return &paths.back();
paths.emplace_back();
GCodePath* ret = &paths.back();
@@ -33,6 +33,7 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceF
ret->config = config;
ret->done = false;
ret->flow = flow;
ret->spiralize = spiralize;
ret->space_fill_type = space_fill_type;
if (config != &storage.travel_config)
{
@@ -235,7 +236,7 @@ void GCodePlanner::addTravel(Point p)
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path->retract = true;
@@ -257,26 +258,26 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
getLatestPathWithConfig(config, space_fill_type, flow)->points.push_back(p);
getLatestPathWithConfig(config, space_fill_type, flow, spiralize)->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation)
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, bool spiralize)
{
Point p0 = polygon[startIdx];
addTravel(p0);
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[startIdx];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
}
else
{
@@ -284,30 +285,36 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type)
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, bool spiralize)
{
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
if (polygons.size() == 0)
{
int nr = orderOptimizer.polyOrder[i];
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config, wall_overlap_computation);
return;
}
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
orderOptimizer.addPolygon(polygons[poly_idx]);
}
orderOptimizer.optimize();
for (unsigned int poly_idx : orderOptimizer.polyOrder)
{
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, spiralize);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
{
LineOrderOptimizer orderOptimizer(lastPosition);
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
{
int nr = orderOptimizer.polyOrder[i];
// addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
PolygonRef polygon = polygons[nr];
int start = orderOptimizer.polyStart[nr];
orderOptimizer.addPolygon(polygons[line_idx]);
}
orderOptimizer.optimize();
for (int poly_idx : orderOptimizer.polyOrder)
{
// addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config); // adds line as polygon; old code
PolygonRef polygon = polygons[poly_idx];
int start = orderOptimizer.polyStart[poly_idx];
int end = 1 - start;
Point& p0 = polygon[start];
addTravel(p0);
@@ -445,7 +452,21 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
/*
min layer time
:
: min layer time fan speed min
| : :
^ max..|__: :
| \ :
fan | \ :
speed min..|... \:___________
|________________
layer time >
*/
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
fan_speed = fsml.cool_fan_speed_min;
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
@@ -456,8 +477,25 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
fan_speed = fsml.cool_fan_speed_max - (fsml.cool_fan_speed_max-fsml.cool_fan_speed_min) * (totalLayerTime - fsml.cool_min_layer_time) / (fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time);
double fan_speed_diff = fsml.cool_fan_speed_max - fsml.cool_fan_speed_min;
double layer_time_diff = fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time;
double fraction_of_slope = (totalLayerTime - fsml.cool_min_layer_time) / layer_time_diff;
fan_speed = fsml.cool_fan_speed_max - fan_speed_diff * fraction_of_slope;
}
/*
Supposing no influence of minimal layer time; i.e. layer time > min layer time fan speed min:
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
zero..|/__________________
layer nr >
*/
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
@@ -529,22 +567,18 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
if (point_idx == path.points.size() - 1)
{
gcode.setZ(z); // go down to extrusion level when we spiralized before on this layer
gcode.writeMove(gcode.getPositionXY(), speed, path.getExtrusionMM3perMM());
}
}
continue;
}
bool spiralize = path.config->spiralize;
if (spiralize)
{
//Check if we are the last spiralize path in the list, if not, do not spiralize.
for(unsigned int m=path_idx+1; m<paths.size(); m++)
{
if (paths[m].config->spiralize)
spiralize = false;
}
}
bool spiralize = path.spiralize;
if (!spiralize) // normal (extrusion) move (with coasting
{
{
CoastingConfig& coasting_config = storage.coasting_config[extruder];
bool coasting = coasting_config.coasting_enable;
if (coasting)
@@ -582,26 +616,34 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{ // SPIRALIZE
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
float totalLength = 0.0;
int z = gcode.getPositionZ();
Point p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path.points.size(); i++)
for (unsigned int _path_idx = path_idx; _path_idx < paths.size() && !paths[_path_idx].isTravelPath(); _path_idx++)
{
Point p1 = path.points[i];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
GCodePath& _path = paths[_path_idx];
for (unsigned int point_idx = 0; point_idx < _path.points.size(); point_idx++)
{
Point p1 = _path.points[point_idx];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
}
}
float length = 0.0;
p0 = gcode.getPositionXY();
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
Point p1 = path.points[point_idx];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
for (; path_idx < paths.size() && paths[path_idx].spiralize; path_idx++)
{ // handle all consecutive spiralized paths > CHANGES path_idx!
GCodePath& path = paths[path_idx];
for (unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
Point p1 = path.points[point_idx];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path
}
}
@@ -649,24 +691,39 @@ void GCodePlanner::completeConfigs()
void GCodePlanner::processInitialLayersSpeedup()
{
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
if (static_cast<int>(layer_nr) < initial_speedup_layers)
{
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(SliceMeshStorage& mesh : storage.meshes)
for (SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.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)
+32 -5
Ver Arquivo
@@ -156,7 +156,9 @@ public:
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
bool isTravelPath()
@@ -283,9 +285,10 @@ private:
* \param config The config used for the path returned
* \param space_fill_type The type of space filling which this path employs
* \param flow (optional) A ratio for the extrusion speed
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
@@ -408,12 +411,36 @@ public:
* \param config The config with which to extrude
* \param space_fill_type Of what space filling type this extrusion move is a part
* \param flow A modifier of the extrusion width which would follow from the \p config
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
*/
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
/*!
* Add polygon to the gcode starting at vertex \p startIdx
* \param polygon The polygon
* \param startIdx The index of the starting vertex of the \p polygon
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
*/
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST);
/*!
* Add polygons to the gcode with optimized order.
*
* When \p spiralize is true, each polygon will gradually increase from a z corresponding to this layer to the z corresponding to the next layer.
* Doing this for each polygon means there is a chance for the print head to crash into already printed parts,
* but doing it for the last polygon only would mean you are printing half of the layer in non-spiralize mode,
* while each layer starts with a different part.
* Two towers would result in alternating spiralize and non-spiralize layers.
*
* \param polygons The polygons
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param z_seam_type The seam type / poly start optimizer
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
*/
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
/*!
* Add lines to the gcode with optimized order.
+1 -4
Ver Arquivo
@@ -208,10 +208,7 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
outline = in_outline;
}
if (line_distance > infill_line_width * 3 / 2)
{ // infill is not too dense to have overlap with surrounding polygon
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
}
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
if (outline.size() == 0)
{
+2 -2
Ver Arquivo
@@ -16,7 +16,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
part->insets.push_back(Polygons());
if (i == 0)
{
if (line_width_0 < nozzle_width)
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
{
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
@@ -26,7 +26,7 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
}
} else if (i == 1)
{
if (line_width_0 < nozzle_width)
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
{
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
+96 -105
Ver Arquivo
@@ -2,6 +2,7 @@
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/linearAlg2D.h"
#define INLINE static inline
@@ -15,17 +16,16 @@ void PathOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
float dist = vSize2f(poly[point_idx] - startPoint);
if (dist < bestDist)
{
best = i_point;
best = point_idx;
bestDist = dist;
}
}
@@ -37,46 +37,50 @@ void PathOrderOptimizer::optimize()
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
{
int best = -1;
int best_poly_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
continue;
}
assert (polygons[i_polygon].size() != 2);
assert (polygons[poly_idx].size() != 2);
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
if (dist < bestDist)
{
best = i_polygon;
best_poly_idx = poly_idx;
bestDist = dist;
}
}
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
{
assert(polygons[best].size() != 2);
assert(polygons[best_poly_idx].size() != 2);
prev_point = polygons[best][polyStart[best]];
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
picked[best] = true;
polyOrder.push_back(best);
picked[best_poly_idx] = true;
polyOrder.push_back(best_poly_idx);
}
else
{
logError("Failed to find next closest polygon.\n");
}
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
{
int poly_idx = polyOrder[n];
int poly_idx = polyOrder[order_idx];
int point_idx = getPolyStart(prev_point, poly_idx);
polyStart[poly_idx] = point_idx;
prev_point = polygons[poly_idx][point_idx];
@@ -99,22 +103,23 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
float best_point_score = std::numeric_limits<float>::infinity();
Point p0 = poly.back();
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
float dist = vSize2f(poly[i_point] - prev_point);
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
Point& p1 = poly[point_idx];
Point& p2 = poly[(point_idx + 1) % poly.size()];
int64_t dist = vSize2(p1 - prev_point);
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
// this score is in the order of 5 mm
if (dist + is_on_inside_corner_score < best_point_score)
{
best_point_idx = i_point;
bestDist = dist;
best_point_idx = point_idx;
best_point_score = dist + is_on_inside_corner_score;
}
p0 = p1;
}
return best_point_idx;
}
@@ -152,127 +157,113 @@ void LineOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
int best_point_idx = -1;
float best_point_dist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
float dist = vSize2f(poly[point_idx] - startPoint);
if (dist < best_point_dist)
{
best = i_point;
bestDist = dist;
best_point_idx = point_idx;
best_point_dist = dist;
}
}
polyStart.push_back(best);
polyStart.push_back(best_point_idx);
assert(poly.size() == 2);
line_bucket_grid.insert(poly[0], i_polygon);
line_bucket_grid.insert(poly[1], i_polygon);
line_bucket_grid.insert(poly[0], poly_idx);
line_bucket_grid.insert(poly[1], poly_idx);
}
Point incommingPerpundicularNormal(0, 0);
Point incoming_perpundicular_normal(0, 0);
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
int best_line_idx = -1;
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
{
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
{
continue;
}
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
}
if (best == -1) /// if single-line-polygon hasn't been found yet
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
{
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
continue;
assert(polygons[i_polygon].size() == 2);
}
assert(polygons[poly_idx].size() == 2);
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
}
}
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
{
assert(polygons[best].size() == 2);
PolygonRef best_line = polygons[best_line_idx];
assert(best_line.size() == 2);
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
prev_point = polygons[best][endIdx];
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
int line_start_point_idx = polyStart[best_line_idx];
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
Point& line_start = best_line[line_start_point_idx];
Point& line_end = best_line[line_end_point_idx];
prev_point = line_end;
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
picked[best] = true;
polyOrder.push_back(best);
picked[best_line_idx] = true;
polyOrder.push_back(best_line_idx);
}
else
logError("Failed to find next closest line.\n");
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int nr = polyOrder[n];
PolygonRef poly = polygons[nr];
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i=0;i<poly.size(); i++)
{
float dist = vSize2f(polygons[nr][i] - prev_point);
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
{
best = i;
bestDist = dist + dot_score;
}
logError("Failed to find next closest line.\n");
}
polyStart[nr] = best;
assert(poly.size() == 2);
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
}
}
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
{
Point& p0 = polygons[poly_idx][0];
Point& p1 = polygons[poly_idx][1];
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
{ /// check distance to first point on line (0)
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 0;
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 0;
}
}
{ /// check distance to second point on line (1)
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 1;
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 1;
}
}
}
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
{
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
}
}//namespace cura
+28 -1
Ver Arquivo
@@ -81,8 +81,35 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
/*!
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
*
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
*
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
* in which case the travel move might involve combing, which makes it rather longer.
*
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
* \param best[in, out] The index of current best line
* \param best_score[in, out] The distance score for the current best line
* \param prev_point[in] The previous point from which to find the next best line
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
*/
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
/*!
* Get a score to modify the distance score for measuring how good two lines follow each other.
*
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
*
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
* \param from The one end of the next line
* \param to The other end of the next line
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
*
*/
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
};
}//namespace cura
+5 -3
Ver Arquivo
@@ -1,4 +1,6 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <clipper/clipper.hpp>
#include "raft.h"
#include "support.h"
@@ -8,15 +10,15 @@ void generateRaft(SliceDataStorage& storage, int distance)
{
if (storage.draft_protection_shield.size() > 0)
{
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound));
}
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
{
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound));
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
}
}
+10 -6
Ver Arquivo
@@ -42,13 +42,16 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
if (upSkinCount == 0) upskin = Polygons();
auto getInsidePolygons = [&part](SliceLayer& layer2)
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
{
Polygons result;
for(SliceLayerPart& part2 : layer2.parts)
{
if (part.boundaryBox.hit(part2.boundaryBox))
result.add(part2.insets.back());
{
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
result.add(part2.insets[wall_idx]);
}
}
return result;
};
@@ -67,20 +70,20 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
}
else
{
if (layer_nr > 0 && downSkinCount > 0)
if (layer_nr >= downSkinCount && downSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
}
@@ -142,6 +145,7 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_e
{
if (int(part.insets.size()) < wall_line_count)
{
part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
+2
Ver Arquivo
@@ -65,6 +65,8 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
*
* The walls should already be generated.
*
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
*
* \param layerNr The index of the layer for which to generate the infill
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
* \param innermost_wall_extrusion_width width of the innermost wall lines
+31 -10
Ver Arquivo
@@ -94,7 +94,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
int join_distance = mesh.getSettingInMicrons("support_join_distance");
@@ -120,15 +119,17 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
// derived settings:
if (conical_support_angle == 0)
{
conical_support = false;
}
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
// derived settings:
int supportLayerThickness = layerThickness;
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
@@ -136,12 +137,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int64_t conical_support_offset;
if (conical_support_angle > 0)
{
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
{ // outward ==> wider base than overhang
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
}
else
{
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
{ // inward ==> smaller base than overhang
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
}
unsigned int support_layer_count = layer_count;
@@ -248,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 = layer_idx;
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
still_in_upper_empty_layers = false;
}
@@ -263,6 +264,26 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
{
Polygons& supportLayer = supportAreas[layer_idx];
if (conical_support)
{ // with conical support the next layer is allowed to be larger than the previous
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
//
// conical support can make
// layer above layer below
// v v
// | : |
// | ==> : |__
// |____ :....
//
// a miter limit would result in
// | : : |
// | :.. <== : |__
// .\___ :....
//
}
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
supportAreas[layer_idx] = touching_buildplate;
+34
Ver Arquivo
@@ -0,0 +1,34 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "linearAlg2D.h"
#include <cmath> // atan2
#include "intpoint.h" // dot
namespace cura
{
float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
{
Point ba = a - b;
Point bc = c - b;
int64_t dott = dot(ba, bc); // dot product
int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
float angle = -atan2(det, dott); // from -pi to pi
if (angle >= 0 )
{
return angle;
}
else
{
return M_PI * 2 + angle;
}
// Point ba = a - b;
// Point bc = c - b;
// int64_t dott = dot(ba, bc); // dot product
// int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
// return -atan2(det, dott); // from -pi to pi
}
} // namespace cura
+1 -1
Ver Arquivo
@@ -202,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
return p0 * len / _len;
}
INLINE Point crossZ(const Point& p0)
INLINE Point turn90CCW(const Point& p0)
{
return Point(-p0.Y, p0.X);
}
+19
Ver Arquivo
@@ -181,6 +181,25 @@ public:
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
}
/*!
* Compute the angle between two consecutive line segments.
*
* The angle is computed from the left side of b when looking from a.
*
* c
* \ .
* \ b
* angle|
* |
* a
*
* \param a start of first line segment
* \param b end of first segment and start of second line segment
* \param c end of second line segment
* \return the angle in radians between 0 and 2 * pi of the corner in \p b
*/
static float getAngleLeft(const Point& a, const Point& b, const Point& c);
};
+7 -1
Ver Arquivo
@@ -162,7 +162,13 @@ void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_d
}
polygon->erase(polygon->begin() + writing_idx , polygon->end());
}
if (size() < 3)
{
clear();
return;
}
Point* last = &thiss[0];
unsigned int writing_idx = 1;
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
+23 -8
Ver Arquivo
@@ -65,7 +65,9 @@ public:
}
PolygonRef& operator=(const PolygonRef& other) { polygon = other.polygon; return *this; }
bool operator==(const PolygonRef& other) const =delete;
ClipperLib::Path& operator*() { return *polygon; }
template <typename... Args>
@@ -211,7 +213,7 @@ public:
/*!
* Smooth out the polygon and store the result in \p result.
* Smoothing is performed by removing line segments smaller than \p remove_length
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
*
* \param remove_length The length of the largest segment removed
* \param result (output) The result polygon, assumed to be empty
@@ -221,15 +223,26 @@ public:
PolygonRef& thiss = *this;
ClipperLib::Path* poly = result.polygon;
if (size() > 0)
{
poly->push_back(thiss[0]);
}
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
{
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
Point& last = thiss[poly_idx - 1];
Point& now = thiss[poly_idx];
Point& next = thiss[(poly_idx + 1) % size()];
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
{
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
if (poly_idx < size())
{
poly->push_back(thiss[poly_idx]);
} else poly->push_back(thiss[poly_idx]);
}
}
else
{
poly->push_back(thiss[poly_idx]);
}
}
}
@@ -348,6 +361,9 @@ public:
Polygons(const Polygons& other) { polygons = other.polygons; }
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
bool operator==(const Polygons& other) const =delete;
Polygons difference(const Polygons& other) const
{
Polygons ret;
@@ -391,13 +407,12 @@ public:
clipper.Execute(ClipperLib::ctXor, ret.polygons);
return ret;
}
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const
{
Polygons ret;
double miterLimit = 1.2;
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
clipper.MiterLimit = miterLimit;
clipper.MiterLimit = miter_limit;
clipper.Execute(ret.polygons, distance);
return ret;
}
+11 -11
Ver Arquivo
@@ -70,8 +70,8 @@ Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int poi
Point p1 = poly[point_idx];
Point p2 = poly[(point_idx < (poly.size() - 1)) ? (point_idx + 1) : 0];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
Point n = normal(off0 + off1, -offset);
return p1 + n;
@@ -85,7 +85,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
unsigned int bestPoly = NO_INDEX;
bool is_inside = false;
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
PolygonRef poly = polygons[poly_idx];
@@ -125,9 +125,10 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
is_inside = dot(inward_dir, p - x) >= 0;
Point inward_dir = turn90CCW(normal(ab, MM2INT(10.0)) + normal(p1 - p0, MM2INT(10.0))); // inward direction irrespective of sign of [distance]
// MM2INT(10.0) to retain precision for the eventual normalization
ret = x + normal(inward_dir, distance);
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) * distance >= 0;
}
}
}
@@ -147,7 +148,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
continue;
}
else
{
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
projected_p_beyond_prev_segment = false;
Point x = a + ab * ax_length / ab_length;
@@ -159,9 +160,9 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = crossZ(normal(ab, distance));
Point inward_dir = turn90CCW(normal(ab, distance)); // inward or outward depending on the sign of [distance]
ret = x + inward_dir;
is_inside = dot(inward_dir, p - x) >= 0;
is_already_on_correct_side_of_boundary = dot(inward_dir, p - x) >= 0;
}
}
}
@@ -171,7 +172,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
p1 = p2;
}
}
if (is_inside)
if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
{
if (bestDist2 < distance * distance)
{
@@ -191,7 +192,6 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
return NO_INDEX;
}
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
{
PolygonRef poly1 = poly1_result.poly;
+2 -2
Ver Arquivo
@@ -79,7 +79,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
Point& last_point = *last_it;
Point& point = *it;
if ( from_it.poly == to_list_poly
if (&from_it.poly == &to_list_poly
&& (
(from_it.it == last_it || from_it.it == it) // we currently consider a linesegment directly connected to [from]
|| (from_it.prev().it == it || from_it.next().it == last_it) // line segment from [last_point] to [point] is connected to line segment of which [from] is the other end
@@ -94,7 +94,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
int64_t dist2 = vSize2(closest - from);
if (dist2 > line_width * line_width
|| ( from_it.poly == to_list_poly
|| (&from_it.poly == &to_list_poly
&& dot(from_it.next().p() - from, point - last_point) > 0
&& dot(from - from_it.prev().p(), point - last_point) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
)
+10 -1
Ver Arquivo
@@ -74,7 +74,16 @@ class WallOverlapComputation
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
: poly(poly), it(it) { }
Point& p() const { return *it; }
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.it; }
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
* \param other The ListPolyIt to test for equality
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
*/
bool operator==(const ListPolyIt& other) const
{
return &poly == &other.poly && it == other.it;
}
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
//!< move the iterator forward (and wrap around at the end)
ListPolyIt& operator++()
+52
Ver Arquivo
@@ -215,4 +215,56 @@ void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_
}
}
void LinearAlg2DTest::getAngleStraightTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(100, 1), 1.0);
}
void LinearAlg2DTest::getAngle45CcwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -100), 1.75);
}
void LinearAlg2DTest::getAngle90CcwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, -100), 1.5);
}
void LinearAlg2DTest::getAngle90CwTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, 100), .5);
}
void LinearAlg2DTest::getAngleStraightBackTest()
{
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, 1), 0.0);
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -1), 2.0);
}
void LinearAlg2DTest::getAngleLeftAABTest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(100, 0)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleLeftABBTest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(100, 0), Point(100, 100)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleLeftAAATest()
{
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(0, 0)); //Any output is allowed. Just don't crash!
}
void LinearAlg2DTest::getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds)
{
float actual_angle = actual_angle_in_half_rounds * M_PI;
float supposed_angle = LinearAlg2D::getAngleLeft(a, b, c);
std::stringstream ss;
ss << "Corner in " << a << "-" << b << "-" << c << " was computed to have an angle of " << supposed_angle << " instead of " << actual_angle << ".";
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::fabs(actual_angle - supposed_angle) <= maximum_error_angle);
}
}
+33
Ver Arquivo
@@ -42,6 +42,15 @@ class LinearAlg2DTest : public CppUnit::TestFixture
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
CPPUNIT_TEST(getAngleStraightTest);
CPPUNIT_TEST(getAngle90CcwTest);
CPPUNIT_TEST(getAngle90CwTest);
CPPUNIT_TEST(getAngle45CcwTest);
CPPUNIT_TEST(getAngleStraightBackTest);
CPPUNIT_TEST(getAngleLeftAABTest);
CPPUNIT_TEST(getAngleLeftABBTest);
CPPUNIT_TEST(getAngleLeftAAATest);
CPPUNIT_TEST_SUITE_END();
public:
@@ -90,6 +99,15 @@ public:
void getDist2FromLineSegmentDiagonal2LargeTest();
void getDist2FromLineSegmentZeroNearTest();
void getDist2FromLineSegmentZeroOnTest();
void getAngleStraightTest();
void getAngle90CcwTest();
void getAngle90CwTest();
void getAngle45CcwTest();
void getAngleStraightBackTest();
void getAngleLeftAABTest();
void getAngleLeftABBTest();
void getAngleLeftAAATest();
private:
/*!
@@ -111,6 +129,21 @@ private:
* \param actual_is_beyond Whether the point is actually beyond the line.
*/
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
/*!
* \brief The maximum allowed error in angle measurements.
*/
static constexpr float maximum_error_angle = 1.0;
/*!
* Performs the assertion of the getAngle tests
*
* \param a the a parameter of getAngle
* \param b the b parameter of getAngle
* \param c the c parameter of getAngle
* \param actual_angle_in_half_rounds the actual angle where 0.5 equals ???
*/
void getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds);
};
}
+74 -6
Ver Arquivo
@@ -9,6 +9,7 @@
# * Single random value
# * All settings random
import ast #For safe function evaluation.
import sys
import subprocess
import os
@@ -87,18 +88,34 @@ class TestResults():
class Setting():
def __init__(self, key, data):
## Creates a new setting from a JSON node.
#
# Some parts of the setting may have to be evaluated as functions. For
# these, the default values of all settings are added as local variables.
#
# \param key The name of the setting.
# \param data The JSON node of the setting, containing the default value,
# the setting type, the minimum value, maximum value, the minimum warning
# value, the maximum warning value and (for enum types) the options.
# \param locals The local variables for eventual function evaluation.
def __init__(self, key, data, locals):
self._key = key
self._default = data["default"]
self._type = data["type"]
self._min_value = data.get("min_value", None)
self._max_value = data.get("max_value", None)
self._min_value_warning = data.get("min_value_warning", None)
self._max_value_warning = data.get("max_value_warning", None)
self._min_value = self._evaluateFunction(data.get("min_value", None), locals)
self._max_value = self._evaluateFunction(data.get("max_value", None), locals)
self._min_value_warning = self._evaluateFunction(data.get("min_value_warning", None), locals)
self._max_value_warning = self._evaluateFunction(data.get("max_value_warning", None), locals)
self._options = data.get("options", None)
if self._options is not None:
self._options = list(self._options.keys())
## Gets the default value of this setting, according to the source JSON.
#
# \return The default value.
def getDefault(self):
return self._default
## Return a list of possible values for this setting. This list depends on the setting type.
# For number values it contains the minimal and maximal values.
# For enums and booleans it will contain the exact possible values.
@@ -157,10 +174,42 @@ class Setting():
return random.uniform(float(min), float(max))
return random.choice(self.getSettingValues())
## Evaluates a setting value that is described as a function.
#
# Note that this function should behave EXACTLY the same as it does in
# UM/Settings/Setting.py:_createFunction. The only differences should be
# that this evaluation always uses the default values instead of the
# current profile values, and that this function directly evaluates the
# setting instead of returning a function with which to evaluate the
# setting. Also, this function doesn't need to compile the list of
# settings that this setting depends on.
#
# \param code The string to evaluate as a function of default values of
# other settings.
# \param locals The default values of other settings, as dictionary keyed
# by the setting names.
# \return The evaluated value of the setting, or None if \p code was None.
def _evaluateFunction(self, code, locals):
if not code: #The input was None. This setting value doesn't exist in the JSON.
return None
try:
tree = ast.parse(code, "eval")
compiled = compile(code, self._key, "eval")
except (SyntaxError, TypeError) as e:
print("Parse error in function (" + code + ") for setting", self._key + ":", str(e))
except IllegalMethodError as e:
print("Use of illegal method", str(e), "in function (" + code + ") for setting", self._key)
except Exception as e:
print("Exception in function (" + code + ") for setting", self._key + ":", str(e))
return eval(compiled, globals(), locals)
class EngineTest():
def __init__(self, json_filename, engine_filename, models):
self._json_filename = json_filename
self._json = json.load(open(json_filename, "r"))
self._locals = {}
self._addAllLocals() #Fills the _locals dictionary.
self._engine = engine_filename
self._models = models
self._settings = {}
@@ -174,7 +223,7 @@ class EngineTest():
def _flattenSettings(self, settings):
for key, setting in settings.items():
self._settings[key] = Setting(key, setting)
self._settings[key] = Setting(key, setting, self._locals)
if "children" in setting:
self._flattenSettings(setting["children"])
@@ -248,6 +297,25 @@ class EngineTest():
def getResults(self):
return self._test_results
## Adds all default values for all settings to the locals.
#
# The results are stored in self._locals, keyed by the setting name.
def _addAllLocals(self):
for key, data in self._json["categories"].items():
self._addLocals(data["settings"])
self._addLocals(self._json["machine_settings"])
## Adds the default values in a node of the setting tree to the locals.
#
# The results are stored in self._locals, keyed by the setting name.
#
# \param settings The JSON node of which to add the default values.
def _addLocals(self, settings):
for key, setting in settings.items():
self._locals[key] = setting["default"]
if "children" in setting:
self._addLocals(setting["children"]) #Recursively go down the tree.
def main(engine, model_path):
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
filenames = list(filter(lambda filename: filename.lower().endswith(".stl"), filenames))