#include "Wireframe2gcode.h" #include // sqrt #include // debug IO #include "utils/math.h" #include "utils/logoutput.h" #include "weaveDataStorage.h" #include "progress/Progress.h" #include "pathOrderOptimizer.h" //For skirt/brim. namespace cura { void Wireframe2gcode::writeGCode() { gcode.preSetup(wireFrame.meshgroup); gcode.setInitialTemps(*wireFrame.meshgroup); if (CommandSocket::getInstance()) CommandSocket::getInstance()->beginGCode(); processStartingCode(); int maxObjectHeight; if (wireFrame.layers.empty()) { maxObjectHeight = 0; } else { maxObjectHeight = wireFrame.layers.back().z1; } gcode.setZ(initial_layer_thickness); processSkirt(); unsigned int total_layers = wireFrame.layers.size(); gcode.writeLayerComment(0); gcode.writeTypeComment(PrintFeatureType::SkirtBrim); for (PolygonRef bottom_part : wireFrame.bottom_infill.roof_outlines) { if (bottom_part.size() == 0) continue; writeMoveWithRetract(bottom_part[bottom_part.size()-1]); for (Point& segment_to : bottom_part) { gcode.writeMove(segment_to, speedBottom, extrusion_mm3_per_mm_flat); } } // bottom: Polygons empty_outlines; writeFill(wireFrame.bottom_infill.roof_insets, empty_outlines, [this](Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; if (segment.segmentType == WeaveSegmentType::MOVE || segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT) // this is the case when an inset overlaps with a hole { writeMoveWithRetract(segment.to); } else { gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_connection); } } , [this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) { if (segment.segmentType == WeaveSegmentType::MOVE) writeMoveWithRetract(segment.to); else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT) return; // do nothing else gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_flat); } ); Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr); for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++) { Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine WeaveLayer& layer = wireFrame.layers[layer_nr]; gcode.writeLayerComment(layer_nr+1); double fanSpeed = getSettingInPercentage("cool_fan_speed_max"); if (layer_nr == 0) fanSpeed = getSettingInPercentage("cool_fan_speed_min"); gcode.writeFanCommand(fanSpeed); for (unsigned int part_nr = 0; part_nr < layer.connections.size(); part_nr++) { WeaveConnectionPart& part = layer.connections[part_nr]; if (part.connection.segments.size() == 0) continue; gcode.writeTypeComment(PrintFeatureType::Support); // connection { if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight) { Point3 point_same_height(part.connection.from.x, part.connection.from.y, layer.z1+100); writeMoveWithRetract(point_same_height); } writeMoveWithRetract(part.connection.from); for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++) { handle_segment(layer, part, segment_idx); } } gcode.writeTypeComment(PrintFeatureType::OuterWall); // top { for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; if (segment.segmentType == WeaveSegmentType::DOWN) continue; if (segment.segmentType == WeaveSegmentType::MOVE) { writeMoveWithRetract(segment.to); } else { gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat); gcode.writeDelay(flat_delay); } } } } // roofs: gcode.setZ(layer.z1); std::function handle_roof = &Wireframe2gcode::handle_roof_segment; writeFill(layer.roofs.roof_insets, layer.roofs.roof_outlines, handle_roof, [this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) { // handle flat segments if (segment.segmentType == WeaveSegmentType::MOVE) { writeMoveWithRetract(segment.to); } else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT) { // do nothing } else { gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat); gcode.writeDelay(flat_delay); } }); } gcode.setZ(maxObjectHeight); gcode.writeRetraction(&standard_retraction_config); gcode.updateTotalPrintTime(); gcode.writeDelay(0.3); gcode.writeFanCommand(0); finalize(); } void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to; if (go_back_to_last_top) gcode.writeMove(from, speedDown, 0); if (straight_first_when_going_down <= 0) { gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection); } else { Point3& to = segment.to; Point3 from = gcode.getPosition();// segment.from; Point3 vec = to - from; Point3 in_between = from + vec * straight_first_when_going_down / 100; Point3 up(in_between.x, in_between.y, from.z); int64_t new_length = (up - from).vSize() + (to - up).vSize() + 5; int64_t orr_length = vec.vSize(); double enlargement = new_length / orr_length; gcode.writeMove(up, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement); gcode.writeMove(to, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement); } gcode.writeDelay(bottom_delay); if (up_dist_half_speed > 0) { gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_mm3_per_mm_connection * 2); } } void Wireframe2gcode::strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; gcode.writeMove(segment.to, speedUp, extrusion_mm3_per_mm_connection); Point3 next_vector; if (segment_idx + 1 < part.connection.segments.size()) { WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1]; next_vector = next_segment.to - segment.to; } else { next_vector = part.connection.segments[0].to - segment.to; } Point next_dir_2D(next_vector.x, next_vector.y); next_dir_2D = next_dir_2D * top_jump_dist / vSize(next_dir_2D); Point3 next_dir (next_dir_2D.X / 2, next_dir_2D.Y / 2, -top_jump_dist); Point3 current_pos = gcode.getPosition(); gcode.writeMove(current_pos - next_dir, speedUp, 0); gcode.writeDelay(top_delay); gcode.writeMove(current_pos + next_dir_2D, speedUp, 0); } void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to; RetractionConfig retraction_config; // TODO: get these from the settings! retraction_config.distance = 500; //INT2MM(getSettingInt("retraction_amount")) retraction_config.prime_volume = 0;//INT2MM(getSettingInt("retractionPrime retraction_config.speed = 20; // 40; retraction_config.primeSpeed = 15; // 30; retraction_config.zHop = 0; //getSettingInt("retraction_hop"); retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max"); retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window"); retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel"); double top_retract_pause = 2.0; int retract_hop_dist = 1000; bool after_retract_hop = false; //bool go_horizontal_first = true; bool lower_retract_start = true; Point3& to = segment.to; if (lower_retract_start) { Point3 vec = to - from; Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize(); Point3 lower = to - lowering; gcode.writeMove(lower, speedUp, extrusion_mm3_per_mm_connection); gcode.writeRetraction(&retraction_config); gcode.writeMove(to + lowering, speedUp, 0); gcode.writeDelay(top_retract_pause); if (after_retract_hop) gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0); } else { gcode.writeMove(to, speedUp, extrusion_mm3_per_mm_connection); gcode.writeRetraction(&retraction_config); gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0); gcode.writeDelay(top_retract_pause); if (after_retract_hop) gcode.writeMove(to + Point3(0, 0, retract_hop_dist*3), speedFlat, 0); } } void Wireframe2gcode::strategy_compensate(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to; Point3 to = segment.to + Point3(0, 0, fall_down*(segment.to - from).vSize() / connectionHeight); Point3 vector = segment.to - from; Point3 dir = vector * drag_along / vector.vSize(); Point3 next_point; if (segment_idx + 1 < part.connection.segments.size()) { WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1]; next_point = next_segment.to; } else { next_point = part.connection.segments[0].to; } Point3 next_vector = next_point - segment.to; Point next_dir_2D(next_vector.x, next_vector.y); int64_t next_dir_2D_size = vSize(next_dir_2D); if (next_dir_2D_size > 0) next_dir_2D = next_dir_2D * drag_along / next_dir_2D_size; Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0); Point3 newTop = to - next_dir + dir; int64_t orrLength = (segment.to - from).vSize() + next_vector.vSize() + 1; // + 1 in order to avoid division by zero int64_t newLength = (newTop - from).vSize() + (next_point - newTop).vSize() + 1; // + 1 in order to avoid division by zero gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_mm3_per_mm_connection * orrLength / newLength); } void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; switch(segment.segmentType) { case WeaveSegmentType::MOVE: writeMoveWithRetract(segment.to); break; case WeaveSegmentType::DOWN: go_down(layer, part, segment_idx); break; case WeaveSegmentType::FLAT: logWarning("Warning: flat piece in wire print connection.\n"); break; case WeaveSegmentType::UP: if (strategy == STRATEGY_KNOT) { strategy_knot(layer, part, segment_idx); } else if (strategy == STRATEGY_RETRACT) { strategy_retract(layer, part, segment_idx); } else if (strategy == STRATEGY_COMPENSATE) { strategy_compensate(layer, part, segment_idx); } break; case WeaveSegmentType::DOWN_AND_FLAT: logError("Down and flat move in non-horizontal connection!"); break; } } void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx) { WeaveConnectionSegment& segment = part.connection.segments[segment_idx]; Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to; WeaveConnectionSegment* next_segment = nullptr; if (segment_idx + 1 < part.connection.segments.size()) next_segment = &part.connection.segments[segment_idx+1]; switch(segment.segmentType) { case WeaveSegmentType::MOVE: case WeaveSegmentType::DOWN_AND_FLAT: if (next_segment && next_segment->segmentType != WeaveSegmentType::DOWN_AND_FLAT) { writeMoveWithRetract(segment.to); } break; case WeaveSegmentType::UP: { Point3 to = segment.to + Point3(0, 0, roof_fall_down); Point3 vector = segment.to - from; if (vector.vSize2() == 0) return; Point3 dir = vector * roof_drag_along / vector.vSize(); Point3 next_vector; if (next_segment) { next_vector = next_segment->to - segment.to; } else { next_vector = part.connection.segments[0].to - segment.to; } Point next_dir_2D(next_vector.x, next_vector.y); Point3 detoured = to + dir; if (vSize2(next_dir_2D) > 0) { next_dir_2D = next_dir_2D * roof_drag_along / vSize(next_dir_2D); Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0); detoured -= next_dir; } gcode.writeMove(detoured, speedUp, extrusion_mm3_per_mm_connection); } break; case WeaveSegmentType::DOWN: gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection); gcode.writeDelay(roof_outer_delay); break; case WeaveSegmentType::FLAT: logError("Flat move in connection!"); break; } } void Wireframe2gcode::writeFill(std::vector& infill_insets, Polygons& roof_outlines , std::function connectionHandler , std::function flatHandler) { // bottom: gcode.writeTypeComment(PrintFeatureType::Infill); for (unsigned int inset_idx = 0; inset_idx < infill_insets.size(); inset_idx++) { WeaveRoofPart& inset = infill_insets[inset_idx]; for (unsigned int inset_part_nr = 0; inset_part_nr < inset.connections.size(); inset_part_nr++) { WeaveConnectionPart& inset_part = inset.connections[inset_part_nr]; std::vector& segments = inset_part.connection.segments; gcode.writeTypeComment(PrintFeatureType::Support); // connection if (segments.size() == 0) continue; Point3 first_extrusion_from = inset_part.connection.from; unsigned int first_segment_idx; for (first_segment_idx = 0; first_segment_idx < segments.size() && segments[first_segment_idx].segmentType == WeaveSegmentType::MOVE; first_segment_idx++) { // finds the first segment which is not a move first_extrusion_from = segments[first_segment_idx].to; } if (first_segment_idx == segments.size()) continue; writeMoveWithRetract(first_extrusion_from); for (unsigned int segment_idx = first_segment_idx; segment_idx < segments.size(); segment_idx++) { connectionHandler(*this, inset, inset_part, segment_idx); } gcode.writeTypeComment(PrintFeatureType::InnerWall); // top for (unsigned int segment_idx = 0; segment_idx < segments.size(); segment_idx++) { WeaveConnectionSegment& segment = segments[segment_idx]; if (segment.segmentType == WeaveSegmentType::DOWN) continue; flatHandler(*this, segment); } } } gcode.writeTypeComment(PrintFeatureType::OuterWall); // outer perimeter of the flat parts for (PolygonRef poly : roof_outlines) { writeMoveWithRetract(poly[poly.size() - 1]); for (Point& p : poly) { Point3 to(p.X, p.Y, gcode.getPositionZ()); WeaveConnectionSegment segment(to, WeaveSegmentType::FLAT); flatHandler(*this, segment); } } } void Wireframe2gcode::writeMoveWithRetract(Point3 to) { if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2) gcode.writeRetraction(&standard_retraction_config); gcode.writeMove(to, moveSpeed, 0); } void Wireframe2gcode::writeMoveWithRetract(Point to) { if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2) gcode.writeRetraction(&standard_retraction_config); gcode.writeMove(to, moveSpeed, 0); } Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base) : SettingsMessenger(settings_base) , gcode(gcode) , wireFrame(weaver.wireFrame) { initial_layer_thickness = getSettingInMicrons("layer_height_0"); connectionHeight = getSettingInMicrons("wireframe_height"); roof_inset = getSettingInMicrons("wireframe_roof_inset"); filament_diameter = getSettingInMicrons("material_diameter"); line_width = getSettingInMicrons("wall_line_width_x"); flowConnection = getSettingInPercentage("wireframe_flow_connection"); flowFlat = getSettingInPercentage("wireframe_flow_flat"); const double line_area = M_PI * square(INT2MM(line_width) / 2.0); extrusion_mm3_per_mm_connection = line_area * flowConnection / 100.0; extrusion_mm3_per_mm_flat = line_area * flowFlat / 100.0; nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ . nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | . nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ . nozzle_clearance = getSettingInMicrons("wireframe_nozzle_clearance"); // at least line width nozzle_top_diameter = tan(nozzle_expansion_angle) * connectionHeight + nozzle_outer_diameter + nozzle_clearance; moveSpeed = 40; speedBottom = getSettingInMillimetersPerSecond("wireframe_printspeed_bottom"); speedUp = getSettingInMillimetersPerSecond("wireframe_printspeed_up"); speedDown = getSettingInMillimetersPerSecond("wireframe_printspeed_down"); speedFlat = getSettingInMillimetersPerSecond("wireframe_printspeed_flat"); flat_delay = getSettingInSeconds("wireframe_flat_delay"); bottom_delay = getSettingInSeconds("wireframe_bottom_delay"); top_delay = getSettingInSeconds("wireframe_top_delay"); up_dist_half_speed = getSettingInMicrons("wireframe_up_half_speed"); top_jump_dist = getSettingInMicrons("wireframe_top_jump"); fall_down = getSettingInMicrons("wireframe_fall_down"); drag_along = getSettingInMicrons("wireframe_drag_along"); strategy = STRATEGY_COMPENSATE; if (getSettingString("wireframe_strategy") == "Compensate") strategy = STRATEGY_COMPENSATE; if (getSettingString("wireframe_strategy") == "Knot") strategy = STRATEGY_KNOT; if (getSettingString("wireframe_strategy") == "Retract") strategy = STRATEGY_RETRACT; go_back_to_last_top = false; straight_first_when_going_down = getSettingInPercentage("wireframe_straight_before_down"); roof_fall_down = getSettingInMicrons("wireframe_roof_fall_down"); roof_drag_along = getSettingInMicrons("wireframe_roof_drag_along"); roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay"); standard_retraction_config.distance = getSettingInMillimeters("retraction_amount"); standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount"); standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed"); standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed"); standard_retraction_config.zHop = getSettingInMicrons("retraction_hop"); standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max"); standard_retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window"); standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel"); } void Wireframe2gcode::processStartingCode() { if (!CommandSocket::isInstantiated()) { std::string prefix = gcode.getFileHeader(); gcode.writeCode(prefix.c_str()); } int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr"); gcode.writeComment("Generated with Cura_SteamEngine " VERSION); if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE && gcode.getFlavor() != EGCodeFlavor::GRIFFIN) { if (getSettingBoolean("material_bed_temp_prepend")) { if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0) { gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait")); } } if (getSettingBoolean("material_print_temp_prepend")) { for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++) { double print_temp = getSettingInDegreeCelsius("material_print_temperature"); gcode.writeTemperatureCommand(extruder_nr, print_temp); } if (getSettingBoolean("material_print_temp_wait")) { for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++) { double print_temp = getSettingInDegreeCelsius("material_print_temperature"); gcode.writeTemperatureCommand(extruder_nr, print_temp, true); } } } } gcode.writeCode(getSettingString("machine_start_gcode").c_str()); if (gcode.getFlavor() == EGCodeFlavor::BFB) { gcode.writeComment("enable auto-retraction"); std::ostringstream tmp; tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000); gcode.writeLine(tmp.str().c_str()); } else if (gcode.getFlavor() == EGCodeFlavor::GRIFFIN) { // initialize extruder trains gcode.writeCode("T0"); // Toolhead already assumed to be at T0, but writing it just to be safe... CommandSocket::setSendCurrentPosition(gcode.getPositionXY()); gcode.startExtruder(start_extruder_nr); constexpr bool wait = true; gcode.writeTemperatureCommand(start_extruder_nr, getSettingInDegreeCelsius("material_print_temperature"), wait); gcode.writePrimeTrain(getSettingInMillimetersPerSecond("speed_travel")); gcode.writeRetraction(&standard_retraction_config); } } void Wireframe2gcode::processSkirt() { if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either. { return; } Polygons skirt = wireFrame.bottom_outline.offset(100000+5000).offset(-100000); PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN)); order.addPolygons(skirt); order.optimize(); for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++) { unsigned int poly_idx = order.polyOrder[poly_order_idx]; PolygonRef poly = skirt[poly_idx]; gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0); for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()]; gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_brim_speed"), getSettingInMillimeters("skirt_brim_line_width") * INT2MM(initial_layer_thickness)); } } } void Wireframe2gcode::finalize() { gcode.finalize(getSettingString("machine_end_gcode").c_str()); for(int e=0; e