Comparar commits
87 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ecd832744c | |||
| 03b654af3e | |||
| c79a7f1819 | |||
| e01f18c7d4 | |||
| 42891874f4 | |||
| 257d6a6635 | |||
| bec8bef455 | |||
| 4efeaa7083 | |||
| 95fd9d6685 | |||
| 2db37c6018 | |||
| 41b0966d26 | |||
| 93485cd0df | |||
| 2d3382874a | |||
| 1e78397e18 | |||
| 6bcdd94f7e | |||
| accd28db64 | |||
| 1c0f4c42d9 | |||
| 5da1632d9f | |||
| cbf1152f56 | |||
| 2273c5aefe | |||
| 46c793e73d | |||
| fd4969887b | |||
| 6620a050a5 | |||
| 6325197fce | |||
| f828d44365 | |||
| 29564a23e0 | |||
| 1fdda3319f | |||
| be113eceb4 | |||
| 0c42ff9bfa | |||
| 168e041c42 | |||
| 670ae6dd8c | |||
| 20adfa751f | |||
| bf8776b112 | |||
| 1d0f3f519a | |||
| 07fef8668c | |||
| 1c06fc49fc | |||
| beb9422d9b | |||
| a8359b9a68 | |||
| 5ccfe2d1aa | |||
| 74577759b4 | |||
| 45eb026777 | |||
| aabb07fd81 | |||
| 6377ec63e1 | |||
| eab2d8e667 | |||
| 8d41003c67 | |||
| ecfae4d75c | |||
| a2208f6b69 | |||
| bacacb01dc | |||
| 94c9399f2c | |||
| 168dc3c12b | |||
| 235af65b00 | |||
| f3f3be74cc | |||
| dca0bc80b5 | |||
| c0e57622d0 | |||
| b7a8fbe798 | |||
| 4353980e78 | |||
| 18ae9cf41d | |||
| 04edf35331 | |||
| 6718a8b2f3 | |||
| 08a5ec7dee | |||
| 3de01763c1 | |||
| bc7ee74d19 | |||
| fc20a6661b | |||
| 81d521a58b | |||
| c4eb1d9f27 | |||
| 32d1bb6d75 | |||
| afdb552f63 | |||
| a400ba28f2 | |||
| e0a7818d9e | |||
| 277b5dce75 | |||
| 462a6e8c16 | |||
| c20d35e293 | |||
| 9e56841cd2 | |||
| c39e43c161 | |||
| 421a6d4095 | |||
| 8497e46542 | |||
| 6510ebbd92 | |||
| c1b4a5398b | |||
| 11c4b9339a | |||
| 75efbac68e | |||
| cd199dc43e | |||
| 2372a78c9b | |||
| 142f4d519f | |||
| 9ea43e7fc1 | |||
| 9b92de9b8b | |||
| f0f14b0be3 | |||
| a82c00bead |
@@ -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
@@ -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
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +385,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
|
||||
}
|
||||
}
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+52
-56
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -2,6 +2,7 @@
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/BucketGrid2D.h"
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
#define INLINE static inline
|
||||
|
||||
@@ -15,17 +16,16 @@ void PathOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_point;
|
||||
best = point_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
@@ -37,46 +37,50 @@ void PathOrderOptimizer::optimize()
|
||||
|
||||
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
int best_poly_idx = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
assert (polygons[i_polygon].size() != 2);
|
||||
assert (polygons[poly_idx].size() != 2);
|
||||
|
||||
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
|
||||
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_polygon;
|
||||
best_poly_idx = poly_idx;
|
||||
bestDist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() != 2);
|
||||
assert(polygons[best_poly_idx].size() != 2);
|
||||
|
||||
prev_point = polygons[best][polyStart[best]];
|
||||
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_poly_idx] = true;
|
||||
polyOrder.push_back(best_poly_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Failed to find next closest polygon.\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
|
||||
{
|
||||
int poly_idx = polyOrder[n];
|
||||
int poly_idx = polyOrder[order_idx];
|
||||
int point_idx = getPolyStart(prev_point, poly_idx);
|
||||
polyStart[poly_idx] = point_idx;
|
||||
prev_point = polygons[poly_idx][point_idx];
|
||||
@@ -99,22 +103,23 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
|
||||
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
|
||||
{
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
|
||||
int best_point_idx = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
|
||||
float best_point_score = std::numeric_limits<float>::infinity();
|
||||
Point p0 = poly.back();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - prev_point);
|
||||
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
|
||||
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
Point& p1 = poly[point_idx];
|
||||
Point& p2 = poly[(point_idx + 1) % poly.size()];
|
||||
int64_t dist = vSize2(p1 - prev_point);
|
||||
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
|
||||
// this score is in the order of 5 mm
|
||||
if (dist + is_on_inside_corner_score < best_point_score)
|
||||
{
|
||||
best_point_idx = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_score = dist + is_on_inside_corner_score;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
return best_point_idx;
|
||||
}
|
||||
@@ -152,127 +157,113 @@ void LineOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
|
||||
int best_point_idx = -1;
|
||||
float best_point_dist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
if (dist < best_point_dist)
|
||||
{
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
best_point_idx = point_idx;
|
||||
best_point_dist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
polyStart.push_back(best_point_idx);
|
||||
|
||||
assert(poly.size() == 2);
|
||||
|
||||
line_bucket_grid.insert(poly[0], i_polygon);
|
||||
line_bucket_grid.insert(poly[1], i_polygon);
|
||||
line_bucket_grid.insert(poly[0], poly_idx);
|
||||
line_bucket_grid.insert(poly[1], poly_idx);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point incoming_perpundicular_normal(0, 0);
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
int best_line_idx = -1;
|
||||
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
|
||||
|
||||
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
{
|
||||
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
|
||||
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
}
|
||||
|
||||
if (best == -1) /// if single-line-polygon hasn't been found yet
|
||||
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
|
||||
{
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
|
||||
{
|
||||
continue;
|
||||
assert(polygons[i_polygon].size() == 2);
|
||||
}
|
||||
assert(polygons[poly_idx].size() == 2);
|
||||
|
||||
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() == 2);
|
||||
PolygonRef best_line = polygons[best_line_idx];
|
||||
assert(best_line.size() == 2);
|
||||
|
||||
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
prev_point = polygons[best][endIdx];
|
||||
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
|
||||
int line_start_point_idx = polyStart[best_line_idx];
|
||||
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
Point& line_start = best_line[line_start_point_idx];
|
||||
Point& line_end = best_line[line_end_point_idx];
|
||||
prev_point = line_end;
|
||||
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
picked[best_line_idx] = true;
|
||||
polyOrder.push_back(best_line_idx);
|
||||
}
|
||||
else
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
int nr = polyOrder[n];
|
||||
PolygonRef poly = polygons[nr];
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i=0;i<poly.size(); i++)
|
||||
{
|
||||
float dist = vSize2f(polygons[nr][i] - prev_point);
|
||||
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
|
||||
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist + dot_score;
|
||||
}
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
polyStart[nr] = best;
|
||||
assert(poly.size() == 2);
|
||||
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
|
||||
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
|
||||
{
|
||||
Point& p0 = polygons[poly_idx][0];
|
||||
Point& p1 = polygons[poly_idx][1];
|
||||
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
|
||||
{ /// check distance to first point on line (0)
|
||||
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 0;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 0;
|
||||
}
|
||||
}
|
||||
{ /// check distance to second point on line (1)
|
||||
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
if (score < best_score)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 1;
|
||||
best = poly_idx;
|
||||
best_score = score;
|
||||
polyStart[poly_idx] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
|
||||
{
|
||||
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -81,8 +81,35 @@ public:
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
|
||||
/*!
|
||||
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
|
||||
*
|
||||
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
|
||||
*
|
||||
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
|
||||
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
|
||||
* in which case the travel move might involve combing, which makes it rather longer.
|
||||
*
|
||||
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
|
||||
* \param best[in, out] The index of current best line
|
||||
* \param best_score[in, out] The distance score for the current best line
|
||||
* \param prev_point[in] The previous point from which to find the next best line
|
||||
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
|
||||
*/
|
||||
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
|
||||
|
||||
/*!
|
||||
* Get a score to modify the distance score for measuring how good two lines follow each other.
|
||||
*
|
||||
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
|
||||
*
|
||||
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
|
||||
* \param from The one end of the next line
|
||||
* \param to The other end of the next line
|
||||
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
|
||||
*
|
||||
*/
|
||||
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+5
-3
@@ -1,4 +1,6 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <clipper/clipper.hpp>
|
||||
|
||||
#include "raft.h"
|
||||
#include "support.h"
|
||||
|
||||
@@ -8,15 +10,15 @@ void generateRaft(SliceDataStorage& storage, int distance)
|
||||
{
|
||||
if (storage.draft_protection_shield.size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound));
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
|
||||
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "linearAlg2D.h"
|
||||
|
||||
#include <cmath> // atan2
|
||||
|
||||
#include "intpoint.h" // dot
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
|
||||
{
|
||||
Point ba = a - b;
|
||||
Point bc = c - b;
|
||||
int64_t dott = dot(ba, bc); // dot product
|
||||
int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
float angle = -atan2(det, dott); // from -pi to pi
|
||||
if (angle >= 0 )
|
||||
{
|
||||
return angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return M_PI * 2 + angle;
|
||||
}
|
||||
// Point ba = a - b;
|
||||
// Point bc = c - b;
|
||||
// int64_t dott = dot(ba, bc); // dot product
|
||||
// int64_t det = ba.X * bc.Y - ba.Y * bc.X; // determinant
|
||||
// return -atan2(det, dott); // from -pi to pi
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -202,7 +202,7 @@ INLINE Point normal(const Point& p0, int64_t len)
|
||||
return p0 * len / _len;
|
||||
}
|
||||
|
||||
INLINE Point crossZ(const Point& p0)
|
||||
INLINE Point turn90CCW(const Point& p0)
|
||||
{
|
||||
return Point(-p0.Y, p0.X);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -79,7 +79,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
Point& last_point = *last_it;
|
||||
Point& point = *it;
|
||||
|
||||
if ( from_it.poly == to_list_poly
|
||||
if (&from_it.poly == &to_list_poly
|
||||
&& (
|
||||
(from_it.it == last_it || from_it.it == it) // we currently consider a linesegment directly connected to [from]
|
||||
|| (from_it.prev().it == it || from_it.next().it == last_it) // line segment from [last_point] to [point] is connected to line segment of which [from] is the other end
|
||||
@@ -94,7 +94,7 @@ void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int
|
||||
int64_t dist2 = vSize2(closest - from);
|
||||
|
||||
if (dist2 > line_width * line_width
|
||||
|| ( from_it.poly == to_list_poly
|
||||
|| (&from_it.poly == &to_list_poly
|
||||
&& dot(from_it.next().p() - from, point - last_point) > 0
|
||||
&& dot(from - from_it.prev().p(), point - last_point) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
|
||||
)
|
||||
|
||||
+10
-1
@@ -74,7 +74,16 @@ class WallOverlapComputation
|
||||
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
|
||||
: poly(poly), it(it) { }
|
||||
Point& p() const { return *it; }
|
||||
bool operator==(const ListPolyIt& other) const { return poly == other.poly && it == other.it; }
|
||||
/*!
|
||||
* Test whether two iterators refer to the same polygon in the same polygon list.
|
||||
*
|
||||
* \param other The ListPolyIt to test for equality
|
||||
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
|
||||
*/
|
||||
bool operator==(const ListPolyIt& other) const
|
||||
{
|
||||
return &poly == &other.poly && it == other.it;
|
||||
}
|
||||
void operator=(const ListPolyIt& other) { poly = other.poly; it = other.it; }
|
||||
//!< move the iterator forward (and wrap around at the end)
|
||||
ListPolyIt& operator++()
|
||||
|
||||
@@ -215,4 +215,56 @@ void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(100, 1), 1.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle45CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -100), 1.75);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CcwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, -100), 1.5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngle90CwTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(0, 100), .5);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleStraightBackTest()
|
||||
{
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, 1), 0.0);
|
||||
getAngleAssert(Point(-100, 0), Point(0, 0), Point(-100, -1), 2.0);
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAABTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(100, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftABBTest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(100, 0), Point(100, 100)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
void LinearAlg2DTest::getAngleLeftAAATest()
|
||||
{
|
||||
LinearAlg2D::getAngleLeft(Point(0, 0), Point(0, 0), Point(0, 0)); //Any output is allowed. Just don't crash!
|
||||
}
|
||||
|
||||
|
||||
void LinearAlg2DTest::getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds)
|
||||
{
|
||||
float actual_angle = actual_angle_in_half_rounds * M_PI;
|
||||
float supposed_angle = LinearAlg2D::getAngleLeft(a, b, c);
|
||||
std::stringstream ss;
|
||||
ss << "Corner in " << a << "-" << b << "-" << c << " was computed to have an angle of " << supposed_angle << " instead of " << actual_angle << ".";
|
||||
CPPUNIT_ASSERT_MESSAGE(ss.str(), std::fabs(actual_angle - supposed_angle) <= maximum_error_angle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,15 @@ class LinearAlg2DTest : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
|
||||
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
|
||||
|
||||
CPPUNIT_TEST(getAngleStraightTest);
|
||||
CPPUNIT_TEST(getAngle90CcwTest);
|
||||
CPPUNIT_TEST(getAngle90CwTest);
|
||||
CPPUNIT_TEST(getAngle45CcwTest);
|
||||
CPPUNIT_TEST(getAngleStraightBackTest);
|
||||
CPPUNIT_TEST(getAngleLeftAABTest);
|
||||
CPPUNIT_TEST(getAngleLeftABBTest);
|
||||
CPPUNIT_TEST(getAngleLeftAAATest);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -90,6 +99,15 @@ public:
|
||||
void getDist2FromLineSegmentDiagonal2LargeTest();
|
||||
void getDist2FromLineSegmentZeroNearTest();
|
||||
void getDist2FromLineSegmentZeroOnTest();
|
||||
|
||||
void getAngleStraightTest();
|
||||
void getAngle90CcwTest();
|
||||
void getAngle90CwTest();
|
||||
void getAngle45CcwTest();
|
||||
void getAngleStraightBackTest();
|
||||
void getAngleLeftAABTest();
|
||||
void getAngleLeftABBTest();
|
||||
void getAngleLeftAAATest();
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -111,6 +129,21 @@ private:
|
||||
* \param actual_is_beyond Whether the point is actually beyond the line.
|
||||
*/
|
||||
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
|
||||
|
||||
/*!
|
||||
* \brief The maximum allowed error in angle measurements.
|
||||
*/
|
||||
static constexpr float maximum_error_angle = 1.0;
|
||||
|
||||
/*!
|
||||
* Performs the assertion of the getAngle tests
|
||||
*
|
||||
* \param a the a parameter of getAngle
|
||||
* \param b the b parameter of getAngle
|
||||
* \param c the c parameter of getAngle
|
||||
* \param actual_angle_in_half_rounds the actual angle where 0.5 equals ???
|
||||
*/
|
||||
void getAngleAssert(Point a, Point b, Point c, float actual_angle_in_half_rounds);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+74
-6
@@ -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))
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário