diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b0..0000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/OrcaSlicer.iml b/.idea/OrcaSlicer.iml deleted file mode 100644 index f08604bb65..0000000000 --- a/.idea/OrcaSlicer.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index f60388162d..0000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c2b..0000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 79b3c94830..0000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b80725f1d9..0000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfbb..0000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 7839e5fd45..3ecf029298 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -94,8 +94,8 @@ text = Switch workspaces\nYou can switch between Prepare and Preview [hint:How to use keyboard shortcuts] text = How to use keyboard shortcuts\nDid you know that Orca Slicer offers a wide range of keyboard shortcuts and 3D scene operations? -[hint:Reverse on odd] -text = Reverse on odd\nDid you know that Reverse on odd feature can significantly improve the surface quality of your overhangs? +[hint:Reverse on even] +text = Reverse on even\nDid you know that Reverse on even feature can significantly improve the surface quality of your overhangs? However, it can cause wall inconsistencies so use carefully! [hint:Cut Tool] text = Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool? diff --git a/resources/profiles/Anet A8 Plus(A8+)_cover.png b/resources/profiles/Anet A8 Plus(A8+)_cover.png deleted file mode 100644 index 4eb1ee93b2..0000000000 Binary files a/resources/profiles/Anet A8 Plus(A8+)_cover.png and /dev/null differ diff --git a/resources/profiles/Anet/Anet A8 Plus(A8+)_cover.png b/resources/profiles/Anet/Anet A8 Plus(A8+)_cover.png deleted file mode 100644 index 4eb1ee93b2..0000000000 Binary files a/resources/profiles/Anet/Anet A8 Plus(A8+)_cover.png and /dev/null differ diff --git a/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.json b/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.json deleted file mode 100644 index 50c3866955..0000000000 --- a/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.json +++ /dev/null @@ -1,237 +0,0 @@ -{ - "type": "machine", - "name": "Anet A8 Plus 0.4 nozzle", - "inherits": "", - "from": "User", - "adaptive_bed_mesh_margin": "0", - "auxiliary_fan": "0", - "bbl_use_printhost": "0", - "bed_custom_model": "", - "bed_custom_texture": "", - "bed_exclude_area": [ - "0x0" - ], - "bed_mesh_max": "99999,99999", - "bed_mesh_min": "-99999,-99999", - "bed_mesh_probe_distance": "50,50", - "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n", - "best_object_pos": "0.5,0.5", - "change_extrusion_role_gcode": "", - "change_filament_gcode": "M600", - "cooling_tube_length": "5", - "cooling_tube_retraction": "91.5", - "default_bed_type": "", - "default_filament_profile": [ - "Anycubic Generic PLA" - ], - "default_print_profile": "0.20mm Standard @Anycubic 4MaxPro2", - "deretraction_speed": [ - "25" - ], - "disable_m73": "0", - "emit_machine_limits_to_gcode": "1", - "enable_filament_ramming": "1", - "enable_long_retraction_when_cut": "0", - "extra_loading_move": "-2", - "extruder_clearance_height_to_lid": "140", - "extruder_clearance_height_to_rod": "36", - "extruder_clearance_radius": "65", - "extruder_colour": [ - "#018001" - ], - "extruder_offset": [ - "0x0" - ], - "fan_kickstart": "0", - "fan_speedup_overhangs": "1", - "fan_speedup_time": "0", - "gcode_flavor": "marlin", - "head_wrap_detect_zone": [], - "high_current_on_filament_swap": "0", - "host_type": "octoprint", - "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", - "long_retractions_when_cut": [ - "0" - ], - "machine_end_gcode": "M104 S0 ; turn off extruder heating\nM140 S0 ; turn off bed heating\nM107 ; turn off fans\nG91 ; relative positioning\nG0 Z+0.5 ; move Z up a tiny bit\nG90 ; absolute positioning\nG0 X135 Y105 F{machine_max_speed_x[0]*60} ; move extruder to center position\nG0 Z190.5 F{machine_max_speed_z[0]*60} ; lower the plattform to Z min\nM84 ; steppers off\nG90 ; absolute positioning\n", - "machine_load_filament_time": "0", - "machine_max_acceleration_e": [ - "5000", - "5000" - ], - "machine_max_acceleration_extruding": [ - "1250", - "1250" - ], - "machine_max_acceleration_retracting": [ - "1250", - "1250" - ], - "machine_max_acceleration_travel": [ - "1500", - "1500" - ], - "machine_max_acceleration_x": [ - "1000", - "900" - ], - "machine_max_acceleration_y": [ - "1000", - "900" - ], - "machine_max_acceleration_z": [ - "1000", - "100" - ], - "machine_max_jerk_e": [ - "5", - "5" - ], - "machine_max_jerk_x": [ - "6", - "6" - ], - "machine_max_jerk_y": [ - "6", - "6" - ], - "machine_max_jerk_z": [ - "0.2", - "0.2" - ], - "machine_max_junction_deviation": [ - "0.10", - "0" - ], - "machine_max_speed_e": [ - "120", - "120" - ], - "machine_max_speed_x": [ - "200", - "200" - ], - "machine_max_speed_y": [ - "200", - "200" - ], - "machine_max_speed_z": [ - "64", - "16" - ], - "machine_min_extruding_rate": [ - "0", - "0" - ], - "machine_min_travel_rate": [ - "0", - "0" - ], - "machine_pause_gcode": "M601", - "machine_start_gcode": "G21 ; metric values\nG90 ; absolute positioning\nM82 ; set extruder to absolute mode\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 X0 Y0 ; home X and Y\nG28 Z0 ; home Z\nG1 Z30 F{machine_max_speed_z[0]*60} ; move Z a bit down to not blow on the bed edge while heating\nG1 X10 F3900 ; let some space on x to prevent the filament cooling exhaust from beeing blocked by the servo motor\nM190 S[bed_temperature_initial_layer_single] ; wait for bed temp\nM104 S[nozzle_temperature_initial_layer] ; set extruder temp\nM106 S80 ; turn on fan to prevent air nozzle melt while heating up\nM109 S[nozzle_temperature_initial_layer] ; wait for extruder temp\nM107 ; start with the fan off\nG28 X0 ; goto X home again\nG92 E0 ; zero the extruded length\nG1 Z0.2 F360 ; move plattform upwards\n; extrude material next to the plattform (comment or remove following lines to disable)\nG1 F180 E20 ; extrude some material next to the plattform\nG92 E0 ; zero the extruded length\nG1 E-[retraction_length] F{retraction_speed[0]*60} ; do a filament retract\nG92 E0 ; zero the extruded length again\nG1 X5 F3900 ; move sideways to get rid of that string\nG1 E[retraction_length] F{retraction_speed[0]*60} ; do a filament deretract with retract parameters\nG92 E0 ; zero the extruded length again\n; draw intro line (comment or remove following lines to disable)\nG1 X30 E5 F700 ; draw intro line\nG92 E0 ; zero the extruded length\nG1 E-[retraction_length] F{retraction_speed[0]*60} ; do a filament retract\nG1 X40 Z2.0 ; move away from the introline\nG92 E0 ; zero the extruded length again\nG1 E[retraction_length] F{retraction_speed[0]*60} ; do a filament deretract with retract parameters\n; end of intro line code\nM117 Printing...\nG5", - "machine_tool_change_time": "0", - "machine_unload_filament_time": "0", - "manual_filament_change": "0", - "max_layer_height": [ - "0.3" - ], - "max_resonance_avoidance_speed": "120", - "min_layer_height": [ - "0.07" - ], - "min_resonance_avoidance_speed": "70", - "nozzle_diameter": [ - "0.4" - ], - "nozzle_height": "2.5", - "nozzle_hrc": "0", - "nozzle_type": "undefine", - "nozzle_volume": "0", - "parking_pos_retraction": "92", - "pellet_modded_printer": "0", - "preferred_orientation": "0", - "printable_area": [ - "0x0", - "300x0", - "300x300", - "0x300" - ], - "printable_height": "350", - "printer_model": "Anet A8 Plus", - "printer_notes": "", - "printer_settings_id": "Anet A8 Plus 0.4 nozzle", - "printer_structure": "undefine", - "printer_technology": "FFF", - "printer_variant": "0.4", - "printhost_authorization_type": "key", - "printhost_ssl_ignore_revoke": "0", - "printing_by_object_gcode": "", - "purge_in_prime_tower": "1", - "resonance_avoidance": "0", - "retract_before_wipe": [ - "0%" - ], - "retract_length_toolchange": [ - "10" - ], - "retract_lift_above": [ - "0" - ], - "retract_lift_below": [ - "0" - ], - "retract_lift_enforce": [ - "All Surfaces" - ], - "retract_restart_extra": [ - "0" - ], - "retract_restart_extra_toolchange": [ - "0" - ], - "retract_when_changing_layer": [ - "1" - ], - "retraction_distances_when_cut": [ - "18" - ], - "retraction_length": [ - "2.5" - ], - "retraction_minimum_travel": [ - "2" - ], - "retraction_speed": [ - "35" - ], - "scan_first_layer": "0", - "silent_mode": "0", - "single_extruder_multi_material": "1", - "support_air_filtration": "1", - "support_chamber_temp_control": "1", - "support_multi_bed_types": "0", - "template_custom_gcode": "", - "thumbnails": "48x48/PNG,300x300/PNG", - "thumbnails_format": "PNG", - "time_cost": "0", - "time_lapse_gcode": "", - "travel_slope": [ - "3" - ], - "upward_compatible_machine": [], - "use_firmware_retraction": "0", - "use_relative_e_distances": "1", - "wipe": [ - "1" - ], - "wipe_distance": [ - "1" - ], - "z_hop": [ - "0.4" - ], - "z_hop_types": [ - "Normal Lift" - ], - "z_offset": "0" -} diff --git a/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.orca_printer b/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.orca_printer deleted file mode 100644 index b73091555d..0000000000 Binary files a/resources/profiles/Anet/machine/Anet A8 Plus 0.4 nozzle.orca_printer and /dev/null differ diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 407163b3dd..a9991f6f62 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -736,6 +736,9 @@ std::string CoolingBuffer::apply_layer_cooldown( // Second generate the adjusted G-code. std::string new_gcode; new_gcode.reserve(gcode.size() * 2); + // ORCA: per-printer minimum non-zero fan PWM. Applied at every part-cooling fan emission below so any + // non-zero command is raised to at least this percent (0 disables the clamp). + const unsigned int part_cooling_fan_min_pwm = static_cast(std::max(0, m_config.part_cooling_fan_min_pwm.value)); bool overhang_fan_control= false; int overhang_fan_speed = 0; bool internal_bridge_fan_control= false; // ORCA: Add support for separate internal bridge fan speed control @@ -744,7 +747,7 @@ std::string CoolingBuffer::apply_layer_cooldown( int supp_interface_fan_speed = 0; bool ironing_fan_control= false; // ORCA: Add support for ironing fan speed control int ironing_fan_speed = 0; // ORCA: Add support for ironing fan speed control - auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, + auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, part_cooling_fan_min_pwm, &overhang_fan_control, &overhang_fan_speed, &internal_bridge_fan_control, &internal_bridge_fan_speed, &supp_interface_fan_control, &supp_interface_fan_speed, @@ -760,11 +763,11 @@ std::string CoolingBuffer::apply_layer_cooldown( int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer); supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed); - if (close_fan_the_first_x_layers <= 0 && full_fan_speed_layer > 0) { - // When ramping up fan speed from close_fan_the_first_x_layers to full_fan_speed_layer, force close_fan_the_first_x_layers above zero, - // so there will be a zero fan speed at least at the 1st layer. - close_fan_the_first_x_layers = 1; - } + // ORCA: previously a silent override forced `close_fan_the_first_x_layers` from 0 up to 1 whenever a ramp + // was configured (`full_fan_speed_layer > 0`), so the first printed layer always had the fan disabled. + // That hid the user's literal "no cooling for the first 0 layers" setting and produced a non-zero starting + // factor on the ramp denominator. The override has been removed: with N=0 and M>0 the ramp now genuinely + // starts on layer 0 at a factor of 1/M and reaches 100% at layer M-1, matching the intent of the option. if (int(layer_id) >= close_fan_the_first_x_layers) { float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed); float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time)); @@ -823,7 +826,7 @@ std::string CoolingBuffer::apply_layer_cooldown( m_fan_speed = fan_speed_new; m_current_fan_speed = fan_speed_new; if (immediately_apply) - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm); } //BBS if (additional_fan_speed_new != m_additional_fan_speed) { @@ -997,26 +1000,26 @@ std::string CoolingBuffer::apply_layer_cooldown( if (need_set_fan) { if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){ - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed, part_cooling_fan_min_pwm); m_current_fan_speed = overhang_fan_speed; } else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed, part_cooling_fan_min_pwm); m_current_fan_speed = internal_bridge_fan_speed; } else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){ - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed, part_cooling_fan_min_pwm); m_current_fan_speed = supp_interface_fan_speed; } else if (fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]){ - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed, part_cooling_fan_min_pwm); m_current_fan_speed = ironing_fan_speed; } else if(fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] && m_current_fan_speed != -1){ - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed, part_cooling_fan_min_pwm); fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false; } else - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed); + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm); need_set_fan = false; } pos = line_end; diff --git a/src/libslic3r/GCode/FanMover.cpp b/src/libslic3r/GCode/FanMover.cpp index 63e47f3f73..112066bb85 100644 --- a/src/libslic3r/GCode/FanMover.cpp +++ b/src/libslic3r/GCode/FanMover.cpp @@ -230,7 +230,10 @@ void FanMover::_remove_slow_fan(int16_t min_speed, float past_sec) { std::string FanMover::_set_fan(int16_t speed) { //const Tool* tool = m_writer.get_tool(m_currrent_extruder < 20 ? m_currrent_extruder : 0); - return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed); + // ORCA: apply the per-printer non-zero fan PWM floor so reposted fan commands respect the clamp too. + const int floor_pct = m_writer.config.part_cooling_fan_min_pwm.value; + const unsigned int part_cooling_fan_min_pwm = floor_pct > 0 ? static_cast(floor_pct) : 0u; + return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed, part_cooling_fan_min_pwm); } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 4c39cbf895..721a2cbc58 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1144,9 +1144,13 @@ std::string GCodeWriter::unlift() return gcode; } -std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed) +std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm) { std::ostringstream gcode; + // ORCA: clamp non-zero fan commands up to the configured PWM floor so fans that can't spool at low duty + // cycles still start reliably. Zero (fan off) is preserved exactly so disable-fan commands are never altered. + if (speed > 0 && part_cooling_fan_min_pwm > 0 && speed < part_cooling_fan_min_pwm) + speed = part_cooling_fan_min_pwm; if (speed == 0) { switch (gcode_flavor) { case gcfTeacup: @@ -1181,7 +1185,9 @@ std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int sp std::string GCodeWriter::set_fan(unsigned int speed) const { //BBS - return GCodeWriter::set_fan(this->config.gcode_flavor, speed); + // ORCA: pick up the per-printer PWM floor from the active config. + return GCodeWriter::set_fan(this->config.gcode_flavor, speed, + static_cast(std::max(0, this->config.part_cooling_fan_min_pwm.value))); } //BBS: set additional fan speed for BBS machine only diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 6aa2ee8ffd..dc930ac713 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -98,7 +98,9 @@ public: void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; } Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; }; // To be called by the CoolingBuffer from another thread. - static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed); + // ORCA: `part_cooling_fan_min_pwm` (0-100, default 0) is a floor applied only when `speed` is non-zero, used to overcome + // PWM start-up thresholds on fans that won't spool below a certain duty cycle. A `speed` of 0 is always honoured. + static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm = 0); // To be called by the main thread. It always emits the G-code, it does not remember the previous state. // Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread. std::string set_fan(unsigned int speed) const; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 03b87c025f..b6875f6117 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1347,7 +1347,7 @@ static std::vector s_Preset_printer_options { "belt_origin_snap_z", "belt_origin_offset_z", "belt_support_floor_offset", "belt_support_floor_mode", "belt_support_z_offset_mode", "gcode_flavor", - "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", + "fan_kickstart", "part_cooling_fan_min_pwm", "fan_speedup_time", "fan_speedup_overhangs", "single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f29b412170..8de912a048 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -150,6 +150,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "fan_cooling_layer_time", "full_fan_speed_layer", "fan_kickstart", + "part_cooling_fan_min_pwm", "fan_speedup_overhangs", "fan_speedup_time", "filament_colour", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e83edf5a8d..5edfb1b0f0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3793,6 +3793,28 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0)); + // ORCA: minimum non-zero part cooling fan speed. + def = this->add("part_cooling_fan_min_pwm", coInt); + def->label = L("Minimum non-zero part cooling fan speed"); + def->tooltip = L("Some part-cooling fans cannot start spinning when commanded below a certain PWM duty cycle. " + "When set above 0, any non-zero part-cooling fan command will be raised to at least this percentage " + "so the fan reliably starts. A fan command of 0 (fan off) is always honoured exactly. " + "This clamp is applied after every other fan calculation (first-layer ramp, layer-time interpolation, " + "overhang/bridge/support-interface/ironing overrides), so scaling still operates within the range " + "[this value, 100%]." + "\nIf your firmware already disables the fan below a threshold (for example Klipper's " + "[fan] off_below: 0.10 shuts the fan off whenever the commanded duty cycle is below 10%), " + "this option and the firmware threshold should ideally be set to the same value. Matching them " + "(e.g. off_below: 0.10 in Klipper and 10% here) guarantees the slicer never emits a non-zero " + "value that the firmware would silently drop, and the fan never receives a value below the one " + "you know it can actually spool at." + "\nSet to 0 to deactivate."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(0)); + def = this->add("time_cost", coFloat); def->label = L("Time cost"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 21d353a960..3536f6d909 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1393,6 +1393,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, fan_kickstart)) ((ConfigOptionBool, fan_speedup_overhangs)) ((ConfigOptionFloat, fan_speedup_time)) + // ORCA: minimum PWM (as a percent 0-100) emitted when the part-cooling fan is asked for a non-zero speed. + // Used to overcome the PWM start-up threshold on fans that cannot spool below a certain duty cycle. + // A value of 0 (the default) leaves behaviour unchanged. A fan command of 0 (off) is always honoured. + ((ConfigOptionInt, part_cooling_fan_min_pwm)) ((ConfigOptionFloats, filament_diameter)) ((ConfigOptionBoolsNullable, filament_adaptive_volumetric_speed)) ((ConfigOptionStrings, volumetric_speed_coefficients)) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5e502207ef..c4d6369b72 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -687,6 +687,8 @@ if (APPLE) GUI/Mouse3DHandlerMac.mm GUI/InstanceCheckMac.mm GUI/InstanceCheckMac.h + GUI/DeepLinkHandlerMac.mm + GUI/DeepLinkHandlerMac.h GUI/GUI_UtilsMac.mm GUI/wxMediaCtrl2.mm GUI/wxMediaCtrl2.h diff --git a/src/slic3r/GUI/DeepLinkHandlerMac.h b/src/slic3r/GUI/DeepLinkHandlerMac.h new file mode 100644 index 0000000000..e9877e3868 --- /dev/null +++ b/src/slic3r/GUI/DeepLinkHandlerMac.h @@ -0,0 +1,16 @@ +#ifndef slic3r_GUI_DeepLinkHandlerMac_h_ +#define slic3r_GUI_DeepLinkHandlerMac_h_ + +namespace Slic3r { +namespace GUI { + +// Re-registers a Cocoa Apple Event handler for kInternetEventClass/kAEGetURL. +// Works around a regression observed after upgrading to wxWidgets 3.3.2 on +// macOS Tahoe (#13119) where wxWidgets' built-in handler is registered but +// never fires for orcaslicer:// deep links. +void register_mac_deep_link_handler(); + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/DeepLinkHandlerMac.mm b/src/slic3r/GUI/DeepLinkHandlerMac.mm new file mode 100644 index 0000000000..fd3d8272ff --- /dev/null +++ b/src/slic3r/GUI/DeepLinkHandlerMac.mm @@ -0,0 +1,41 @@ +#import +#import + +#include + +#include "DeepLinkHandlerMac.h" +#include "GUI_App.hpp" + +@interface OrcaDeepLinkHandler : NSObject +- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply; +@end + +@implementation OrcaDeepLinkHandler +- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply +{ + NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + if (url == nil || url.length == 0) + return; + BOOST_LOG_TRIVIAL(info) << "Deep link received: " << [url UTF8String]; + Slic3r::GUI::wxGetApp().MacOpenURL(wxString::FromUTF8([url UTF8String])); +} +@end + +namespace Slic3r { +namespace GUI { + +void register_mac_deep_link_handler() +{ + static OrcaDeepLinkHandler *handler = nil; + if (handler == nil) + handler = [[OrcaDeepLinkHandler alloc] init]; + + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler:handler + andSelector:@selector(handleGetURLEvent:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID:kAEGetURL]; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 889cccde0f..b5204e1d49 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -100,6 +100,9 @@ #include "Mouse3DController.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" +#ifdef __APPLE__ +#include "DeepLinkHandlerMac.h" +#endif #include "NotificationManager.hpp" #include "UnsavedChangesDialog.hpp" #include "SavePresetDialog.hpp" @@ -2547,6 +2550,12 @@ std::string get_system_info() bool GUI_App::on_init_inner() { wxLog::SetActiveTarget(new wxBoostLog()); + +#ifdef __APPLE__ + // Override wxWidgets' kAEGetURL handler so orcaslicer:// deep links keep + // working after the wxWidgets 3.3.2 upgrade on macOS (#13119). + register_mac_deep_link_handler(); +#endif #if BBL_RELEASE_TO_PUBLIC wxLog::SetLogLevel(wxLOG_Message); #endif diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index 71174665e1..aa3408ba29 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -561,14 +561,23 @@ wxString GridCellSupportEditor::ms_stringValues[2] = { wxT(""), wxT("") }; void GridCellSupportEditor::DoActivate(int row, int col, wxGrid* grid) { - ObjectGrid* local_table = dynamic_cast(grid); wxGridBlocks cell_array = grid->GetSelectedBlocks(); - - auto left_col = cell_array.begin()->GetLeftCol(); - auto right_col = cell_array.begin()->GetRightCol(); - auto top_row = cell_array.begin()->GetTopRow(); - auto bottom_row = cell_array.begin()->GetBottomRow(); - + auto iter = cell_array.begin(); + + int left_col, right_col, top_row, bottom_row; + if (iter == cell_array.end()) { + // wxWidgets 3.3.x returns an empty range when nothing is selected; + // fall back to the cell that triggered activation so the single-cell + // branch below handles it. + left_col = right_col = col; + top_row = bottom_row = row; + } else { + left_col = iter->GetLeftCol(); + right_col = iter->GetRightCol(); + top_row = iter->GetTopRow(); + bottom_row = iter->GetBottomRow(); + } + if ((left_col == right_col) && (top_row == bottom_row)) { wxGridCellBoolEditor::DoActivate(row, col, grid); diff --git a/src/slic3r/GUI/PrinterWebView.cpp b/src/slic3r/GUI/PrinterWebView.cpp index 1dccfab66f..6c20e98ce6 100644 --- a/src/slic3r/GUI/PrinterWebView.cpp +++ b/src/slic3r/GUI/PrinterWebView.cpp @@ -23,6 +23,81 @@ namespace Slic3r { namespace GUI { +#ifdef __linux__ +// Workaround for #7210: WebKitGTK crashes on vue-resize's hidden probe used by +// older Fluidd/Mainsail pages. Swap that for a shim at appendChild time +// and bridge resize events through a fake contentDocument.defaultView so vue-resize keeps +// working. Workaround proposed by @VittC. +static void inject_vue_resize_workaround(wxWebView *webView) +{ + webView->AddUserScript( + "(function() {" + " 'use strict';" + " function isVueResizeObject(el) {" + " return el && el.tagName === 'OBJECT'" + " && el.type === 'text/html'" + " && el.getAttribute('aria-hidden') === 'true'" + " && el.getAttribute('tabindex') === '-1';" + " }" + " function isResizeObserverParent(p) {" + " return p && p.classList && p.classList.contains('resize-observer');" + " }" + " function makeShim(orig, parentForRO) {" + " var shim = document.createElement('div');" + " shim.setAttribute('aria-hidden', 'true');" + " shim.setAttribute('tabindex', '-1');" + " shim.style.display = 'none';" + " var fakeWin = document.createElement('div');" + " var ro = null;" + " var origRemoveEL = fakeWin.removeEventListener.bind(fakeWin);" + " fakeWin.removeEventListener = function(type, fn, opts) {" + " origRemoveEL(type, fn, opts);" + " if (type === 'resize' && ro) { ro.disconnect(); ro = null; }" + " };" + " Object.defineProperty(shim, 'contentDocument', {" + " configurable: true," + " get: function() { return { defaultView: fakeWin }; }" + " });" + " Object.defineProperty(shim, 'contentWindow', {" + " configurable: true," + " get: function() { return fakeWin; }" + " });" + " if (typeof orig.onload === 'function') { shim.onload = orig.onload; }" + " queueMicrotask(function() {" + " if (parentForRO && typeof ResizeObserver !== 'undefined') {" + " ro = new ResizeObserver(function() {" + " fakeWin.dispatchEvent(new Event('resize'));" + " });" + " ro.observe(parentForRO);" + " }" + " if (typeof shim.onload === 'function') {" + " try { shim.onload(new Event('load')); } catch (e) {}" + " }" + " shim.dispatchEvent(new Event('load'));" + " });" + " return shim;" + " }" + " var origAppend = Node.prototype.appendChild;" + " Node.prototype.appendChild = function(child) {" + " if (isResizeObserverParent(this) && isVueResizeObject(child)) {" + " return origAppend.call(this, makeShim(child, this));" + " }" + " return origAppend.call(this, child);" + " };" + " var origInsertBefore = Node.prototype.insertBefore;" + " Node.prototype.insertBefore = function(child, ref) {" + " if (isResizeObserverParent(this) && isVueResizeObject(child)) {" + " return origInsertBefore.call(this, makeShim(child, this), ref);" + " }" + " return origInsertBefore.call(this, child, ref);" + " };" + " console.log('[vr-fix] vue-resize WebKitGTK patch active');" + "})();", + wxWEBVIEW_INJECT_AT_DOCUMENT_START + ); +} +#endif + PrinterWebView::PrinterWebView(wxWindow *parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) , m_browser(nullptr) @@ -43,6 +118,8 @@ PrinterWebView::PrinterWebView(wxWindow *parent) } #ifdef __linux__ + inject_vue_resize_workaround(m_browser); + auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db"); auto wv = static_cast(m_browser->GetNativeBackend()); auto wv_ctx = webkit_web_view_get_context(wv); @@ -165,6 +242,10 @@ void PrinterWebView::SendAPIKey() )", m_apikey); m_browser->RemoveAllUserScripts(); +#ifdef __linux__ + // Re-inject the vue-resize/WebKitGTK workaround that RemoveAllUserScripts just cleared. + inject_vue_resize_workaround(m_browser); +#endif m_browser->AddUserScript(script); m_browser->Reload(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b0ee2c9435..93dde99522 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4672,6 +4672,8 @@ void TabPrinter::build_fff() line.append_option(optgroup->get_option("fan_speedup_overhangs")); optgroup->append_line(line); optgroup->append_single_option_line("fan_kickstart", "printer_basic_information_cooling_fan#fan-kick-start-time"); + // ORCA: PWM floor for fans that won't spool at low duty cycles. + optgroup->append_single_option_line("part_cooling_fan_min_pwm", "printer_basic_information_cooling_fan#minimum-non-zero-part-cooling-fan-speed"); optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance"); optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");