mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-29 20:01:26 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12b6d6b9ba | ||
|
|
c6d89d6978 | ||
|
|
abecef53b0 | ||
|
|
44d5a22a32 | ||
|
|
dec146f1e6 | ||
|
|
a9fec0ffc4 | ||
|
|
9ab6bbebf0 |
@@ -189,7 +189,8 @@ the presets to a base is in [shared-bases.md](shared-bases.md#variant-arrays-on-
|
|||||||
2. Every key in `filament_options_with_variant` ([the full list](#the-four-key-sets)) that reaches the
|
2. Every key in `filament_options_with_variant` ([the full list](#the-four-key-sets)) that reaches the
|
||||||
preset, whether written, included or inherited, is resized to its variant count: write it at exactly
|
preset, whether written, included or inherited, is resized to its variant count: write it at exactly
|
||||||
that width, or leave it out. Keys outside the set
|
that width, or leave it out. Keys outside the set
|
||||||
(`filament_type`, plate temperatures, `fan_max_speed`, `slow_down_min_speed`, …) are never addressed by variant index; the preset contributes their first value however wide a
|
(`filament_type`, plate temperatures, `fan_max_speed`, `slow_down_min_speed`, `pressure_advance`,
|
||||||
|
…) are never addressed by variant index; the preset contributes their first value however wide a
|
||||||
file writes them.
|
file writes them.
|
||||||
3. Cover every variant the material is meant to print on across its `compatible_printers`. Leave out
|
3. Cover every variant the material is meant to print on across its `compatible_printers`. Leave out
|
||||||
a variant deliberately when the material should not be tuned for it (e.g. a TPU High Flow variant
|
a variant deliberately when the material should not be tuned for it (e.g. a TPU High Flow variant
|
||||||
@@ -454,7 +455,7 @@ no set: the variant-length resize leaves it alone, and only the pair rebuild of
|
|||||||
last for contrast; it is not a variant set.
|
last for contrast; it is not a variant set.
|
||||||
|
|
||||||
`check` and `fix-variant` read the four sets from `PrintConfig.cpp` on every run, so they follow the
|
`check` and `fix-variant` read the four sets from `PrintConfig.cpp` on every run, so they follow the
|
||||||
engine. The lists below are from the 2026-09-29 checkout; regenerate them from the repository root
|
engine. The lists below are from the 2026-09-25 checkout; regenerate them from the repository root
|
||||||
before relying on them (the recipe strips comments, since an initializer can carry a commented-out entry):
|
before relying on them (the recipe strips comments, since an initializer can carry a commented-out entry):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -477,7 +478,7 @@ EOF
|
|||||||
|
|
||||||
**`print_options_with_variant`**, process, stride 1 (45): `bridge_acceleration`, `bridge_speed`, `default_acceleration`, `default_jerk`, `default_junction_deviation`, `enable_overhang_speed`, `gap_infill_speed`, `infill_jerk`, `initial_layer_acceleration`, `initial_layer_infill_speed`, `initial_layer_jerk`, `initial_layer_speed`, `initial_layer_travel_acceleration`, `initial_layer_travel_jerk`, `initial_layer_travel_speed`, `inner_wall_acceleration`, `inner_wall_jerk`, `inner_wall_speed`, `internal_bridge_speed`, `internal_solid_infill_acceleration`, `internal_solid_infill_speed`, `outer_wall_acceleration`, `outer_wall_jerk`, `outer_wall_speed`, `overhang_1_4_speed`, `overhang_2_4_speed`, `overhang_3_4_speed`, `overhang_4_4_speed`, `print_extruder_id`, `print_extruder_variant`, `slowdown_for_curled_perimeters`, `small_perimeter_speed`, `small_perimeter_threshold`, `sparse_infill_acceleration`, `sparse_infill_speed`, `support_interface_speed`, `support_speed`, `top_solid_infill_flow_ratio`, `top_surface_acceleration`, `top_surface_jerk`, `top_surface_speed`, `travel_acceleration`, `travel_jerk`, `travel_speed`, `travel_speed_z`
|
**`print_options_with_variant`**, process, stride 1 (45): `bridge_acceleration`, `bridge_speed`, `default_acceleration`, `default_jerk`, `default_junction_deviation`, `enable_overhang_speed`, `gap_infill_speed`, `infill_jerk`, `initial_layer_acceleration`, `initial_layer_infill_speed`, `initial_layer_jerk`, `initial_layer_speed`, `initial_layer_travel_acceleration`, `initial_layer_travel_jerk`, `initial_layer_travel_speed`, `inner_wall_acceleration`, `inner_wall_jerk`, `inner_wall_speed`, `internal_bridge_speed`, `internal_solid_infill_acceleration`, `internal_solid_infill_speed`, `outer_wall_acceleration`, `outer_wall_jerk`, `outer_wall_speed`, `overhang_1_4_speed`, `overhang_2_4_speed`, `overhang_3_4_speed`, `overhang_4_4_speed`, `print_extruder_id`, `print_extruder_variant`, `slowdown_for_curled_perimeters`, `small_perimeter_speed`, `small_perimeter_threshold`, `sparse_infill_acceleration`, `sparse_infill_speed`, `support_interface_speed`, `support_speed`, `top_solid_infill_flow_ratio`, `top_surface_acceleration`, `top_surface_jerk`, `top_surface_speed`, `travel_acceleration`, `travel_jerk`, `travel_speed`, `travel_speed_z`
|
||||||
|
|
||||||
**`filament_options_with_variant`**, filament, stride 1 (54): `activate_air_filtration`, `activate_air_filtration_during_print`, `activate_air_filtration_on_completion`, `adaptive_pressure_advance`, `adaptive_pressure_advance_bridges`, `adaptive_pressure_advance_model`, `adaptive_pressure_advance_overhangs`, `complete_print_exhaust_fan_speed`, `during_print_exhaust_fan_speed`, `enable_pressure_advance`, `filament_adaptive_volumetric_speed`, `filament_cooling_before_tower`, `filament_deretraction_speed`, `filament_extruder_variant`, `filament_flow_ratio`, `filament_flush_temp`, `filament_flush_volumetric_speed`, `filament_ironing_flow`, `filament_ironing_inset`, `filament_ironing_spacing`, `filament_ironing_speed`, `filament_long_retractions_when_cut`, `filament_max_volumetric_speed`, `filament_pre_cooling_temperature`, `filament_pre_cooling_temperature_nc`, `filament_preheat_temperature_delta`, `filament_ramming_travel_time`, `filament_ramming_travel_time_nc`, `filament_ramming_volumetric_speed`, `filament_ramming_volumetric_speed_nc`, `filament_retract_after_wipe`, `filament_retract_before_wipe`, `filament_retract_length_nc`, `filament_retract_length_toolchange`, `filament_retract_lift_above`, `filament_retract_lift_below`, `filament_retract_lift_enforce`, `filament_retract_restart_extra`, `filament_retract_restart_extra_toolchange`, `filament_retract_when_changing_layer`, `filament_retraction_distances_when_cut`, `filament_retraction_length`, `filament_retraction_minimum_travel`, `filament_retraction_speed`, `filament_wipe`, `filament_wipe_distance`, `filament_z_hop`, `filament_z_hop_types`, `long_retractions_when_ec`, `nozzle_temperature`, `nozzle_temperature_initial_layer`, `pressure_advance`, `retraction_distances_when_ec`, `volumetric_speed_coefficients`
|
**`filament_options_with_variant`**, filament, stride 1 (48): `activate_air_filtration`, `activate_air_filtration_during_print`, `activate_air_filtration_on_completion`, `complete_print_exhaust_fan_speed`, `during_print_exhaust_fan_speed`, `filament_adaptive_volumetric_speed`, `filament_cooling_before_tower`, `filament_deretraction_speed`, `filament_extruder_variant`, `filament_flow_ratio`, `filament_flush_temp`, `filament_flush_volumetric_speed`, `filament_ironing_flow`, `filament_ironing_inset`, `filament_ironing_spacing`, `filament_ironing_speed`, `filament_long_retractions_when_cut`, `filament_max_volumetric_speed`, `filament_pre_cooling_temperature`, `filament_pre_cooling_temperature_nc`, `filament_preheat_temperature_delta`, `filament_ramming_travel_time`, `filament_ramming_travel_time_nc`, `filament_ramming_volumetric_speed`, `filament_ramming_volumetric_speed_nc`, `filament_retract_after_wipe`, `filament_retract_before_wipe`, `filament_retract_length_nc`, `filament_retract_length_toolchange`, `filament_retract_lift_above`, `filament_retract_lift_below`, `filament_retract_lift_enforce`, `filament_retract_restart_extra`, `filament_retract_restart_extra_toolchange`, `filament_retract_when_changing_layer`, `filament_retraction_distances_when_cut`, `filament_retraction_length`, `filament_retraction_minimum_travel`, `filament_retraction_speed`, `filament_wipe`, `filament_wipe_distance`, `filament_z_hop`, `filament_z_hop_types`, `long_retractions_when_ec`, `nozzle_temperature`, `nozzle_temperature_initial_layer`, `retraction_distances_when_ec`, `volumetric_speed_coefficients`
|
||||||
|
|
||||||
**`printer_extruder_options`**, machine, one value per extruder, not a variant set (8):
|
**`printer_extruder_options`**, machine, one value per extruder, not a variant set (8):
|
||||||
`default_nozzle_volume_type`, `extruder_max_nozzle_count`, `extruder_printable_area`,
|
`default_nozzle_volume_type`, `extruder_max_nozzle_count`, `extruder_printable_area`,
|
||||||
|
|||||||
@@ -244,10 +244,10 @@ preset for a 0.2 nozzle, explicitly revisit flow limits; do not infer a pressure
|
|||||||
required direction of change, from diameter alone.
|
required direction of change, from diameter alone.
|
||||||
|
|
||||||
On a printer with extruder variants, a filament tunes these per variant too:
|
On a printer with extruder variants, a filament tunes these per variant too:
|
||||||
`filament_max_volumetric_speed`, `filament_flow_ratio`, `nozzle_temperature`, pressure advance and the
|
`filament_max_volumetric_speed`, `filament_flow_ratio`, `nozzle_temperature` and the retraction
|
||||||
retraction overrides carry one value per variant of `filament_extruder_variant` (Standard, High Flow, …). The
|
overrides carry one value per variant of `filament_extruder_variant` (Standard, High Flow, …). The
|
||||||
exact key set is [`filament_options_with_variant`](extruder-variants.md#the-four-key-sets);
|
exact key set is [`filament_options_with_variant`](extruder-variants.md#the-four-key-sets);
|
||||||
`slow_down_min_speed` and `fan_max_speed` are not in it. Keep every such array at exactly that width, even where the
|
`slow_down_min_speed` and `pressure_advance` are not in it. Keep every such array at exactly that width, even where the
|
||||||
setting does not differ per variant, and measure the High Flow variant rather than copying Standard
|
setting does not differ per variant, and measure the High Flow variant rather than copying Standard
|
||||||
([extruder-variants.md](extruder-variants.md#filament)).
|
([extruder-variants.md](extruder-variants.md#filament)).
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# G-code preview while dragging
|
||||||
|
|
||||||
|
The sliced preview draws every toolpath segment of the plate as an instanced box. On a large
|
||||||
|
plate that is tens of millions of segments, and the frame is GPU-bound: the cost is the number of
|
||||||
|
instances drawn, not anything the CPU does per frame. Dragging the camera over such a plate cannot
|
||||||
|
keep up. The `preview_reduced_detail_mode` preference (*Graphics > G-code Preview*, off by
|
||||||
|
default) lets the preview draw less while the user drags and put the full toolpaths back when they
|
||||||
|
let go.
|
||||||
|
|
||||||
|
| Preference | Values | Effect |
|
||||||
|
|---|---|---|
|
||||||
|
| `preview_reduced_detail_mode` | `off`, `solid`, `shell` | what is drawn while dragging |
|
||||||
|
|
||||||
|
libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the
|
||||||
|
preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging.
|
||||||
|
The OpenGL ES path keeps a single set and ignores the preference.
|
||||||
|
|
||||||
|
## Two sets, one walk
|
||||||
|
|
||||||
|
`ViewerImpl::update_enabled_entities()` walks the visible vertex range once and fills two segment
|
||||||
|
index buffers side by side: the **full** set and the **reduced** set (segments and options).
|
||||||
|
Building them together is what makes switching free: starting or ending a drag is a buffer
|
||||||
|
binding, never a rebuild. A change of mode does rebuild. Nothing is built while the mode
|
||||||
|
is off, and the reduced buffers are then uploaded empty so that the last set does not stay
|
||||||
|
allocated.
|
||||||
|
|
||||||
|
Whatever the mode leaves out, the bottom and top layers of the visible range are kept whole: they
|
||||||
|
are the faces the range cuts open, and the top is what the user is looking at.
|
||||||
|
|
||||||
|
### Modes
|
||||||
|
|
||||||
|
- `EndLayersOnly` (`solid` in the preference) keeps only the two end layers. `GCodeViewer` then
|
||||||
|
draws the sliced objects and the prime tower as opaque solids, see below.
|
||||||
|
- `ShellOnly` (`shell`) keeps every role but the sparse infill, internal solid infill, internal
|
||||||
|
bridge infill and gap fill, which lie under the skins or between the walls. Walls, top and
|
||||||
|
bottom surfaces, bridges, supports and the prime tower are drawn whole, so from outside the
|
||||||
|
print looks as it does at rest. The roles come from
|
||||||
|
the slicer, so the classification is exact wherever the slicer's is; what it cannot express is
|
||||||
|
the inside of the prime tower and of a support, which share one role with their outside and are
|
||||||
|
kept whole. A profile with no top or bottom shell layers, or no walls, leaves the infill on the
|
||||||
|
surface, so `GCodeViewer` turns the hiding off for such a print when it loads it, from the
|
||||||
|
print's default region settings; per-object overrides are not consulted.
|
||||||
|
|
||||||
|
## The solid model
|
||||||
|
|
||||||
|
The preview already loads the sliced objects as shells for its translucent ghost.
|
||||||
|
`GCodeViewer::render_solid_model()` draws those shells opaque, in their filament colors, with the
|
||||||
|
`gouraud` shader, whose z range cuts them to the visible layer range. The two toolpath layers of
|
||||||
|
the reduced set are drawn afterwards and cap the cut with what was really printed there. The
|
||||||
|
shells hold only the objects, so while this mode is on the prime tower is added from its sliced
|
||||||
|
mesh, positioned as the print placed it. It is added or removed on its own when the mode changes,
|
||||||
|
without reloading the objects, keeps its opaque color so that it never appears among the
|
||||||
|
translucent shells, and stays out of their bounding box. Supports have no mesh and are not shown,
|
||||||
|
and `load_shells()` drops every non-model-part volume, so a negative volume is not cut out.
|
||||||
|
|
||||||
|
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
||||||
|
only the end layers.
|
||||||
|
|
||||||
|
## Deciding that the user is dragging
|
||||||
|
|
||||||
|
`GLCanvas3D::_update_preview_interaction()` runs at the top of every preview frame, before the
|
||||||
|
canvas decides whether to reuse its cached scene, so that the switch lands in that frame. Dragging
|
||||||
|
is `GLCanvas3D::is_user_interacting()`, the same answer the scene cache reads: the camera, the
|
||||||
|
navigator, a gizmo, the rectangle selection or either slider being held. A slider reports this from
|
||||||
|
ImGui's active id rather than its dirty flag, which is raised and consumed inside one frame. A
|
||||||
|
wheel step has no duration, so it holds the reduced set for a 150 ms settle time instead, and the
|
||||||
|
frame that restores the toolpaths is scheduled for when that time runs out, since the render timer
|
||||||
|
only wakes the idle loop. A drag cut short by focus or capture loss is ended explicitly, and a
|
||||||
|
button release wakes the idle loop, because on some platforms nothing else would until the next
|
||||||
|
input.
|
||||||
|
|
||||||
|
## Reused scene frames
|
||||||
|
|
||||||
|
`GLCanvas3D` keeps its last scene pass for frames that only rebuild the overlay (`SceneCache`). Its
|
||||||
|
key covers the canvas size, the camera and hover state, not what the toolpath sets draw, so a frame
|
||||||
|
that reuses the scene must never be one on which the set is switched.
|
||||||
|
`_update_preview_interaction()` therefore reports whether the bound set changed, and a frame on
|
||||||
|
which it did redraws the scene. The canvas neither captures nor reuses the scene while the user
|
||||||
|
drags, so no reduced frame outlives a drag, and the frame that ends a wheel's settle time is
|
||||||
|
requested as a full frame.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "OrcaFilamentLibrary",
|
"name": "OrcaFilamentLibrary",
|
||||||
"version": "02.04.00.16",
|
"version": "02.04.00.15",
|
||||||
"force_update": "0",
|
"force_update": "0",
|
||||||
"description": "Orca Filament Library",
|
"description": "Orca Filament Library",
|
||||||
"filament_list": [
|
"filament_list": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
-2
@@ -111,7 +111,6 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"enable_pressure_advance": [
|
"enable_pressure_advance": [
|
||||||
"0",
|
|
||||||
"0"
|
"0"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
@@ -317,7 +316,6 @@
|
|||||||
"2"
|
"2"
|
||||||
],
|
],
|
||||||
"pressure_advance": [
|
"pressure_advance": [
|
||||||
"0.02",
|
|
||||||
"0.02"
|
"0.02"
|
||||||
],
|
],
|
||||||
"reduce_fan_stop_start_freq": [
|
"reduce_fan_stop_start_freq": [
|
||||||
|
|||||||
+30
-129
@@ -214,9 +214,9 @@ function ShowModelInfo( pModel )
|
|||||||
|
|
||||||
SendWXDebugInfo("Model Name: "+sModelName);
|
SendWXDebugInfo("Model Name: "+sModelName);
|
||||||
|
|
||||||
$('#ModelName').text(sModelName);
|
$('#ModelName').html(sModelName);
|
||||||
$('#ModelName').attr('title',sModelName);
|
$('#ModelName').attr('title',sModelName);
|
||||||
$('#ModelAuthorName').text(sModelAuthor);
|
$('#ModelAuthorName').html(sModelAuthor);
|
||||||
|
|
||||||
switch(UploadType)
|
switch(UploadType)
|
||||||
{
|
{
|
||||||
@@ -268,7 +268,7 @@ function ShowModelInfo( pModel )
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#Model_Desc').empty().append( SanitizeDescHtml( html_decode(sModelDesc) ) );
|
$('#Model_Desc').html( html_decode(sModelDesc) );
|
||||||
|
|
||||||
let ModelPreviewList=pModel.preview_img;
|
let ModelPreviewList=pModel.preview_img;
|
||||||
let TotalPreview=ModelPreviewList.length;
|
let TotalPreview=ModelPreviewList.length;
|
||||||
@@ -281,15 +281,16 @@ function ShowModelInfo( pModel )
|
|||||||
|
|
||||||
if(TotalPreview>0)
|
if(TotalPreview>0)
|
||||||
{
|
{
|
||||||
$('#ModelPreviewList').empty();
|
let htmlPreview='';
|
||||||
for(let pn=0;pn<TotalPreview;pn++)
|
for(let pn=0;pn<TotalPreview;pn++)
|
||||||
{
|
{
|
||||||
//let FTmpPath=decodeURIComponent(ModelPreviewList[pn]);
|
//let FTmpPath=decodeURIComponent(ModelPreviewList[pn]);
|
||||||
let FTmpPath=ModelPreviewList[pn]['filepath'];
|
let FTmpPath=ModelPreviewList[pn]['filepath'];
|
||||||
|
|
||||||
$('#ModelPreviewList').append( $('<div class="swiper-slide"></div>').append( $('<img class="Model_PrevImg" />').attr('src',FTmpPath) ) );
|
htmlPreview+='<div class="swiper-slide"><img class="Model_PrevImg" src="'+FTmpPath+'" /></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#ModelPreviewList').html(htmlPreview);
|
||||||
$('#Model_Preview_Image').viewer({
|
$('#Model_Preview_Image').viewer({
|
||||||
title: false,
|
title: false,
|
||||||
fullsreen: false,
|
fullsreen: false,
|
||||||
@@ -409,8 +410,7 @@ function ConstructFileHtml( ID, pItem )
|
|||||||
{
|
{
|
||||||
let fTotal=pItem.length;
|
let fTotal=pItem.length;
|
||||||
|
|
||||||
let pBoard=$('#'+ID+' .FileListBoard');
|
let strHtml='';
|
||||||
pBoard.empty();
|
|
||||||
for( let f=0;f<fTotal;f++ )
|
for( let f=0;f<fTotal;f++ )
|
||||||
{
|
{
|
||||||
let pOne=pItem[f];
|
let pOne=pItem[f];
|
||||||
@@ -443,139 +443,39 @@ function ConstructFileHtml( ID, pItem )
|
|||||||
ImgPath='img/default.png';
|
ImgPath='img/default.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add html. File names come from the 3MF, so build the nodes rather than concatenating markup.
|
//Add html
|
||||||
let pIconImg=$('<img />').attr('src',ImgPath);
|
|
||||||
let pMenu=$('<div class="FileMenu"><img src="img/s.svg" /></div>');
|
|
||||||
if( strClass!='ImageIcon' )
|
if( strClass!='ImageIcon' )
|
||||||
{
|
{
|
||||||
pMenu.on('click', function(){ OnClickOpenFile(tPath); });
|
strHtml+='<div class="FileItem">'+
|
||||||
|
' <div class="'+strClass+'"><img src="'+ImgPath+'" /></div>'+
|
||||||
|
' <div class="FileText">'+
|
||||||
|
' <div class="FileName">'+tName+'</div>'+
|
||||||
|
' </div>'+
|
||||||
|
' <div class="FileMenu" onClick="OnClickOpenFile(\''+tPath+'\')"><img src="img/s.svg" /></div>'+
|
||||||
|
'</div>';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImgID++;
|
ImgID++;
|
||||||
let TmpImgID="AF"+ImgID;
|
let TmpImgID="AF"+ImgID;
|
||||||
|
|
||||||
pIconImg.attr('id',TmpImgID);
|
strHtml+='<div class="FileItem">'+
|
||||||
pMenu.on('click', function(){ OnClickOpenImage(TmpImgID); });
|
' <div class="'+strClass+'"><img id="'+TmpImgID+'" src="'+ImgPath+'" /></div>'+
|
||||||
|
' <div class="FileText">'+
|
||||||
|
' <div class="FileName">'+tName+'</div>'+
|
||||||
|
' </div>'+
|
||||||
|
' <div class="FileMenu" onClick="OnClickOpenImage(\''+TmpImgID+'\')"><img src="img/s.svg" /></div>'+
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pFileItem=$('<div class="FileItem"></div>');
|
$('#'+ID+' .FileListBoard').html(strHtml);
|
||||||
pFileItem.append( $('<div></div>').addClass(strClass).append(pIconImg) );
|
|
||||||
pFileItem.append( $('<div class="FileText"></div>').append( $('<div class="FileName"></div>').text(tName).attr('title',tName) ) );
|
|
||||||
pFileItem.append( pMenu );
|
|
||||||
pBoard.append( pFileItem );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( fTotal>0 )
|
if( fTotal>0 )
|
||||||
$('#'+ID).show();
|
$('#'+ID).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Descriptions are untrusted 3MF metadata that may carry rich-text HTML (e.g. from MakerWorld).
|
|
||||||
// Rebuild them from an inert parse, keeping only plain formatting tags and http(s) links and images.
|
|
||||||
var DescAllowedTags=['P','BR','B','STRONG','I','EM','U','S','STRIKE','DEL','INS','SUB','SUP','SMALL','MARK',
|
|
||||||
'Q','ABBR','KBD','WBR','H1','H2','H3','H4','H5','H6','UL','OL','LI','DL','DT','DD','BLOCKQUOTE','PRE','CODE',
|
|
||||||
'HR','SPAN','DIV','FIGURE','FIGCAPTION','TABLE','CAPTION','THEAD','TBODY','TFOOT','TR','TH','TD','A','IMG'];
|
|
||||||
// Plain attributes kept per tag; the numeric ones must be plain non-negative integers.
|
|
||||||
var DescAllowedAttrs={'IMG':['alt','title','width','height'],'TD':['colspan','rowspan'],'TH':['colspan','rowspan'],'OL':['start']};
|
|
||||||
var DescNumericAttrs=['width','height','colspan','rowspan','start'];
|
|
||||||
// Dropped together with their content; any other unknown tag is unwrapped to its children.
|
|
||||||
var DescDroppedTags=['SCRIPT','STYLE','TEMPLATE','NOSCRIPT','TEXTAREA','TITLE','IFRAME','FRAME','OBJECT','EMBED','SVG','MATH'];
|
|
||||||
|
|
||||||
function IsHttpUrl( strUrl )
|
|
||||||
{
|
|
||||||
// The scheme must be written as is, so nothing the URL parser would strip can precede or split it.
|
|
||||||
if( typeof strUrl!='string' || !/^https?:/i.test(strUrl) )
|
|
||||||
return false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
let sProtocol=new URL(strUrl).protocol;
|
|
||||||
return sProtocol=='http:' || sProtocol=='https:';
|
|
||||||
}
|
|
||||||
catch(e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Images load as soon as the page opens, so only https sources are kept: no plain-http requests to the local network.
|
|
||||||
function IsHttpsUrl( strUrl )
|
|
||||||
{
|
|
||||||
return IsHttpUrl(strUrl) && new URL(strUrl).protocol=='https:';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Embedded YouTube players become a plain link to the video.
|
|
||||||
function GetYouTubeEmbedUrl( pNode )
|
|
||||||
{
|
|
||||||
let sSrc=pNode.getAttribute('src');
|
|
||||||
if( !IsHttpUrl(sSrc) )
|
|
||||||
return null;
|
|
||||||
let pUrl=new URL(sSrc);
|
|
||||||
return ( pUrl.origin=='https://www.youtube.com' && pUrl.pathname.indexOf('/embed/')==0 ) ? pUrl.href : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function CopyDescNodes( pSrc, pDst )
|
|
||||||
{
|
|
||||||
for( let pNode=pSrc.firstChild;pNode!=null;pNode=pNode.nextSibling )
|
|
||||||
{
|
|
||||||
if( pNode.nodeType==Node.TEXT_NODE )
|
|
||||||
{
|
|
||||||
pDst.appendChild( document.createTextNode(pNode.nodeValue) );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if( pNode.nodeType!=Node.ELEMENT_NODE )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let sTag=pNode.nodeName.toUpperCase();
|
|
||||||
if( sTag=='IFRAME' )
|
|
||||||
{
|
|
||||||
let sVideoUrl=GetYouTubeEmbedUrl(pNode);
|
|
||||||
if( sVideoUrl!=null )
|
|
||||||
{
|
|
||||||
let pLink=document.createElement('A');
|
|
||||||
pLink.setAttribute('href',sVideoUrl);
|
|
||||||
pLink.textContent=sVideoUrl;
|
|
||||||
pDst.appendChild(pLink);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if( $.inArray(sTag,DescDroppedTags)>=0 )
|
|
||||||
continue;
|
|
||||||
if( $.inArray(sTag,DescAllowedTags)<0 )
|
|
||||||
{
|
|
||||||
CopyDescNodes(pNode,pDst);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pElem=document.createElement(sTag);
|
|
||||||
if( sTag=='A' && IsHttpUrl(pNode.getAttribute('href')) )
|
|
||||||
pElem.setAttribute('href',pNode.getAttribute('href'));
|
|
||||||
else if( sTag=='IMG' )
|
|
||||||
{
|
|
||||||
if( !IsHttpsUrl(pNode.getAttribute('src')) )
|
|
||||||
continue;
|
|
||||||
pElem.setAttribute('src',pNode.getAttribute('src'));
|
|
||||||
}
|
|
||||||
$.each( DescAllowedAttrs[sTag]||[], function(i,sAttr){
|
|
||||||
let sValue=pNode.getAttribute(sAttr);
|
|
||||||
if( sValue!=null && ( $.inArray(sAttr,DescNumericAttrs)<0 || /^\d+$/.test(sValue) ) )
|
|
||||||
pElem.setAttribute(sAttr,sValue);
|
|
||||||
});
|
|
||||||
CopyDescNodes(pNode,pElem);
|
|
||||||
pDst.appendChild(pElem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function SanitizeDescHtml( strHtml )
|
|
||||||
{
|
|
||||||
let pFragment=document.createDocumentFragment();
|
|
||||||
// A DOMParser document is inert: it runs no scripts and loads no resources.
|
|
||||||
let pDoc=new DOMParser().parseFromString(strHtml,'text/html');
|
|
||||||
if( pDoc && pDoc.body )
|
|
||||||
CopyDescNodes(pDoc.body,pFragment);
|
|
||||||
return pFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ShowProfilelInfo( pProfile )
|
function ShowProfilelInfo( pProfile )
|
||||||
{
|
{
|
||||||
//==========Profile Info==========
|
//==========Profile Info==========
|
||||||
@@ -583,10 +483,10 @@ function ShowProfilelInfo( pProfile )
|
|||||||
let sProfileAuthor=decodeURIComponent(pProfile.author);
|
let sProfileAuthor=decodeURIComponent(pProfile.author);
|
||||||
let sProfileDesc=decodeURIComponent(pProfile.description);
|
let sProfileDesc=decodeURIComponent(pProfile.description);
|
||||||
|
|
||||||
$('#ProfileName').text(sProfileName);
|
$('#ProfileName').html(sProfileName);
|
||||||
$('#ProfileAuthor').text(sProfileAuthor);
|
$('#ProfileAuthor').html(sProfileAuthor);
|
||||||
|
|
||||||
$('#Profile_Desc').empty().append( SanitizeDescHtml( html_decode(sProfileDesc) ) );
|
$('#Profile_Desc').html( html_decode(sProfileDesc) );
|
||||||
|
|
||||||
let ProfilePreviewList=pProfile.preview_img;
|
let ProfilePreviewList=pProfile.preview_img;
|
||||||
let TotalPreview=ProfilePreviewList.length;
|
let TotalPreview=ProfilePreviewList.length;
|
||||||
@@ -599,14 +499,15 @@ function ShowProfilelInfo( pProfile )
|
|||||||
|
|
||||||
if(TotalPreview>0)
|
if(TotalPreview>0)
|
||||||
{
|
{
|
||||||
$('#ProfilePreviewList').empty();
|
let htmlPreview='';
|
||||||
for(let pn=0;pn<TotalPreview;pn++)
|
for(let pn=0;pn<TotalPreview;pn++)
|
||||||
{
|
{
|
||||||
let FTmpPath=ProfilePreviewList[pn]['filepath'];
|
let FTmpPath=ProfilePreviewList[pn]['filepath'];
|
||||||
|
|
||||||
$('#ProfilePreviewList').append( $('<div class="swiper-slide"></div>').append( $('<img class="Model_PrevImg" />').attr('src',FTmpPath) ) );
|
htmlPreview+='<div class="swiper-slide"><img class="Model_PrevImg" src="'+FTmpPath+'" /></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#ProfilePreviewList').html(htmlPreview);
|
||||||
$('#Profile_Preview_Image').viewer({
|
$('#Profile_Preview_Image').viewer({
|
||||||
title: false,
|
title: false,
|
||||||
fullsreen: false,
|
fullsreen: false,
|
||||||
|
|||||||
@@ -3476,14 +3476,6 @@ int CLI::run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
new_variant_counts = old_variant_counts;
|
new_variant_counts = old_variant_counts;
|
||||||
//filament_variant_count = old_variant_counts;
|
//filament_variant_count = old_variant_counts;
|
||||||
//ORCA: lay the per-variant options out one value per variant of the current filaments before each
|
|
||||||
// loaded filament replaces its own variants, including an option only a loaded filament
|
|
||||||
// defines, which otherwise starts as a single default value and never reaches the others.
|
|
||||||
for (const DynamicPrintConfig &config : load_filaments_config)
|
|
||||||
for (const std::string &opt_key : filament_options_with_variant)
|
|
||||||
if (opt_key != "filament_extruder_variant" && config.has(opt_key))
|
|
||||||
m_print_config.option(opt_key, true);
|
|
||||||
normalize_filament_values_to_variants(m_print_config);
|
|
||||||
for (int index = 0; index < load_filaments_config.size(); index++) {
|
for (int index = 0; index < load_filaments_config.size(); index++) {
|
||||||
DynamicPrintConfig& config = load_filaments_config[index];
|
DynamicPrintConfig& config = load_filaments_config[index];
|
||||||
int filament_index = load_filaments_index[index];
|
int filament_index = load_filaments_index[index];
|
||||||
@@ -3655,25 +3647,6 @@ int CLI::run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ORCA: a per-variant option the loaded filament does not define keeps the values of the
|
|
||||||
// variants the filament already had, and a variant new to it takes its first one's.
|
|
||||||
const int old_start = old_start_indice[filament_index - 1];
|
|
||||||
std::vector<int> kept_variant_indice = new_variant_indice;
|
|
||||||
for (int &i : kept_variant_indice)
|
|
||||||
if (i < 0)
|
|
||||||
i = old_start;
|
|
||||||
for (const std::string &opt_key : filament_options_with_variant) {
|
|
||||||
if (config.has(opt_key))
|
|
||||||
continue;
|
|
||||||
auto *opt_vec_dst = dynamic_cast<ConfigOptionVectorBase *>(m_print_config.option(opt_key));
|
|
||||||
if (opt_vec_dst == nullptr || opt_vec_dst->size() < size_t(old_start + old_variant_count))
|
|
||||||
continue;
|
|
||||||
// set_with_restore_2() pads its source in place
|
|
||||||
std::unique_ptr<ConfigOption> old_values(opt_vec_dst->clone());
|
|
||||||
opt_vec_dst->set_with_restore_2(static_cast<ConfigOptionVectorBase *>(old_values.get()), kept_variant_indice, old_start,
|
|
||||||
old_variant_count, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//update the old index
|
//update the old index
|
||||||
if (old_variant_count != new_variant_count)
|
if (old_variant_count != new_variant_count)
|
||||||
{
|
{
|
||||||
@@ -4108,9 +4081,6 @@ int CLI::run(int argc, char **argv)
|
|||||||
if (printer_technology == ptFFF) {
|
if (printer_technology == ptFFF) {
|
||||||
fff_print_config.apply(m_print_config, true);
|
fff_print_config.apply(m_print_config, true);
|
||||||
m_print_config.apply(fff_print_config, true);
|
m_print_config.apply(fff_print_config, true);
|
||||||
//ORCA: an option no preset or project defines has just come in as its single default value, and a
|
|
||||||
// command line override may hold one value per filament.
|
|
||||||
normalize_filament_values_to_variants(m_print_config);
|
|
||||||
} else {
|
} else {
|
||||||
boost::nowide::cerr << "invalid printer_technology " << std::endl;
|
boost::nowide::cerr << "invalid printer_technology " << std::endl;
|
||||||
record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH], sliced_info);
|
record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH], sliced_info);
|
||||||
|
|||||||
@@ -205,6 +205,13 @@ void AppConfig::set_defaults()
|
|||||||
if (get("seq_top_layer_only").empty())
|
if (get("seq_top_layer_only").empty())
|
||||||
set("seq_top_layer_only", "1");
|
set("seq_top_layer_only", "1");
|
||||||
|
|
||||||
|
// what the preview draws while the user drags it
|
||||||
|
{
|
||||||
|
const std::string mode = get("preview_reduced_detail_mode");
|
||||||
|
if (mode != "off" && mode != "solid" && mode != "shell")
|
||||||
|
set("preview_reduced_detail_mode", "off");
|
||||||
|
}
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
if (get("preview_dim_previous_layers").empty())
|
if (get("preview_dim_previous_layers").empty())
|
||||||
set_bool("preview_dim_previous_layers", false);
|
set_bool("preview_dim_previous_layers", false);
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
|||||||
its.indices.emplace_back(indices[0], indices[1], indices[2]);
|
its.indices.emplace_back(indices[0], indices[1], indices[2]);
|
||||||
int face_index =its.indices.size() - 1;
|
int face_index =its.indices.size() - 1;
|
||||||
RGBA face_color;
|
RGBA face_color;
|
||||||
auto set_face_color = [&uvs, &data, &mtl_data, &obj_info, &face_color](int face_index, const std::string mtl_name, const std::array<int, 3> &corners) {
|
auto set_face_color = [&uvs, &data, &mtl_data, &obj_info, &face_color](int face_index, const std::string mtl_name) {
|
||||||
if (mtl_data.new_mtl_unmap.find(mtl_name) != mtl_data.new_mtl_unmap.end()) {
|
if (mtl_data.new_mtl_unmap.find(mtl_name) != mtl_data.new_mtl_unmap.end()) {
|
||||||
bool is_merge_ka_kd = true;
|
bool is_merge_ka_kd = true;
|
||||||
for (size_t n = 0; n < 3; n++) {
|
for (size_t n = 0; n < 3; n++) {
|
||||||
@@ -174,7 +174,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
|||||||
return Vec2f::Zero();
|
return Vec2f::Zero();
|
||||||
return Vec2f(data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH], data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH + 1]);
|
return Vec2f(data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH], data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH + 1]);
|
||||||
};
|
};
|
||||||
std::array<Vec2f, 3> uv_array{uv_at(uvs[corners[0]]), uv_at(uvs[corners[1]]), uv_at(uvs[corners[2]])};
|
std::array<Vec2f, 3> uv_array{uv_at(uvs[0]), uv_at(uvs[1]), uv_at(uvs[2])};
|
||||||
obj_info.uvs.emplace_back(uv_array);
|
obj_info.uvs.emplace_back(uv_array);
|
||||||
}
|
}
|
||||||
obj_info.face_colors.emplace_back(face_color);
|
obj_info.face_colors.emplace_back(face_color);
|
||||||
@@ -185,27 +185,27 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto set_face_color_by_mtl = [&data, &set_face_color](int face_index, const std::array<int, 3> &corners) {
|
auto set_face_color_by_mtl = [&data, &set_face_color](int face_index) {
|
||||||
if (data.usemtls.size() == 1) {
|
if (data.usemtls.size() == 1) {
|
||||||
set_face_color(face_index, data.usemtls[0].name, corners);
|
set_face_color(face_index, data.usemtls[0].name);
|
||||||
} else {
|
} else {
|
||||||
for (size_t k = 0; k < data.usemtls.size(); k++) {
|
for (size_t k = 0; k < data.usemtls.size(); k++) {
|
||||||
auto mtl = data.usemtls[k];
|
auto mtl = data.usemtls[k];
|
||||||
if (face_index >= mtl.face_start && face_index <= mtl.face_end) {
|
if (face_index >= mtl.face_start && face_index <= mtl.face_end) {
|
||||||
set_face_color(face_index, data.usemtls[k].name, corners);
|
set_face_color(face_index, data.usemtls[k].name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (exist_mtl) {
|
if (exist_mtl) {
|
||||||
set_face_color_by_mtl(face_index, {0, 1, 2});
|
set_face_color_by_mtl(face_index);
|
||||||
}
|
}
|
||||||
if (cnt == 4) {
|
if (cnt == 4) {
|
||||||
its.indices.emplace_back(indices[0], indices[2], indices[3]);
|
its.indices.emplace_back(indices[0], indices[2], indices[3]);
|
||||||
int face_index = its.indices.size() - 1;
|
int face_index = its.indices.size() - 1;
|
||||||
if (exist_mtl) {
|
if (exist_mtl) {
|
||||||
set_face_color_by_mtl(face_index, {0, 2, 3});
|
set_face_color_by_mtl(face_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,12 +217,8 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
|
|||||||
message = _L("This OBJ file couldn't be read because it's empty.");
|
message = _L("This OBJ file couldn't be read because it's empty.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (meshptr->volume() < 0) {
|
if (meshptr->volume() < 0)
|
||||||
meshptr->flip_triangles();
|
meshptr->flip_triangles();
|
||||||
// Flipping swaps corners 1 and 2 of every face, so the UVs have to follow.
|
|
||||||
for (std::array<Vec2f, 3> &uv : obj_info.uvs)
|
|
||||||
std::swap(uv[1], uv[2]);
|
|
||||||
}
|
|
||||||
// Hand the parsed material table back so callers can build a TexturedMesh from it.
|
// Hand the parsed material table back so callers can build a TexturedMesh from it.
|
||||||
if (out_mtl)
|
if (out_mtl)
|
||||||
*out_mtl = mtl_data;
|
*out_mtl = mtl_data;
|
||||||
|
|||||||
+68
-48
@@ -1497,7 +1497,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
|||||||
check_add_eol(toolchange_gcode_str);
|
check_add_eol(toolchange_gcode_str);
|
||||||
|
|
||||||
// SoftFever: set new PA for new filament
|
// SoftFever: set new PA for new filament
|
||||||
gcode += gcodegen.set_filament_pressure_advance(new_filament_id);
|
if (gcodegen.config().enable_pressure_advance.get_at(new_filament_id)) {
|
||||||
|
gcode += gcodegen.writer().set_pressure_advance(gcodegen.config().pressure_advance.get_at(new_filament_id));
|
||||||
|
// Orca: Adaptive PA
|
||||||
|
// Reset Adaptive PA processor last PA value
|
||||||
|
gcodegen.m_pa_processor->resetPreviousPA(gcodegen.config().pressure_advance.get_at(new_filament_id));
|
||||||
|
}
|
||||||
|
|
||||||
// A phony move to the end position at the wipe tower.
|
// A phony move to the end position at the wipe tower.
|
||||||
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast<double>());
|
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast<double>());
|
||||||
@@ -1821,8 +1826,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
|||||||
check_add_eol(toolchange_gcode_str);
|
check_add_eol(toolchange_gcode_str);
|
||||||
|
|
||||||
// SoftFever: set new PA for new filament
|
// SoftFever: set new PA for new filament
|
||||||
if (new_extruder_id != -1)
|
if (new_extruder_id != -1 && gcodegen.config().enable_pressure_advance.get_at(new_extruder_id)) {
|
||||||
gcode += gcodegen.set_filament_pressure_advance(new_extruder_id);
|
gcode += gcodegen.writer().set_pressure_advance(gcodegen.config().pressure_advance.get_at(new_extruder_id));
|
||||||
|
// Orca: Adaptive PA
|
||||||
|
// Reset Adaptive PA processor last PA value
|
||||||
|
gcodegen.m_pa_processor->resetPreviousPA(gcodegen.config().pressure_advance.get_at(new_extruder_id));
|
||||||
|
}
|
||||||
|
|
||||||
// A phony move to the end position at the wipe tower.
|
// A phony move to the end position at the wipe tower.
|
||||||
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast<double>());
|
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast<double>());
|
||||||
@@ -3223,7 +3232,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
m_cooling_buffer->set_current_extruder(initial_extruder_id, extruder_id);
|
m_cooling_buffer->set_current_extruder(initial_extruder_id, extruder_id);
|
||||||
|
|
||||||
// Orca: Initialise AdaptivePA processor filter
|
// Orca: Initialise AdaptivePA processor filter
|
||||||
m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this);
|
m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this, tool_ordering.all_extruders());
|
||||||
|
|
||||||
// Update output variables after the extruders were initialized.
|
// Update output variables after the extruders were initialized.
|
||||||
m_placeholder_parser_integration.init(m_writer);
|
m_placeholder_parser_integration.init(m_writer);
|
||||||
@@ -3788,7 +3797,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Orca: add missing PA settings for initial filament
|
// Orca: add missing PA settings for initial filament
|
||||||
file.write(set_filament_pressure_advance(initial_non_support_extruder_id));
|
if (m_config.enable_pressure_advance.get_at(initial_non_support_extruder_id)) {
|
||||||
|
file.write(m_writer.set_pressure_advance(m_config.pressure_advance.get_at(initial_non_support_extruder_id)));
|
||||||
|
// Orca: Adaptive PA
|
||||||
|
// Reset Adaptive PA processor last PA value
|
||||||
|
m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(initial_non_support_extruder_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//flush FanMover buffer to avoid modifying the start gcode if it's manual.
|
//flush FanMover buffer to avoid modifying the start gcode if it's manual.
|
||||||
@@ -8376,16 +8390,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
|
|
||||||
// Orca: Dynamic PA
|
// Orca: Dynamic PA
|
||||||
// If adaptive PA is enabled, by default evaluate PA on all extrusion moves
|
// If adaptive PA is enabled, by default evaluate PA on all extrusion moves
|
||||||
// The PA settings and the PA_CHANGE tags use the filament config index of the filament's extruder variant
|
|
||||||
const unsigned int pa_config_index = (unsigned int) get_filament_config_index(m_writer.filament()->id());
|
|
||||||
const bool adaptive_pa = m_config.adaptive_pressure_advance.get_at(pa_config_index) && m_config.enable_pressure_advance.get_at(pa_config_index);
|
|
||||||
const bool adaptive_pa_overhangs = adaptive_pa && m_config.adaptive_pressure_advance_overhangs.get_at(pa_config_index);
|
|
||||||
bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
|
bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
|
||||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
|
m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
|
||||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
||||||
bool evaluate_adaptive_pa = false;
|
bool evaluate_adaptive_pa = false;
|
||||||
bool role_change = (m_last_extrusion_role != path.role());
|
bool role_change = (m_last_extrusion_role != path.role());
|
||||||
if (!is_pa_calib && adaptive_pa) {
|
if (!is_pa_calib && FILAMENT_CONFIG(adaptive_pressure_advance) && FILAMENT_CONFIG(enable_pressure_advance)) {
|
||||||
evaluate_adaptive_pa = true;
|
evaluate_adaptive_pa = true;
|
||||||
// If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set
|
// If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set
|
||||||
// skip re-issuing the PA change tag.
|
// skip re-issuing the PA change tag.
|
||||||
@@ -8468,7 +8478,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
// Post processor flag generation code segment when option to emit only at role changes is enabled
|
// Post processor flag generation code segment when option to emit only at role changes is enabled
|
||||||
// Variables published to the post processor:
|
// Variables published to the post processor:
|
||||||
// 1) Tag to trigger a PA evaluation (because a role change was identified and the user has requested dynamic PA adjustments)
|
// 1) Tag to trigger a PA evaluation (because a role change was identified and the user has requested dynamic PA adjustments)
|
||||||
// 2) Current filament config index (to identify the PA model for the currently used filament and its extruder variant)
|
// 2) Current extruder ID (to identify the PA model for the currently used extruder)
|
||||||
// 3) mm3_per_mm value (to then multiply by the final model print speed after slowdown for cooling is applied)
|
// 3) mm3_per_mm value (to then multiply by the final model print speed after slowdown for cooling is applied)
|
||||||
// 4) the current acceleration (to pass to the model for evaluation)
|
// 4) the current acceleration (to pass to the model for evaluation)
|
||||||
// 5) whether this is an external perimeter (for future use)
|
// 5) whether this is an external perimeter (for future use)
|
||||||
@@ -8479,7 +8489,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
if (m_multi_flow_segment_path_average_mm3_per_mm > 0) {
|
if (m_multi_flow_segment_path_average_mm3_per_mm > 0) {
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
m_multi_flow_segment_path_average_mm3_per_mm,
|
m_multi_flow_segment_path_average_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -8492,7 +8502,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
// to issue a zero flow PA change command for this
|
// to issue a zero flow PA change command for this
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
_mm3_per_mm,
|
_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -8600,7 +8610,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
// or a flow change, so emit the flag to evaluate PA for the upcomming extrusion
|
// or a flow change, so emit the flag to evaluate PA for the upcomming extrusion
|
||||||
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
|
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
|
||||||
// Dont emit tag if it has just already been emitted from a role change above
|
// Dont emit tag if it has just already been emitted from a role change above
|
||||||
if(_mm3_per_mm >0 && adaptive_pa_overhangs && !evaluate_adaptive_pa){
|
if(_mm3_per_mm >0 &&
|
||||||
|
FILAMENT_CONFIG(adaptive_pressure_advance) &&
|
||||||
|
FILAMENT_CONFIG(enable_pressure_advance) &&
|
||||||
|
FILAMENT_CONFIG(adaptive_pressure_advance_overhangs) &&
|
||||||
|
!evaluate_adaptive_pa){
|
||||||
if(writer().get_current_speed() > F){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
|
if(writer().get_current_speed() > F){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
|
||||||
if(m_config.gcode_comments){
|
if(m_config.gcode_comments){
|
||||||
sprintf(buf, "; Ramp down-non-variable\n");
|
sprintf(buf, "; Ramp down-non-variable\n");
|
||||||
@@ -8608,7 +8622,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
_mm3_per_mm,
|
_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -8623,7 +8637,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
_mm3_per_mm,
|
_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -8829,7 +8843,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
// ORCA: Adaptive PA code segment when adjusting PA within the same feature
|
// ORCA: Adaptive PA code segment when adjusting PA within the same feature
|
||||||
// There is a speed change or flow change so emit the flag to evaluate PA for the upcomming extrusion
|
// There is a speed change or flow change so emit the flag to evaluate PA for the upcomming extrusion
|
||||||
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
|
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
|
||||||
if(_mm3_per_mm >0 && adaptive_pa_overhangs){
|
if(_mm3_per_mm >0 &&
|
||||||
|
EXTRUDER_CONFIG(adaptive_pressure_advance) &&
|
||||||
|
EXTRUDER_CONFIG(enable_pressure_advance) &&
|
||||||
|
EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) ){
|
||||||
if(last_set_speed > new_speed){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
|
if(last_set_speed > new_speed){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
|
||||||
if(m_config.gcode_comments) {
|
if(m_config.gcode_comments) {
|
||||||
sprintf(buf, "; Ramp up-variable\n");
|
sprintf(buf, "; Ramp up-variable\n");
|
||||||
@@ -8837,7 +8854,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
_mm3_per_mm,
|
_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -8852,7 +8869,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
|
||||||
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
|
||||||
pa_config_index,
|
m_writer.filament()->id(),
|
||||||
_mm3_per_mm,
|
_mm3_per_mm,
|
||||||
acceleration_i,
|
acceleration_i,
|
||||||
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
|
||||||
@@ -9405,29 +9422,33 @@ void GCode::update_placeholder_parser_with_variant_params()
|
|||||||
if (num_filaments == 0)
|
if (num_filaments == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Helper: remap config arrays from variant index space to filament_id index space.
|
// Helpers: remap config arrays from variant index space to filament_id index space.
|
||||||
// After remapping, gcode templates can use param[filament_id] directly.
|
// After remapping, gcode templates can use param[filament_id] directly.
|
||||||
auto remap_by_filament = [&](const auto &src) {
|
auto remap_floats_by_filament = [&](const auto &src) {
|
||||||
std::decay_t<decltype(src.values)> dst(num_filaments);
|
std::vector<double> dst(num_filaments);
|
||||||
|
for (size_t i = 0; i < num_filaments; ++i)
|
||||||
|
dst[i] = src.get_at(get_filament_config_index(i));
|
||||||
|
return dst;
|
||||||
|
};
|
||||||
|
auto remap_ints_by_filament = [&](const auto &src) {
|
||||||
|
std::vector<int> dst(num_filaments);
|
||||||
for (size_t i = 0; i < num_filaments; ++i)
|
for (size_t i = 0; i < num_filaments; ++i)
|
||||||
dst[i] = src.get_at(get_filament_config_index(i));
|
dst[i] = src.get_at(get_filament_config_index(i));
|
||||||
return dst;
|
return dst;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- filament_options_with_variant: gcode indexes by filament_id ---
|
// --- filament_options_with_variant: gcode indexes by filament_id ---
|
||||||
this->placeholder_parser().set("filament_max_volumetric_speed", new ConfigOptionFloats(remap_by_filament(m_config.filament_max_volumetric_speed)));
|
this->placeholder_parser().set("filament_max_volumetric_speed", new ConfigOptionFloats(remap_floats_by_filament(m_config.filament_max_volumetric_speed)));
|
||||||
this->placeholder_parser().set("filament_pre_cooling_temperature", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature)));
|
this->placeholder_parser().set("filament_pre_cooling_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.filament_pre_cooling_temperature)));
|
||||||
this->placeholder_parser().set("filament_pre_cooling_temperature_nc", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature_nc)));
|
this->placeholder_parser().set("filament_pre_cooling_temperature_nc", new ConfigOptionInts(remap_ints_by_filament(m_config.filament_pre_cooling_temperature_nc)));
|
||||||
this->placeholder_parser().set("filament_cooling_before_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_cooling_before_tower)));
|
this->placeholder_parser().set("filament_cooling_before_tower", new ConfigOptionFloats(remap_floats_by_filament(m_config.filament_cooling_before_tower)));
|
||||||
this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
|
this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature_initial_layer)));
|
||||||
this->placeholder_parser().set("nozzle_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature)));
|
this->placeholder_parser().set("nozzle_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature)));
|
||||||
// first_layer_temperature is a legacy alias of nozzle_temperature_initial_layer
|
// first_layer_temperature is a legacy alias of nozzle_temperature_initial_layer
|
||||||
this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
|
this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature_initial_layer)));
|
||||||
this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance)));
|
|
||||||
this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance)));
|
|
||||||
|
|
||||||
// --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed ---
|
// --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed ---
|
||||||
this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut)));
|
this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_floats_by_filament(m_config.retraction_distances_when_cut)));
|
||||||
// hotend_cooling_rate / hotend_heating_rate: gcode uses [filament_map[x]-1] (extruder_id), no remap needed
|
// hotend_cooling_rate / hotend_heating_rate: gcode uses [filament_map[x]-1] (extruder_id), no remap needed
|
||||||
|
|
||||||
// --- filament_map: per-layer dynamic, sync from m_config to placeholder_parser ---
|
// --- filament_map: per-layer dynamic, sync from m_config to placeholder_parser ---
|
||||||
@@ -9437,9 +9458,9 @@ void GCode::update_placeholder_parser_with_variant_params()
|
|||||||
{
|
{
|
||||||
// Fast purge mode uses filament_flush_temp_fast; Default is inert.
|
// Fast purge mode uses filament_flush_temp_fast; Default is inert.
|
||||||
bool use_fast_flush = m_config.prime_volume_mode == PrimeVolumeMode::pvmFast;
|
bool use_fast_flush = m_config.prime_volume_mode == PrimeVolumeMode::pvmFast;
|
||||||
auto flush_v_speed = remap_by_filament(m_config.filament_flush_volumetric_speed);
|
auto flush_v_speed = remap_floats_by_filament(m_config.filament_flush_volumetric_speed);
|
||||||
auto filament_max_v = remap_by_filament(m_config.filament_max_volumetric_speed);
|
auto filament_max_v = remap_floats_by_filament(m_config.filament_max_volumetric_speed);
|
||||||
auto flush_temps = remap_by_filament(use_fast_flush ? m_config.filament_flush_temp_fast
|
auto flush_temps = remap_ints_by_filament(use_fast_flush ? m_config.filament_flush_temp_fast
|
||||||
: m_config.filament_flush_temp);
|
: m_config.filament_flush_temp);
|
||||||
for (size_t i = 0; i < num_filaments; ++i) {
|
for (size_t i = 0; i < num_filaments; ++i) {
|
||||||
if (flush_v_speed[i] == 0)
|
if (flush_v_speed[i] == 0)
|
||||||
@@ -9492,7 +9513,12 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
|||||||
gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
|
gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
|
||||||
check_add_eol(gcode);
|
check_add_eol(gcode);
|
||||||
}
|
}
|
||||||
gcode += set_filament_pressure_advance(new_filament_id);
|
if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
|
||||||
|
gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id));
|
||||||
|
// Orca: Adaptive PA
|
||||||
|
// Reset Adaptive PA processor last PA value
|
||||||
|
m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id));
|
||||||
|
}
|
||||||
|
|
||||||
gcode += m_writer.toolchange(new_filament_id, new_extruder_id);
|
gcode += m_writer.toolchange(new_filament_id, new_extruder_id);
|
||||||
if (Extruder *fil = m_writer.filament())
|
if (Extruder *fil = m_writer.filament())
|
||||||
@@ -9883,24 +9909,18 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
|||||||
if (m_ooze_prevention.enable && !defer_temp_wait)
|
if (m_ooze_prevention.enable && !defer_temp_wait)
|
||||||
gcode += m_ooze_prevention.post_toolchange(*this);
|
gcode += m_ooze_prevention.post_toolchange(*this);
|
||||||
|
|
||||||
gcode += set_filament_pressure_advance(new_filament_id);
|
if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
|
||||||
|
gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id));
|
||||||
|
// Orca: Adaptive PA
|
||||||
|
// Reset Adaptive PA processor last PA value
|
||||||
|
m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id));
|
||||||
|
}
|
||||||
//Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
|
//Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
|
||||||
m_last_pos_defined = false;
|
m_last_pos_defined = false;
|
||||||
|
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GCode::set_filament_pressure_advance(unsigned int filament_id)
|
|
||||||
{
|
|
||||||
const size_t fi = get_filament_config_index(filament_id);
|
|
||||||
if (!m_config.enable_pressure_advance.get_at(fi))
|
|
||||||
return {};
|
|
||||||
// Orca: Adaptive PA
|
|
||||||
// Reset Adaptive PA processor last PA value
|
|
||||||
m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(fi));
|
|
||||||
return m_writer.set_pressure_advance(m_config.pressure_advance.get_at(fi));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space = false) {
|
inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space = false) {
|
||||||
std::ostringstream gcode;
|
std::ostringstream gcode;
|
||||||
gcode << "[";
|
gcode << "[";
|
||||||
|
|||||||
@@ -281,8 +281,6 @@ public:
|
|||||||
// extra_retract forwards a PETG pre-extrusion over-extrusion; default 0 -> identical to the plain deretract.
|
// extra_retract forwards a PETG pre-extrusion over-extrusion; default 0 -> identical to the plain deretract.
|
||||||
std::string unretract(float extra_retract = 0.f) { return m_writer.unlift() + m_writer.unretract(extra_retract); }
|
std::string unretract(float extra_retract = 0.f) { return m_writer.unlift() + m_writer.unretract(extra_retract); }
|
||||||
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1, bool defer_temp_wait = false);
|
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1, bool defer_temp_wait = false);
|
||||||
// Sets the pressure advance of the filament's extruder variant, if enabled for it.
|
|
||||||
std::string set_filament_pressure_advance(unsigned int filament_id);
|
|
||||||
bool is_BBL_Printer();
|
bool is_BBL_Printer();
|
||||||
WipeTowerType wipe_tower_type();
|
WipeTowerType wipe_tower_type();
|
||||||
|
|
||||||
|
|||||||
@@ -21,30 +21,38 @@ namespace Slic3r {
|
|||||||
*
|
*
|
||||||
* @param gcodegen A reference to the GCode object that generates the G-code.
|
* @param gcodegen A reference to the GCode object that generates the G-code.
|
||||||
*/
|
*/
|
||||||
AdaptivePAProcessor::AdaptivePAProcessor(GCode &gcodegen)
|
AdaptivePAProcessor::AdaptivePAProcessor(GCode &gcodegen, const std::vector<unsigned int> &tools_used)
|
||||||
: m_gcodegen(gcodegen),
|
: m_gcodegen(gcodegen),
|
||||||
m_config(gcodegen.config()),
|
m_config(gcodegen.config()),
|
||||||
m_last_predicted_pa(0.0),
|
m_last_predicted_pa(0.0),
|
||||||
m_max_next_feedrate(0.0),
|
m_max_next_feedrate(0.0),
|
||||||
m_next_feedrate(0.0),
|
m_next_feedrate(0.0),
|
||||||
m_current_feedrate(0.0),
|
m_current_feedrate(0.0),
|
||||||
m_last_config_index(-1),
|
m_last_extruder_id(-1),
|
||||||
m_pa_change_pattern(R"(; PA_CHANGE:T(\d+) MM3MM:([0-9]*\.[0-9]+) ACCEL:(\d+) BR:(\d+) RC:(\d+) OV:(\d+))"),
|
m_pa_change_pattern(R"(; PA_CHANGE:T(\d+) MM3MM:([0-9]*\.[0-9]+) ACCEL:(\d+) BR:(\d+) RC:(\d+) OV:(\d+))"),
|
||||||
m_g1_f_pattern(R"(G1 F([0-9]+))")
|
m_g1_f_pattern(R"(G1 F([0-9]+))")
|
||||||
{
|
{
|
||||||
|
// Constructor body can be used for further initialization if necessary
|
||||||
|
for (unsigned int tool : tools_used) {
|
||||||
|
// Only enable model for the tool if both PA and adaptive PA options are enabled
|
||||||
|
if(m_config.adaptive_pressure_advance.get_at(tool) && m_config.enable_pressure_advance.get_at(tool)){
|
||||||
|
auto interpolator = std::make_unique<AdaptivePAInterpolator>();
|
||||||
|
// Get calibration values from extruder
|
||||||
|
std::string pa_calibration_values = m_config.adaptive_pressure_advance_model.get_at(tool);
|
||||||
|
// Setup the model and store it in the tool-interpolation model map
|
||||||
|
interpolator->parseAndSetData(pa_calibration_values);
|
||||||
|
m_AdaptivePAInterpolators[tool] = std::move(interpolator);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to get the interpolator for a specific filament config index.
|
// Method to get the interpolator for a specific tool ID
|
||||||
// The model is built the first time an index is requested, as the indices in use depend on
|
AdaptivePAInterpolator* AdaptivePAProcessor::getInterpolator(unsigned int tool_id) {
|
||||||
// the extruder variant each filament prints with.
|
auto it = m_AdaptivePAInterpolators.find(tool_id);
|
||||||
AdaptivePAInterpolator* AdaptivePAProcessor::getInterpolator(unsigned int config_index) {
|
if (it != m_AdaptivePAInterpolators.end()) {
|
||||||
auto [it, inserted] = m_AdaptivePAInterpolators.try_emplace(config_index);
|
|
||||||
// Only enable model for the index if both PA and adaptive PA options are enabled
|
|
||||||
if (inserted && m_config.adaptive_pressure_advance.get_at(config_index) && m_config.enable_pressure_advance.get_at(config_index)) {
|
|
||||||
it->second = std::make_unique<AdaptivePAInterpolator>();
|
|
||||||
it->second->parseAndSetData(m_config.adaptive_pressure_advance_model.get_at(config_index));
|
|
||||||
}
|
|
||||||
return it->second.get();
|
return it->second.get();
|
||||||
|
}
|
||||||
|
return nullptr; // Handle the case where the tool_id was not found
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,17 +108,16 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
|
|||||||
// the PA for that material is set. As no tag below will be found for this extruder, the original PA is retained.
|
// the PA for that material is set. As no tag below will be found for this extruder, the original PA is retained.
|
||||||
if (line.find("; PA_CHANGE") == 0) { // prune lines quickly before running regex check as regex is more expensive to run
|
if (line.find("; PA_CHANGE") == 0) { // prune lines quickly before running regex check as regex is more expensive to run
|
||||||
if (std::regex_search(line, m_match, m_pa_change_pattern)) {
|
if (std::regex_search(line, m_match, m_pa_change_pattern)) {
|
||||||
// The tag carries the filament config index, which selects the PA settings of the filament's extruder variant
|
int extruder_id = std::stoi(m_match[1].str());
|
||||||
int config_index = std::stoi(m_match[1].str());
|
|
||||||
mm3mm_value = std::stod(m_match[2].str());
|
mm3mm_value = std::stod(m_match[2].str());
|
||||||
accel_value = std::stod(m_match[3].str());
|
accel_value = std::stod(m_match[3].str());
|
||||||
int isBridge = std::stoi(m_match[4].str());
|
int isBridge = std::stoi(m_match[4].str());
|
||||||
int roleChange = std::stoi(m_match[5].str());
|
int roleChange = std::stoi(m_match[5].str());
|
||||||
int isOverhang = std::stoi(m_match[6].str());
|
int isOverhang = std::stoi(m_match[6].str());
|
||||||
|
|
||||||
// Check if the filament config index has changed
|
// Check if the extruder ID has changed
|
||||||
bool config_index_changed = (config_index != m_last_config_index);
|
bool extruder_changed = (extruder_id != m_last_extruder_id);
|
||||||
m_last_config_index = config_index;
|
m_last_extruder_id = extruder_id;
|
||||||
|
|
||||||
// Save the PA_CHANGE line to output later after finding feedrate
|
// Save the PA_CHANGE line to output later after finding feedrate
|
||||||
pa_change_line = line;
|
pa_change_line = line;
|
||||||
@@ -205,23 +212,23 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
|
|||||||
|
|
||||||
// Calculate the predicted PA using the upcomming feature maximum feedrate
|
// Calculate the predicted PA using the upcomming feature maximum feedrate
|
||||||
// Get the interpolator for the active tool
|
// Get the interpolator for the active tool
|
||||||
AdaptivePAInterpolator* interpolator = getInterpolator(m_last_config_index);
|
AdaptivePAInterpolator* interpolator = getInterpolator(m_last_extruder_id);
|
||||||
|
|
||||||
double predicted_pa = 0;
|
double predicted_pa = 0;
|
||||||
double adaptive_PA_speed = 0;
|
double adaptive_PA_speed = 0;
|
||||||
|
|
||||||
if(!interpolator){ // Tool not found in the interpolator map
|
if(!interpolator){ // Tool not found in the interpolator map
|
||||||
// Tool not found in the PA interpolator to tool map
|
// Tool not found in the PA interpolator to tool map
|
||||||
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
|
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
|
||||||
if(m_config.gcode_comments) output << "; APA: Tool doesnt have APA enabled\n";
|
if(m_config.gcode_comments) output << "; APA: Tool doesnt have APA enabled\n";
|
||||||
} else if (!interpolator->isInitialised() || (!m_config.adaptive_pressure_advance.get_at(m_last_config_index)) )
|
} else if (!interpolator->isInitialised() || (!m_config.adaptive_pressure_advance.get_at(m_last_extruder_id)) )
|
||||||
// Check if the model is not initialised by the constructor for the active extruder
|
// Check if the model is not initialised by the constructor for the active extruder
|
||||||
// Also check that adaptive PA is enabled for that extruder. This should not be needed
|
// Also check that adaptive PA is enabled for that extruder. This should not be needed
|
||||||
// as the PA change flag should not be set upstream (in the GCode.cpp file) if adaptive PA is disabled
|
// as the PA change flag should not be set upstream (in the GCode.cpp file) if adaptive PA is disabled
|
||||||
// however check for robustness sake.
|
// however check for robustness sake.
|
||||||
{
|
{
|
||||||
// Model failed or adaptive pressure advance not enabled - use default value from m_config
|
// Model failed or adaptive pressure advance not enabled - use default value from m_config
|
||||||
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
|
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
|
||||||
if(m_config.gcode_comments) output << "; APA: Interpolator setup failed, using default pressure advance\n";
|
if(m_config.gcode_comments) output << "; APA: Interpolator setup failed, using default pressure advance\n";
|
||||||
} else { // Model setup succeeded
|
} else { // Model setup succeeded
|
||||||
// Proceed to identify the print speed to use to calculate the adaptive PA value
|
// Proceed to identify the print speed to use to calculate the adaptive PA value
|
||||||
@@ -242,18 +249,18 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
|
|||||||
predicted_pa = (*interpolator)(mm3mm_value * adaptive_PA_speed, accel_value);
|
predicted_pa = (*interpolator)(mm3mm_value * adaptive_PA_speed, accel_value);
|
||||||
|
|
||||||
// This is a bridge, use the dedicated PA setting.
|
// This is a bridge, use the dedicated PA setting.
|
||||||
if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
|
if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id) > EPSILON)
|
||||||
predicted_pa = m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index);
|
predicted_pa = m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id);
|
||||||
|
|
||||||
if (predicted_pa < 0) { // If extrapolation fails, fall back to the default PA for the extruder.
|
if (predicted_pa < 0) { // If extrapolation fails, fall back to the default PA for the extruder.
|
||||||
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
|
predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
|
||||||
if(m_config.gcode_comments) output << "; APA: Interpolation failed, using fallback pressure advance value\n";
|
if(m_config.gcode_comments) output << "; APA: Interpolation failed, using fallback pressure advance value\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(m_config.gcode_comments) {
|
if(m_config.gcode_comments) {
|
||||||
// Output debug GCode comments
|
// Output debug GCode comments
|
||||||
output << pa_change_line << '\n'; // Output PA change command tag
|
output << pa_change_line << '\n'; // Output PA change command tag
|
||||||
if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
|
if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id) > EPSILON)
|
||||||
output << "; APA Model Override (bridge)\n";
|
output << "; APA Model Override (bridge)\n";
|
||||||
output << "; APA Current Speed: " << std::to_string(m_current_feedrate) << "\n";
|
output << "; APA Current Speed: " << std::to_string(m_current_feedrate) << "\n";
|
||||||
output << "; APA Next Speed: " << std::to_string(m_next_feedrate) << "\n";
|
output << "; APA Next Speed: " << std::to_string(m_next_feedrate) << "\n";
|
||||||
@@ -262,7 +269,7 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
|
|||||||
output << "; APA Flow rate: " << std::to_string(mm3mm_value * m_max_next_feedrate) << "\n";
|
output << "; APA Flow rate: " << std::to_string(mm3mm_value * m_max_next_feedrate) << "\n";
|
||||||
output << "; APA Prev PA: " << std::to_string(m_last_predicted_pa) << " New PA: " << std::to_string(predicted_pa) << "\n";
|
output << "; APA Prev PA: " << std::to_string(m_last_predicted_pa) << " New PA: " << std::to_string(predicted_pa) << "\n";
|
||||||
}
|
}
|
||||||
if (config_index_changed || std::fabs(predicted_pa - m_last_predicted_pa) > EPSILON) {
|
if (extruder_changed || std::fabs(predicted_pa - m_last_predicted_pa) > EPSILON) {
|
||||||
output << m_gcodegen.writer().set_pressure_advance(predicted_pa); // Use m_writer to set pressure advance
|
output << m_gcodegen.writer().set_pressure_advance(predicted_pa); // Use m_writer to set pressure advance
|
||||||
m_last_predicted_pa = predicted_pa; // Update the last predicted PA value
|
m_last_predicted_pa = predicted_pa; // Update the last predicted PA value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
#include "AdaptivePAInterpolator.hpp"
|
#include "AdaptivePAInterpolator.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -32,7 +33,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param gcodegen A reference to the GCode object that generates the G-code.
|
* @param gcodegen A reference to the GCode object that generates the G-code.
|
||||||
*/
|
*/
|
||||||
AdaptivePAProcessor(GCode &gcodegen);
|
AdaptivePAProcessor(GCode &gcodegen, const std::vector<unsigned int> &tools_used);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Processes a layer of G-code and applies adaptive pressure advance.
|
* @brief Processes a layer of G-code and applies adaptive pressure advance.
|
||||||
@@ -69,25 +70,28 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
GCode &m_gcodegen; ///< Reference to the GCode object.
|
GCode &m_gcodegen; ///< Reference to the GCode object.
|
||||||
std::unordered_map<unsigned int, std::unique_ptr<AdaptivePAInterpolator>> m_AdaptivePAInterpolators; ///< Map between Interpolator objects and filament config indices (null when adaptive PA is off)
|
std::unordered_map<unsigned int, std::unique_ptr<AdaptivePAInterpolator>> m_AdaptivePAInterpolators; ///< Map between Interpolator objects and tool ID's
|
||||||
const PrintConfig &m_config; ///< Reference to the print configuration.
|
const PrintConfig &m_config; ///< Reference to the print configuration.
|
||||||
double m_last_predicted_pa; ///< Last predicted pressure advance value.
|
double m_last_predicted_pa; ///< Last predicted pressure advance value.
|
||||||
double m_max_next_feedrate; ///< Maximum feed rate (speed) for the upcomming island. If no speed is found, the previous island speed is used.
|
double m_max_next_feedrate; ///< Maximum feed rate (speed) for the upcomming island. If no speed is found, the previous island speed is used.
|
||||||
double m_next_feedrate; ///< First feed rate (speed) for the upcomming island.
|
double m_next_feedrate; ///< First feed rate (speed) for the upcomming island.
|
||||||
double m_current_feedrate; ///< Current, latest feedrate.
|
double m_current_feedrate; ///< Current, latest feedrate.
|
||||||
int m_last_config_index; ///< Filament config index of the last PA_CHANGE tag.
|
int m_last_extruder_id; ///< Last used extruder ID.
|
||||||
|
|
||||||
std::regex m_pa_change_pattern; ///< Regular expression to detect PA_CHANGE pattern.
|
std::regex m_pa_change_pattern; ///< Regular expression to detect PA_CHANGE pattern.
|
||||||
std::regex m_g1_f_pattern; ///< Regular expression to detect G1 F pattern.
|
std::regex m_g1_f_pattern; ///< Regular expression to detect G1 F pattern.
|
||||||
std::smatch m_match; ///< Match results for regular expressions.
|
std::smatch m_match; ///< Match results for regular expressions.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the PA interpolator attached to the specified filament config index.
|
* @brief Get the PA interpolator attached to the specified tool ID.
|
||||||
*
|
*
|
||||||
* @param config_index The filament config index (one per filament and extruder variant) for which the PA interpolation model is to be returned.
|
* This method manually sets the adaptive PA internally held value.
|
||||||
* @return The Adaptive PA Interpolator object corresponding to that index, or nullptr when adaptive PA is off for it.
|
* Call this when changing tools or in any other case where the internally assumed last PA value may be incorrect
|
||||||
|
*
|
||||||
|
* @param An integer with the tool ID for which the PA interpolation model is to be returned.
|
||||||
|
* @return The Adaptive PA Interpolator object corresponding to that tool.
|
||||||
*/
|
*/
|
||||||
AdaptivePAInterpolator* getInterpolator(unsigned int config_index);
|
AdaptivePAInterpolator* getInterpolator(unsigned int tool_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
+5
-11
@@ -2439,18 +2439,12 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
|||||||
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
||||||
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
||||||
|
|
||||||
// check adaptive pressure advance model of every extruder variant column of the used filaments
|
// check adaptive pressure advance model
|
||||||
const std::vector<int> &self_index = m_config.filament_self_index.values;
|
for (unsigned int extruder_id : extruders) {
|
||||||
const size_t pa_columns = std::max(m_config.adaptive_pressure_advance_model.size(), size_t(extruders.back()) + 1);
|
if (m_config.adaptive_pressure_advance.get_at(extruder_id) &&
|
||||||
for (size_t column = 0; column < pa_columns; ++column) {
|
m_config.enable_pressure_advance.get_at(extruder_id)) {
|
||||||
// filament_self_index maps a column to its filament once the filament arrays hold one column per variant
|
|
||||||
const unsigned int filament_id = self_index.size() == pa_columns ? self_index[column] - 1 : column;
|
|
||||||
if (!std::binary_search(extruders.begin(), extruders.end(), filament_id))
|
|
||||||
continue;
|
|
||||||
if (m_config.adaptive_pressure_advance.get_at(column) &&
|
|
||||||
m_config.enable_pressure_advance.get_at(column)) {
|
|
||||||
|
|
||||||
const std::string pa_model = m_config.adaptive_pressure_advance_model.get_at(column);
|
const std::string pa_model = m_config.adaptive_pressure_advance_model.get_at(extruder_id);
|
||||||
if (!pa_model.empty()) {
|
if (!pa_model.empty()) {
|
||||||
std::string validation_error = AdaptivePAProcessor::validate_adaptive_pa_model(pa_model);
|
std::string validation_error = AdaptivePAProcessor::validate_adaptive_pa_model(pa_model);
|
||||||
if (!validation_error.empty()) {
|
if (!validation_error.empty()) {
|
||||||
|
|||||||
@@ -9403,10 +9403,6 @@ void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config)
|
|||||||
}
|
}
|
||||||
config.set_key_value("wiping_volumes_use_custom_matrix", new ConfigOptionBool(custom));
|
config.set_key_value("wiping_volumes_use_custom_matrix", new ConfigOptionBool(custom));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orca: a config saved before a key joined filament_options_with_variant stores it once per filament
|
|
||||||
// rather than once per filament variant, and one exported by an older CLI may store a single value.
|
|
||||||
normalize_filament_values_to_variants(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrintConfigDef print_config_def;
|
const PrintConfigDef print_config_def;
|
||||||
@@ -9509,13 +9505,6 @@ std::set<std::string> filament_options_with_variant = {
|
|||||||
"filament_ironing_spacing",
|
"filament_ironing_spacing",
|
||||||
"filament_ironing_inset",
|
"filament_ironing_inset",
|
||||||
"filament_ironing_speed",
|
"filament_ironing_speed",
|
||||||
// Orca: pressure advance
|
|
||||||
"enable_pressure_advance",
|
|
||||||
"pressure_advance",
|
|
||||||
"adaptive_pressure_advance",
|
|
||||||
"adaptive_pressure_advance_model",
|
|
||||||
"adaptive_pressure_advance_overhangs",
|
|
||||||
"adaptive_pressure_advance_bridges",
|
|
||||||
"activate_air_filtration",
|
"activate_air_filtration",
|
||||||
"activate_air_filtration_during_print",
|
"activate_air_filtration_during_print",
|
||||||
"activate_air_filtration_on_completion",
|
"activate_air_filtration_on_completion",
|
||||||
@@ -10723,25 +10712,6 @@ void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVect
|
|||||||
target.set_to_index(&source, indices, stride);
|
target.set_to_index(&source, indices, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
void normalize_filament_values_to_variants(DynamicPrintConfig &config)
|
|
||||||
{
|
|
||||||
const auto *self_index = config.option<ConfigOptionInts>("filament_self_index");
|
|
||||||
if (self_index == nullptr || self_index->empty())
|
|
||||||
return;
|
|
||||||
const int filament_count = *std::max_element(self_index->values.begin(), self_index->values.end());
|
|
||||||
if (filament_count <= 0 || size_t(filament_count) >= self_index->size())
|
|
||||||
return;
|
|
||||||
for (const std::string &key : filament_options_with_variant) {
|
|
||||||
auto *opt = dynamic_cast<ConfigOptionVectorBase *>(config.option(key));
|
|
||||||
if (opt == nullptr || (opt->size() != size_t(filament_count) && opt->size() != 1))
|
|
||||||
continue;
|
|
||||||
std::unique_ptr<ConfigOption> per_filament(opt->clone());
|
|
||||||
// set_at() takes the first value for a filament past the end of a single-value vector
|
|
||||||
for (size_t variant = 0; variant < self_index->size(); ++variant)
|
|
||||||
opt->set_at(per_filament.get(), variant, self_index->values[variant] - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//used for object/region config
|
//used for object/region config
|
||||||
//use the smallest of multiple to single
|
//use the smallest of multiple to single
|
||||||
|
|||||||
@@ -884,11 +884,6 @@ extern std::set<std::string> empty_options;
|
|||||||
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
|
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
|
||||||
const std::vector<int> &variant_index, int stride = 1);
|
const std::vector<int> &variant_index, int stride = 1);
|
||||||
|
|
||||||
// Orca: lays every filament_options_with_variant option out one value per filament variant, as
|
|
||||||
// filament_self_index maps the variants to filaments. An option holding one value per filament, or a
|
|
||||||
// single value, gives every variant of a filament that filament's value; other lengths are left alone.
|
|
||||||
void normalize_filament_values_to_variants(DynamicPrintConfig &config);
|
|
||||||
|
|
||||||
extern std::set<std::string> filament_dev_options;
|
extern std::set<std::string> filament_dev_options;
|
||||||
|
|
||||||
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
||||||
|
|||||||
+1
-10
@@ -256,19 +256,10 @@ extern bool is_gallery_file(const std::string& path, char const* type);
|
|||||||
extern bool is_shapes_dir(const std::string& dir);
|
extern bool is_shapes_dir(const std::string& dir);
|
||||||
//BBS: add json support
|
//BBS: add json support
|
||||||
extern bool is_json_file(const std::string& path);
|
extern bool is_json_file(const std::string& path);
|
||||||
// True if rel_path is relative, has no ".." component or embedded NUL and, joined to root, still resolves inside it.
|
// True if rel_path is relative, has no ".." component and, joined to root, still resolves inside it.
|
||||||
// Both '/' and '\\' are treated as separators on every platform, so an archive rejected on one OS
|
// Both '/' and '\\' are treated as separators on every platform, so an archive rejected on one OS
|
||||||
// is rejected on all of them.
|
// is rejected on all of them.
|
||||||
extern bool is_path_within_root(const std::string &rel_path, const boost::filesystem::path &root);
|
extern bool is_path_within_root(const std::string &rel_path, const boost::filesystem::path &root);
|
||||||
// True if a symlink stored at link_rel_path (relative to root) with this target stays inside root: the target
|
|
||||||
// must be relative, and joined to the link's directory it must pass is_path_within_root.
|
|
||||||
extern bool is_symlink_target_within_root(const std::string &link_rel_path, const std::string &target, const boost::filesystem::path &root);
|
|
||||||
// True if path names an entry strictly inside root: it must be spelled with root as its prefix,
|
|
||||||
// and must still resolve inside root once symlinks are followed.
|
|
||||||
extern bool is_absolute_path_within_root(const boost::filesystem::path &path, const boost::filesystem::path &root);
|
|
||||||
// True if a file with this name is of a type that the desktop opens as plain content, so it cannot run code.
|
|
||||||
// Anything unknown is not safe.
|
|
||||||
extern bool is_safe_to_open_file_name(const std::string &file_name);
|
|
||||||
|
|
||||||
// Orca: custom protocal support utils
|
// Orca: custom protocal support utils
|
||||||
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
|
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
#include "miniz_extension.hpp"
|
#include "miniz_extension.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW64__)
|
#if defined(_MSC_VER) || defined(__MINGW64__)
|
||||||
#include "boost/nowide/cstdio.hpp"
|
#include "boost/nowide/cstdio.hpp"
|
||||||
#endif
|
#endif
|
||||||
@@ -118,66 +115,6 @@ std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_
|
|||||||
return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename);
|
return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extract_archive_confined(const std::string &zip_path_utf8, const std::string &dest_dir)
|
|
||||||
{
|
|
||||||
mz_zip_archive archive;
|
|
||||||
mz_zip_zero_struct(&archive);
|
|
||||||
|
|
||||||
if (!open_zip_reader(&archive, zip_path_utf8)) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Unable to open zip reader for " << zip_path_utf8;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
|
||||||
mz_zip_archive_file_stat stat;
|
|
||||||
|
|
||||||
// Validate every entry first so an archive with a single escaping entry leaves no partial output behind.
|
|
||||||
const boost::filesystem::path root(dest_dir);
|
|
||||||
for (mz_uint i = 0; i < num_entries; ++i) {
|
|
||||||
if (mz_zip_reader_file_stat(&archive, i, &stat) && !is_path_within_root(stat.m_filename, root)) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Unzip: rejecting " << zip_path_utf8 << ", entry " << stat.m_filename << " resolves outside " << dest_dir;
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (mz_uint i = 0; i < num_entries; ++i) {
|
|
||||||
if (!mz_zip_reader_file_stat(&archive, i, &stat)) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Unzip: read file stat failed";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const std::string dest_file = dest_dir + "/" + stat.m_filename;
|
|
||||||
try {
|
|
||||||
if (stat.m_is_directory) {
|
|
||||||
const boost::filesystem::path dest_path(dest_file);
|
|
||||||
if (!boost::filesystem::exists(dest_path))
|
|
||||||
boost::filesystem::create_directories(dest_path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (stat.m_uncomp_size == 0) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Unzip: invalid size for file " << stat.m_filename;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Replace a symlink at the destination rather than writing through it.
|
|
||||||
const boost::filesystem::path dest_path(dest_file);
|
|
||||||
if (boost::filesystem::is_symlink(boost::filesystem::symlink_status(dest_path)))
|
|
||||||
boost::filesystem::remove(dest_path);
|
|
||||||
if (!mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_file.c_str(), 0)) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Unzip: extract file " << stat.m_filename << " to dest " << dest_file << " failed";
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Unzip: successfully extract file " << stat.m_file_index << " to " << dest_file;
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Unzip: archive read exception: " << e.what();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MZ_Archive::MZ_Archive()
|
MZ_Archive::MZ_Archive()
|
||||||
{
|
{
|
||||||
mz_zip_zero_struct(&arch);
|
mz_zip_zero_struct(&arch);
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8);
|
|||||||
bool close_zip_reader(mz_zip_archive *zip);
|
bool close_zip_reader(mz_zip_archive *zip);
|
||||||
bool close_zip_writer(mz_zip_archive *zip);
|
bool close_zip_writer(mz_zip_archive *zip);
|
||||||
std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat);
|
std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat);
|
||||||
// Extracts every entry of the archive under dest_dir. Nothing is written if any entry would resolve outside dest_dir.
|
|
||||||
bool extract_archive_confined(const std::string &zip_path_utf8, const std::string &dest_dir);
|
|
||||||
|
|
||||||
class MZ_Archive {
|
class MZ_Archive {
|
||||||
public:
|
public:
|
||||||
|
|||||||
+1
-39
@@ -70,7 +70,6 @@
|
|||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <boost/nowide/fstream.hpp>
|
||||||
@@ -1094,9 +1093,6 @@ bool is_path_within_root(const std::string &rel_path, const boost::filesystem::p
|
|||||||
auto is_separator = [](char c) { return c == '/' || c == '\\'; };
|
auto is_separator = [](char c) { return c == '/' || c == '\\'; };
|
||||||
if (rel_path.empty() || is_separator(rel_path.front()) || (rel_path.size() > 1 && rel_path[1] == ':'))
|
if (rel_path.empty() || is_separator(rel_path.front()) || (rel_path.size() > 1 && rel_path[1] == ':'))
|
||||||
return false;
|
return false;
|
||||||
// The filesystem calls stop at a NUL, so they would act on a shorter path than the one checked here.
|
|
||||||
if (rel_path.find('\0') != std::string::npos)
|
|
||||||
return false;
|
|
||||||
for (size_t start = 0; start <= rel_path.size();) {
|
for (size_t start = 0; start <= rel_path.size();) {
|
||||||
size_t end = start;
|
size_t end = start;
|
||||||
while (end < rel_path.size() && !is_separator(rel_path[end]))
|
while (end < rel_path.size() && !is_separator(rel_path[end]))
|
||||||
@@ -1107,10 +1103,7 @@ bool is_path_within_root(const std::string &rel_path, const boost::filesystem::p
|
|||||||
}
|
}
|
||||||
// Resolve against the canonical root so a symlink inside it cannot lead back out.
|
// Resolve against the canonical root so a symlink inside it cannot lead back out.
|
||||||
try {
|
try {
|
||||||
std::string root_str = boost::filesystem::weakly_canonical(root).string();
|
const std::string root_str = boost::filesystem::weakly_canonical(root).string();
|
||||||
// A trailing separator on root would otherwise fail the prefix match below for every path.
|
|
||||||
while (!root_str.empty() && (root_str.back() == '/' || root_str.back() == boost::filesystem::path::preferred_separator))
|
|
||||||
root_str.pop_back();
|
|
||||||
const std::string full_str = boost::filesystem::weakly_canonical(root / rel_path).string();
|
const std::string full_str = boost::filesystem::weakly_canonical(root / rel_path).string();
|
||||||
return full_str.compare(0, root_str.size(), root_str) == 0 &&
|
return full_str.compare(0, root_str.size(), root_str) == 0 &&
|
||||||
(full_str.size() == root_str.size() || full_str[root_str.size()] == boost::filesystem::path::preferred_separator);
|
(full_str.size() == root_str.size() || full_str[root_str.size()] == boost::filesystem::path::preferred_separator);
|
||||||
@@ -1119,37 +1112,6 @@ bool is_path_within_root(const std::string &rel_path, const boost::filesystem::p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_symlink_target_within_root(const std::string &link_rel_path, const std::string &target, const boost::filesystem::path &root)
|
|
||||||
{
|
|
||||||
if (target.empty() || target.front() == '/' || target.front() == '\\' || (target.size() > 1 && target[1] == ':'))
|
|
||||||
return false;
|
|
||||||
// A relative target without ".." only descends from the link's directory, so no chain of such links can leave root.
|
|
||||||
const size_t sep = link_rel_path.find_last_of("/\\");
|
|
||||||
return is_path_within_root((sep == std::string::npos ? std::string() : link_rel_path.substr(0, sep + 1)) + target, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_absolute_path_within_root(const boost::filesystem::path &path, const boost::filesystem::path &root)
|
|
||||||
{
|
|
||||||
const boost::filesystem::path rel = path.lexically_relative(root);
|
|
||||||
return !rel.empty() && rel != "." && is_path_within_root(rel.string(), root);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_safe_to_open_file_name(const std::string &file_name)
|
|
||||||
{
|
|
||||||
// Formats that cannot carry macros or scripts. Legacy and OpenDocument office files, HTML and SVG are left out on purpose.
|
|
||||||
static const std::vector<std::string> safe_extensions = {
|
|
||||||
"jpg", "jpeg", "jfif", "pjpeg", "pjp", "png", "gif", "bmp", "webp", "tif", "tiff",
|
|
||||||
"pdf", "txt", "md", "csv", "docx", "xlsx", "pptx",
|
|
||||||
"stl", "obj", "3mf", "amf", "ply", "step", "stp", "iges", "igs", "dxf",
|
|
||||||
"mp4", "mov", "webm"};
|
|
||||||
// The name must end in the extension itself: Windows drops trailing dots and spaces and reads ':' as a stream separator.
|
|
||||||
const size_t dot = file_name.find_last_of('.');
|
|
||||||
if (dot == std::string::npos || file_name.find_first_of("/\\:") != std::string::npos)
|
|
||||||
return false;
|
|
||||||
const std::string extension = boost::algorithm::to_lower_copy(file_name.substr(dot + 1));
|
|
||||||
return std::find(safe_extensions.begin(), safe_extensions.end(), extension) != safe_extensions.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_img_file(const std::string &path)
|
bool is_img_file(const std::string &path)
|
||||||
{
|
{
|
||||||
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
|
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
|
||||||
|
|||||||
@@ -158,6 +158,21 @@ enum class EGCodeExtrusionRole : uint8_t
|
|||||||
|
|
||||||
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
||||||
|
|
||||||
|
//
|
||||||
|
// What the reduced set, drawn while the user is dragging, holds in place of the full toolpaths
|
||||||
|
//
|
||||||
|
enum class EReducedDetailMode : uint8_t
|
||||||
|
{
|
||||||
|
// nothing: no reduced set is built
|
||||||
|
Off,
|
||||||
|
// only the bottom and top layers of the visible range, for a caller that draws the print
|
||||||
|
// itself some other way
|
||||||
|
EndLayersOnly,
|
||||||
|
// every role but the infill under the skins and the gap fill: what can be seen from outside
|
||||||
|
ShellOnly,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Option types
|
// Option types
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -114,6 +114,19 @@ public:
|
|||||||
//
|
//
|
||||||
bool is_dim_previous_layers() const;
|
bool is_dim_previous_layers() const;
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
|
//
|
||||||
|
// The reduced set drawn while the user drags: what the mode keeps, and always the bottom and
|
||||||
|
// top layers of the visible range. While a mode is set it is built alongside the full set, so
|
||||||
|
// set_reduced_detail() rebuilds nothing. Ignored on the OpenGL ES path.
|
||||||
|
//
|
||||||
|
EReducedDetailMode get_reduced_detail_mode() const;
|
||||||
|
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||||
|
// Whether the shell mode hides the infill roles; off for a profile that leaves them on the
|
||||||
|
// surface, with no top or bottom shell or no walls.
|
||||||
|
bool get_reduced_detail_hide_infill() const;
|
||||||
|
void set_reduced_detail_hide_infill(bool value);
|
||||||
|
void set_reduced_detail(bool value);
|
||||||
|
bool is_reduced_detail() const;
|
||||||
float get_dim_previous_layers_brightness() const;
|
float get_dim_previous_layers_brightness() const;
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ struct Settings
|
|||||||
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
||||||
float dim_previous_layers_brightness{ 0.4f };
|
float dim_previous_layers_brightness{ 0.4f };
|
||||||
bool spiral_vase_mode{ false };
|
bool spiral_vase_mode{ false };
|
||||||
|
// what the reduced set holds and whether it is drawn. Ignored on the OpenGL ES path.
|
||||||
|
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
|
||||||
|
// whether the shell mode may hide the infill roles: false when the profile leaves them on the surface
|
||||||
|
bool reduced_detail_hide_infill{ true };
|
||||||
|
bool reduced_detail{ false };
|
||||||
//
|
//
|
||||||
// Required update flags
|
// Required update flags
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -92,6 +92,36 @@ bool Viewer::is_dim_previous_layers() const
|
|||||||
return m_impl->is_dim_previous_layers();
|
return m_impl->is_dim_previous_layers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail(bool value)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewer::is_reduced_detail() const
|
||||||
|
{
|
||||||
|
return m_impl->is_reduced_detail();
|
||||||
|
}
|
||||||
|
|
||||||
|
EReducedDetailMode Viewer::get_reduced_detail_mode() const
|
||||||
|
{
|
||||||
|
return m_impl->get_reduced_detail_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail_mode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewer::get_reduced_detail_hide_infill() const
|
||||||
|
{
|
||||||
|
return m_impl->get_reduced_detail_hide_infill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::set_reduced_detail_hide_infill(bool value)
|
||||||
|
{
|
||||||
|
m_impl->set_reduced_detail_hide_infill(value);
|
||||||
|
}
|
||||||
|
|
||||||
void Viewer::set_dim_previous_layers(bool value)
|
void Viewer::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
m_impl->set_dim_previous_layers(value);
|
m_impl->set_dim_previous_layers(value);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
namespace libvgcode {
|
namespace libvgcode {
|
||||||
|
|
||||||
@@ -901,9 +902,17 @@ void ViewerImpl::reset()
|
|||||||
#else
|
#else
|
||||||
m_enabled_segments_count = 0;
|
m_enabled_segments_count = 0;
|
||||||
m_enabled_options_count = 0;
|
m_enabled_options_count = 0;
|
||||||
|
m_enabled_segments_reduced_count = 0;
|
||||||
|
m_enabled_options_reduced_count = 0;
|
||||||
|
m_enabled_segments_reduced_tex_size = 0;
|
||||||
|
m_enabled_options_reduced_tex_size = 0;
|
||||||
|
|
||||||
m_settings_used_for_ranges = std::nullopt;
|
m_settings_used_for_ranges = std::nullopt;
|
||||||
|
|
||||||
|
delete_textures(m_enabled_options_reduced_tex_id);
|
||||||
|
delete_buffers(m_enabled_options_reduced_buf_id);
|
||||||
|
delete_textures(m_enabled_segments_reduced_tex_id);
|
||||||
|
delete_buffers(m_enabled_segments_reduced_buf_id);
|
||||||
delete_textures(m_enabled_options_tex_id);
|
delete_textures(m_enabled_options_tex_id);
|
||||||
delete_buffers(m_enabled_options_buf_id);
|
delete_buffers(m_enabled_options_buf_id);
|
||||||
delete_textures(m_enabled_segments_tex_id);
|
delete_textures(m_enabled_segments_tex_id);
|
||||||
@@ -1163,6 +1172,17 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
|
|
||||||
|
// create (but do not fill) the reduced counterparts of the two buffers above
|
||||||
|
glsafe(glGenBuffers(1, &m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glGenTextures(1, &m_enabled_segments_reduced_tex_id));
|
||||||
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_id));
|
||||||
|
|
||||||
|
glsafe(glGenBuffers(1, &m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glGenTextures(1, &m_enabled_options_reduced_tex_id));
|
||||||
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_id));
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
@@ -1174,6 +1194,18 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
update_colors();
|
update_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
// the roles that lie under a skin or between walls, never seen from outside the print
|
||||||
|
static bool is_hidden_in_shell(EGCodeExtrusionRole role)
|
||||||
|
{
|
||||||
|
return role == EGCodeExtrusionRole::InternalInfill ||
|
||||||
|
role == EGCodeExtrusionRole::SolidInfill ||
|
||||||
|
role == EGCodeExtrusionRole::InternalBridgeInfill ||
|
||||||
|
role == EGCodeExtrusionRole::GapFill;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
|
|
||||||
void ViewerImpl::update_enabled_entities()
|
void ViewerImpl::update_enabled_entities()
|
||||||
{
|
{
|
||||||
if (m_vertices.empty())
|
if (m_vertices.empty())
|
||||||
@@ -1181,6 +1213,17 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
|
|
||||||
std::vector<uint32_t> enabled_segments;
|
std::vector<uint32_t> enabled_segments;
|
||||||
std::vector<uint32_t> enabled_options;
|
std::vector<uint32_t> enabled_options;
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
// the reduced set is filled by the same walk, so switching to it costs no rebuild. Whatever the
|
||||||
|
// mode leaves out, the bottom and top layers of the visible range are kept whole: they are the
|
||||||
|
// surfaces the range cuts open
|
||||||
|
const EReducedDetailMode reduced_mode = m_settings.reduced_detail_mode;
|
||||||
|
const bool build_reduced = reduced_mode != EReducedDetailMode::Off;
|
||||||
|
const bool hide_infill = m_settings.reduced_detail_hide_infill;
|
||||||
|
std::vector<uint32_t> enabled_segments_reduced;
|
||||||
|
std::vector<uint32_t> enabled_options_reduced;
|
||||||
|
const Interval& layers_range = m_layers.get_view_range();
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
Interval range = m_view_range.get_visible();
|
Interval range = m_view_range.get_visible();
|
||||||
|
|
||||||
// when top layer only visualization is enabled, we need to render
|
// when top layer only visualization is enabled, we need to render
|
||||||
@@ -1228,6 +1271,20 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
enabled_options.push_back(static_cast<uint32_t>(i));
|
enabled_options.push_back(static_cast<uint32_t>(i));
|
||||||
else
|
else
|
||||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||||
|
|
||||||
|
#ifndef ENABLE_OPENGL_ES
|
||||||
|
if (build_reduced) {
|
||||||
|
const bool end_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
|
||||||
|
if (end_layer)
|
||||||
|
(v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast<uint32_t>(i));
|
||||||
|
else if (reduced_mode == EReducedDetailMode::ShellOnly) {
|
||||||
|
if (v.is_option())
|
||||||
|
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
|
||||||
|
else if (!v.is_extrusion() || !hide_infill || !is_hidden_in_shell(v.role))
|
||||||
|
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
@@ -1256,6 +1313,23 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
else
|
else
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
||||||
|
|
||||||
|
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
|
||||||
|
m_enabled_options_reduced_count = enabled_options_reduced.size();
|
||||||
|
m_enabled_segments_reduced_tex_size = enabled_segments_reduced.size() * sizeof(uint32_t);
|
||||||
|
m_enabled_options_reduced_tex_size = enabled_options_reduced.size() * sizeof(uint32_t);
|
||||||
|
|
||||||
|
// uploaded even when nothing was built, so that the last reduced set is released as soon as
|
||||||
|
// the preference is switched off
|
||||||
|
assert(m_enabled_segments_reduced_buf_id > 0);
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||||
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_size,
|
||||||
|
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
||||||
|
|
||||||
|
assert(m_enabled_options_reduced_buf_id > 0);
|
||||||
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||||
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_size,
|
||||||
|
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
@@ -1463,6 +1537,23 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
|||||||
update_colors_texture();
|
update_colors_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Both decide which vertices land in the reduced set, so the sets are rebuilt.
|
||||||
|
void ViewerImpl::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||||
|
{
|
||||||
|
if (m_settings.reduced_detail_mode == mode)
|
||||||
|
return;
|
||||||
|
m_settings.reduced_detail_mode = mode;
|
||||||
|
m_settings.update_enabled_entities = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewerImpl::set_reduced_detail_hide_infill(bool value)
|
||||||
|
{
|
||||||
|
if (m_settings.reduced_detail_hide_infill == value)
|
||||||
|
return;
|
||||||
|
m_settings.reduced_detail_hide_infill = value;
|
||||||
|
m_settings.update_enabled_entities = true;
|
||||||
|
}
|
||||||
|
|
||||||
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
||||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
@@ -1856,6 +1947,8 @@ size_t ViewerImpl::get_used_gpu_memory() const
|
|||||||
ret += m_colors_tex_size;
|
ret += m_colors_tex_size;
|
||||||
ret += m_enabled_segments_tex_size;
|
ret += m_enabled_segments_tex_size;
|
||||||
ret += m_enabled_options_tex_size;
|
ret += m_enabled_options_tex_size;
|
||||||
|
ret += m_enabled_segments_reduced_tex_size;
|
||||||
|
ret += m_enabled_options_reduced_tex_size;
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -2072,7 +2165,8 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_segments_count() == 0)
|
if (m_texture_data.get_enabled_segments_count() == 0)
|
||||||
#else
|
#else
|
||||||
if (m_enabled_segments_count == 0)
|
const ActiveSet segments = active_segments();
|
||||||
|
if (segments.count == 0)
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2099,7 +2193,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
||||||
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
||||||
#ifndef ENABLE_OPENGL_ES
|
#ifndef ENABLE_OPENGL_ES
|
||||||
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(m_enabled_segments_count)));
|
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(segments.count)));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
||||||
// with the lookup off.
|
// with the lookup off.
|
||||||
@@ -2149,10 +2243,10 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, segments.tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_segments_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, segments.buf_id));
|
||||||
|
|
||||||
m_segment_template.render(m_enabled_segments_count);
|
m_segment_template.render(segments.count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (curr_cull_face)
|
if (curr_cull_face)
|
||||||
@@ -2178,7 +2272,8 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_options_count() == 0)
|
if (m_texture_data.get_enabled_options_count() == 0)
|
||||||
#else
|
#else
|
||||||
if (m_enabled_options_count == 0)
|
const ActiveSet options = active_options();
|
||||||
|
if (options.count == 0)
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2236,10 +2331,10 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, options.tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_options_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, options.buf_id));
|
||||||
|
|
||||||
m_option_template.render(m_enabled_options_count);
|
m_option_template.render(options.count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (!curr_cull_face)
|
if (!curr_cull_face)
|
||||||
|
|||||||
@@ -109,6 +109,21 @@ public:
|
|||||||
// 0.0 = black
|
// 0.0 = black
|
||||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
|
//
|
||||||
|
// Draw from the reduced set; it is already built, so this is just a buffer binding.
|
||||||
|
//
|
||||||
|
void set_reduced_detail(bool value) {
|
||||||
|
#ifdef ENABLE_OPENGL_ES
|
||||||
|
// no reduced set is built on OpenGL ES
|
||||||
|
value = false;
|
||||||
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
m_settings.reduced_detail = value;
|
||||||
|
}
|
||||||
|
bool is_reduced_detail() const { return m_settings.reduced_detail; }
|
||||||
|
EReducedDetailMode get_reduced_detail_mode() const { return m_settings.reduced_detail_mode; }
|
||||||
|
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||||
|
bool get_reduced_detail_hide_infill() const { return m_settings.reduced_detail_hide_infill; }
|
||||||
|
void set_reduced_detail_hide_infill(bool value);
|
||||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
|
|
||||||
@@ -501,6 +516,15 @@ private:
|
|||||||
unsigned int m_enabled_options_tex_id{ 0 };
|
unsigned int m_enabled_options_tex_id{ 0 };
|
||||||
size_t m_enabled_options_count{ 0 };
|
size_t m_enabled_options_count{ 0 };
|
||||||
//
|
//
|
||||||
|
// OpenGL buffers to store the reduced set drawn while Settings::reduced_detail is set
|
||||||
|
//
|
||||||
|
unsigned int m_enabled_segments_reduced_buf_id{ 0 };
|
||||||
|
unsigned int m_enabled_segments_reduced_tex_id{ 0 };
|
||||||
|
size_t m_enabled_segments_reduced_count{ 0 };
|
||||||
|
unsigned int m_enabled_options_reduced_buf_id{ 0 };
|
||||||
|
unsigned int m_enabled_options_reduced_tex_id{ 0 };
|
||||||
|
size_t m_enabled_options_reduced_count{ 0 };
|
||||||
|
//
|
||||||
// Caches for size of data sent to gpu, in bytes
|
// Caches for size of data sent to gpu, in bytes
|
||||||
//
|
//
|
||||||
size_t m_positions_tex_size{ 0 };
|
size_t m_positions_tex_size{ 0 };
|
||||||
@@ -508,6 +532,27 @@ private:
|
|||||||
size_t m_colors_tex_size{ 0 };
|
size_t m_colors_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_tex_size{ 0 };
|
size_t m_enabled_segments_tex_size{ 0 };
|
||||||
size_t m_enabled_options_tex_size{ 0 };
|
size_t m_enabled_options_tex_size{ 0 };
|
||||||
|
size_t m_enabled_segments_reduced_tex_size{ 0 };
|
||||||
|
size_t m_enabled_options_reduced_tex_size{ 0 };
|
||||||
|
|
||||||
|
// The set the next draw reads from: the reduced one while dragging, if one is built.
|
||||||
|
bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off; }
|
||||||
|
struct ActiveSet
|
||||||
|
{
|
||||||
|
size_t count{ 0 };
|
||||||
|
unsigned int buf_id{ 0 };
|
||||||
|
unsigned int tex_id{ 0 };
|
||||||
|
};
|
||||||
|
ActiveSet active_segments() const {
|
||||||
|
if (use_reduced_set())
|
||||||
|
return { m_enabled_segments_reduced_count, m_enabled_segments_reduced_buf_id, m_enabled_segments_reduced_tex_id };
|
||||||
|
return { m_enabled_segments_count, m_enabled_segments_buf_id, m_enabled_segments_tex_id };
|
||||||
|
}
|
||||||
|
ActiveSet active_options() const {
|
||||||
|
if (use_reduced_set())
|
||||||
|
return { m_enabled_options_reduced_count, m_enabled_options_reduced_buf_id, m_enabled_options_reduced_tex_id };
|
||||||
|
return { m_enabled_options_count, m_enabled_options_buf_id, m_enabled_options_tex_id };
|
||||||
|
}
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1019,9 +1019,14 @@ if (UNIX AND NOT APPLE)
|
|||||||
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
||||||
pkg_check_modules(LIBSECRET REQUIRED libsecret-1)
|
pkg_check_modules(LIBSECRET REQUIRED libsecret-1)
|
||||||
pkg_check_modules(webkit2gtk REQUIRED webkit2gtk-4.1)
|
pkg_check_modules(webkit2gtk REQUIRED webkit2gtk-4.1)
|
||||||
|
if (FLATPAK)
|
||||||
|
# I don't know why this is needed, but for whatever reason slic3r isn't
|
||||||
|
# linking to X11 and webkit2gtk. force it.
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
|
target_link_libraries(libslic3r_gui ${X11_LIBRARIES} ${webkit2gtk_LIBRARIES})
|
||||||
|
endif()
|
||||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS} ${LIBSECRET_INCLUDE_DIRS} ${webkit2gtk_INCLUDE_DIRS})
|
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS} ${LIBSECRET_INCLUDE_DIRS} ${webkit2gtk_INCLUDE_DIRS})
|
||||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig ${LIBSECRET_LIBRARIES} ${webkit2gtk_LIBRARIES} ${X11_LIBRARIES})
|
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig ${LIBSECRET_LIBRARIES})
|
||||||
|
|
||||||
# Propagate GDK backend detection results as compile definitions so that
|
# Propagate GDK backend detection results as compile definitions so that
|
||||||
# LinuxDisplayBackend.cpp can include the right GDK headers.
|
# LinuxDisplayBackend.cpp can include the right GDK headers.
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ void AuFile::on_dclick(wxMouseEvent &evt)
|
|||||||
if (m_type == AddFileButton)
|
if (m_type == AddFileButton)
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
desktop_open_project_attachment(this, m_file_path);
|
wxLaunchDefaultApplication(m_file_path.wstring(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuFile::on_mouse_left_up(wxMouseEvent &evt)
|
void AuFile::on_mouse_left_up(wxMouseEvent &evt)
|
||||||
|
|||||||
@@ -171,16 +171,14 @@ void ConfigManipulation::check_adaptive_pressure_advance_model(DynamicPrintConfi
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const auto* model = config->option<ConfigOptionStrings>("adaptive_pressure_advance_model");
|
const auto* model = config->option<ConfigOptionStrings>("adaptive_pressure_advance_model");
|
||||||
if (model == nullptr)
|
if (model == nullptr || model->values.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Each extruder variant holds its own model.
|
std::string raw_model;
|
||||||
std::string error;
|
for (const std::string& chunk : model->values)
|
||||||
for (const std::string& variant_model : model->values) {
|
raw_model += chunk;
|
||||||
error = AdaptivePAProcessor::validate_adaptive_pa_model(variant_model);
|
|
||||||
if (!error.empty())
|
std::string error = AdaptivePAProcessor::validate_adaptive_pa_model(raw_model);
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!error.empty()) {
|
if (!error.empty()) {
|
||||||
wxString msg_text = _L("Adaptive Pressure Advance model validation failed:\n");
|
wxString msg_text = _L("Adaptive Pressure Advance model validation failed:\n");
|
||||||
msg_text += from_u8(error);
|
msg_text += from_u8(error);
|
||||||
|
|||||||
@@ -1267,6 +1267,8 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
if (current_top_layer_only != required_top_layer_only)
|
if (current_top_layer_only != required_top_layer_only)
|
||||||
m_viewer.toggle_top_layer_only_view_range();
|
m_viewer.toggle_top_layer_only_view_range();
|
||||||
|
|
||||||
|
read_reduced_detail_preferences();
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||||
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||||
@@ -1491,6 +1493,10 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
|
|
||||||
// BBS: data for rendering color arrangement recommendation
|
// BBS: data for rendering color arrangement recommendation
|
||||||
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
|
// the shell drag mode hides the infill only where the profile covers it: with no top or bottom
|
||||||
|
// shell, or no walls, the infill is the surface. Per-object settings are not consulted.
|
||||||
|
const PrintRegionConfig& region = print.default_region_config();
|
||||||
|
m_viewer.set_reduced_detail_hide_infill(region.top_shell_layers.value > 0 && region.bottom_shell_layers.value > 0 && region.wall_loops.value > 0);
|
||||||
// Orca hack: Hide filament group for non-bbl printers
|
// Orca hack: Hide filament group for non-bbl printers
|
||||||
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
||||||
std::vector<int> filament_maps = print.get_filament_maps();
|
std::vector<int> filament_maps = print.get_filament_maps();
|
||||||
@@ -1661,6 +1667,7 @@ void GCodeViewer::reset_shell()
|
|||||||
{
|
{
|
||||||
m_shells.volumes.clear();
|
m_shells.volumes.clear();
|
||||||
m_shells.print_id = -1;
|
m_shells.print_id = -1;
|
||||||
|
m_shells.with_wipe_tower = false;
|
||||||
m_shell_bounding_box = BoundingBoxf3();
|
m_shell_bounding_box = BoundingBoxf3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1697,6 +1704,11 @@ void GCodeViewer::reset()
|
|||||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
// while dragging in the solid model mode, the objects stand in for their toolpaths, cut to the
|
||||||
|
// visible layer range; the toolpath set then holds only the range's bottom and top layers
|
||||||
|
if (m_viewer.is_reduced_detail() && solid_model_enabled())
|
||||||
|
render_solid_model(canvas_width, canvas_height);
|
||||||
|
else
|
||||||
render_shells(canvas_width, canvas_height);
|
render_shells(canvas_width, canvas_height);
|
||||||
|
|
||||||
if (m_viewer.get_extrusion_roles_count() == 0)
|
if (m_viewer.get_extrusion_roles_count() == 0)
|
||||||
@@ -2020,6 +2032,56 @@ void GCodeViewer::update_layers_slider_mode()
|
|||||||
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::set_interacting(bool interacting)
|
||||||
|
{
|
||||||
|
// with no shells to stand in for the toolpaths, the solid model would leave only the end layers
|
||||||
|
const bool usable = !solid_model_enabled() || !m_shells.volumes.empty();
|
||||||
|
m_viewer.set_reduced_detail(interacting && usable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::read_reduced_detail_preferences()
|
||||||
|
{
|
||||||
|
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
|
||||||
|
apply_reduced_detail_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::apply_reduced_detail_settings()
|
||||||
|
{
|
||||||
|
m_viewer.set_reduced_detail_mode(m_reduced_detail_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::set_reduced_detail_mode(const std::string& mode)
|
||||||
|
{
|
||||||
|
const bool was_solid = solid_model_enabled();
|
||||||
|
m_reduced_detail_mode = reduced_detail_mode_from_string(mode);
|
||||||
|
apply_reduced_detail_settings();
|
||||||
|
reload_shells_if_solid_model_changed(was_solid);
|
||||||
|
}
|
||||||
|
|
||||||
|
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
|
||||||
|
{
|
||||||
|
if (mode == "solid")
|
||||||
|
return libvgcode::EReducedDetailMode::EndLayersOnly;
|
||||||
|
if (mode == "shell")
|
||||||
|
return libvgcode::EReducedDetailMode::ShellOnly;
|
||||||
|
return libvgcode::EReducedDetailMode::Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeViewer::reload_shells_if_solid_model_changed(bool was_enabled)
|
||||||
|
{
|
||||||
|
if (was_enabled == solid_model_enabled() || m_shells.print_id == -1)
|
||||||
|
return;
|
||||||
|
// only the prime tower comes and goes with the mode: a full reload would drop the shells
|
||||||
|
// whenever the print has moved on since they were loaded, leaving the solid model nothing to draw
|
||||||
|
if (wxGetApp().plater() == nullptr)
|
||||||
|
return;
|
||||||
|
// the shells are loaded from the current plate's print, which is not the plater's own
|
||||||
|
const Print& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
|
||||||
|
if (static_cast<int>(print.id().id) != m_shells.print_id)
|
||||||
|
return;
|
||||||
|
update_shell_wipe_tower(print, m_gl_data_initialized);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
||||||
{
|
{
|
||||||
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
||||||
@@ -2344,7 +2406,11 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||||||
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
||||||
|
// the shells can load before the first G-code does, so the preferences are read here as well
|
||||||
|
read_reduced_detail_preferences();
|
||||||
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
||||||
|
// the prime tower comes and goes on its own, without reloading the objects
|
||||||
|
update_shell_wipe_tower(print, initialized);
|
||||||
//BBS: update force previewing logic
|
//BBS: update force previewing logic
|
||||||
if (force_previewing)
|
if (force_previewing)
|
||||||
m_shells.previewing = force_previewing;
|
m_shells.previewing = force_previewing;
|
||||||
@@ -2453,10 +2519,45 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
|||||||
m_shells.print_id = print.id().id;
|
m_shells.print_id = print.id().id;
|
||||||
m_shells.print_modify_count = print.get_modified_count();
|
m_shells.print_modify_count = print.get_modified_count();
|
||||||
m_shells.previewing = true;
|
m_shells.previewing = true;
|
||||||
|
update_shell_wipe_tower(print, initialized);
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
||||||
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The prime tower as it was sliced, so that the solid model shows what the print shows. It keeps its
|
||||||
|
// opaque colour, so it never appears among the translucent shells, and stays out of their bounding box.
|
||||||
|
void GCodeViewer::update_shell_wipe_tower(const Print& print, bool initialized)
|
||||||
|
{
|
||||||
|
const bool with_wipe_tower = solid_model_enabled() && print.is_step_done(psWipeTower) && print.wipe_tower_data().wipe_tower_mesh_data;
|
||||||
|
if (with_wipe_tower == m_shells.with_wipe_tower)
|
||||||
|
return;
|
||||||
|
m_shells.with_wipe_tower = with_wipe_tower;
|
||||||
|
GLVolumePtrs& volumes = m_shells.volumes.volumes;
|
||||||
|
if (!with_wipe_tower) {
|
||||||
|
volumes.erase(std::remove_if(volumes.begin(), volumes.end(), [](GLVolume* volume) {
|
||||||
|
if (!volume->is_wipe_tower)
|
||||||
|
return false;
|
||||||
|
delete volume;
|
||||||
|
return true;
|
||||||
|
}), volumes.end());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const PrintConfig& config = print.config();
|
||||||
|
const int plate_idx = print.get_plate_index();
|
||||||
|
const Vec3d plate_origin = print.get_plate_origin();
|
||||||
|
const float x = static_cast<float>(config.wipe_tower_x.get_at(plate_idx) + plate_origin.x());
|
||||||
|
const float y = static_cast<float>(config.wipe_tower_y.get_at(plate_idx) + plate_origin.y());
|
||||||
|
const size_t first_new = volumes.size();
|
||||||
|
m_shells.volumes.load_real_wipe_tower_preview(1000 + plate_idx, x, y, print.wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
|
||||||
|
print.wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, true,
|
||||||
|
static_cast<float>(config.wipe_tower_rotation_angle), false, initialized);
|
||||||
|
for (size_t i = first_new; i < volumes.size(); ++i) {
|
||||||
|
volumes[i]->zoom_to_volumes = false;
|
||||||
|
volumes[i]->force_native_color = true;
|
||||||
|
volumes[i]->set_render_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeViewer::render_toolpaths()
|
void GCodeViewer::render_toolpaths()
|
||||||
{
|
{
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
@@ -2657,6 +2758,50 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height)
|
|||||||
glsafe(::glDepthMask(GL_TRUE));
|
glsafe(::glDepthMask(GL_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The sliced objects and the prime tower drawn opaque, in their filament colours, cut to the
|
||||||
|
// visible layer range by the shader's z range. The toolpaths of the range's bottom and top layers
|
||||||
|
// are drawn afterwards and cap the cut.
|
||||||
|
void GCodeViewer::render_solid_model(int canvas_width, int canvas_height)
|
||||||
|
{
|
||||||
|
if (m_shells.volumes.empty())
|
||||||
|
return;
|
||||||
|
// gouraud_light has no z range, so it could not cut the model
|
||||||
|
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||||
|
if (shader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
|
||||||
|
const float z_top = m_viewer.get_layer_z(layers[1]) - m_z_offset + 0.001f;
|
||||||
|
const float z_bottom = (layers[0] > 0) ? m_viewer.get_layer_z(layers[0] - 1) - m_z_offset - 0.001f : -FLT_MAX;
|
||||||
|
|
||||||
|
std::vector<float> alphas;
|
||||||
|
alphas.reserve(m_shells.volumes.volumes.size());
|
||||||
|
for (GLVolume* volume : m_shells.volumes.volumes) {
|
||||||
|
alphas.push_back(volume->color.a());
|
||||||
|
volume->color.a(1.0f);
|
||||||
|
volume->set_render_color();
|
||||||
|
}
|
||||||
|
m_shells.volumes.set_z_range(z_bottom, z_top);
|
||||||
|
// gouraud also clips by this plane, which nothing else sets on the shells
|
||||||
|
m_shells.volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
|
||||||
|
|
||||||
|
shader->start_using();
|
||||||
|
// the 3D view leaves its shadow settings on the shared program
|
||||||
|
shader->set_uniform("shadow_intensity", 0.0f);
|
||||||
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
|
shader->set_uniform("z_far", camera.get_far_z());
|
||||||
|
shader->set_uniform("z_near", camera.get_near_z());
|
||||||
|
m_shells.volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
|
||||||
|
shader->stop_using();
|
||||||
|
|
||||||
|
m_shells.volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
||||||
|
size_t k = 0;
|
||||||
|
for (GLVolume* volume : m_shells.volumes.volumes) {
|
||||||
|
volume->color.a(alphas[k++]);
|
||||||
|
volume->set_render_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//BBS
|
//BBS
|
||||||
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
||||||
if (!show)
|
if (!show)
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ public:
|
|||||||
int print_id{-1};
|
int print_id{-1};
|
||||||
int print_modify_count{-1};
|
int print_modify_count{-1};
|
||||||
bool previewing{false};
|
bool previewing{false};
|
||||||
|
// the prime tower was loaded with the objects, for the solid model
|
||||||
|
bool with_wipe_tower{false};
|
||||||
};
|
};
|
||||||
//BBS
|
//BBS
|
||||||
ConflictResultOpt m_conflict_result;
|
ConflictResultOpt m_conflict_result;
|
||||||
@@ -234,6 +236,17 @@ private:
|
|||||||
|
|
||||||
bool m_legend_visible{ true };
|
bool m_legend_visible{ true };
|
||||||
bool m_legend_enabled{ true };
|
bool m_legend_enabled{ true };
|
||||||
|
// the reduced-detail preferences, pushed to libvgcode by apply_reduced_detail_settings()
|
||||||
|
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::Off };
|
||||||
|
void read_reduced_detail_preferences();
|
||||||
|
void apply_reduced_detail_settings();
|
||||||
|
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
|
||||||
|
// in the solid model mode, the sliced objects are drawn as solid shapes instead of toolpaths
|
||||||
|
bool solid_model_enabled() const { return m_reduced_detail_mode == libvgcode::EReducedDetailMode::EndLayersOnly; }
|
||||||
|
void render_solid_model(int canvas_width, int canvas_height);
|
||||||
|
// the prime tower is only among the shells for the solid model, so it is added or removed when that changes
|
||||||
|
void reload_shells_if_solid_model_changed(bool was_enabled);
|
||||||
|
void update_shell_wipe_tower(const Print& print, bool initialized);
|
||||||
|
|
||||||
float m_legend_height;
|
float m_legend_height;
|
||||||
PrintEstimatedStatistics m_print_statistics;
|
PrintEstimatedStatistics m_print_statistics;
|
||||||
@@ -291,7 +304,7 @@ public:
|
|||||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); }
|
bool has_data() const { return m_viewer.get_extrusion_roles_count() != 0; }
|
||||||
|
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
std::vector<int> get_plater_extruder();
|
std::vector<int> get_plater_extruder();
|
||||||
@@ -363,6 +376,14 @@ public:
|
|||||||
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
||||||
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
||||||
|
|
||||||
|
// whether the mouse is holding either slider's handle
|
||||||
|
bool is_slider_dragging() const { return m_layers_slider->is_dragging() || m_moves_slider->is_dragging(); }
|
||||||
|
// while the user drags the camera or a slider, draw the reduced set, if the preference asks for one
|
||||||
|
void set_interacting(bool interacting);
|
||||||
|
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
||||||
|
// the preference's string value: "off", "solid" or "shell"
|
||||||
|
void set_reduced_detail_mode(const std::string& mode);
|
||||||
|
|
||||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||||
|
|
||||||
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
||||||
|
|||||||
@@ -2084,6 +2084,11 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
|||||||
const bool overlay_tick = m_fps_overlay_tick;
|
const bool overlay_tick = m_fps_overlay_tick;
|
||||||
m_fps_overlay_tick = false;
|
m_fps_overlay_tick = false;
|
||||||
|
|
||||||
|
// Whether the preview draws its reduced set is decided before the cached scene is consulted,
|
||||||
|
// since switching changes what the scene pass draws.
|
||||||
|
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview && m_gcode_viewer.has_data() && _update_preview_interaction())
|
||||||
|
scene_dirty = true;
|
||||||
|
|
||||||
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
||||||
// below once it is known whether the frame differs from the one on screen.
|
// below once it is known whether the frame differs from the one on screen.
|
||||||
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
||||||
@@ -3277,9 +3282,16 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
if (m_selection_edit.kind != SelectionEdit::None)
|
if (m_selection_edit.kind != SelectionEdit::None)
|
||||||
finish_selection_edit();
|
finish_selection_edit();
|
||||||
ImGui::SetWindowFocus(nullptr);
|
ImGui::SetWindowFocus(nullptr);
|
||||||
|
// a drag cut short never sees its button release, which would leave the reduced set drawn
|
||||||
|
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
||||||
|
mouse_up_cleanup();
|
||||||
render();
|
render();
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
});
|
});
|
||||||
|
m_canvas->Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent&) {
|
||||||
|
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
||||||
|
mouse_up_cleanup();
|
||||||
|
});
|
||||||
m_event_handlers_bound = true;
|
m_event_handlers_bound = true;
|
||||||
|
|
||||||
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -3355,6 +3367,17 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||||
|
// the render timer only wakes the idle loop; the frame that puts the preview's toolpaths back
|
||||||
|
// after a wheel burst has to be asked for here, once the settle time is really up
|
||||||
|
if (m_preview_settle_pending) {
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
if (now >= m_preview_interaction_until) {
|
||||||
|
m_preview_settle_pending = false;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
else // the timer fired early
|
||||||
|
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_dirty && !m_overlay_dirty)
|
if (!m_dirty && !m_overlay_dirty)
|
||||||
return;
|
return;
|
||||||
@@ -3885,6 +3908,10 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only a wheel the panels did not take moves the camera
|
||||||
|
if (m_canvas_type == CanvasPreview)
|
||||||
|
note_preview_interaction();
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
||||||
// if the event is not allowed to be passed further.
|
// if the event is not allowed to be passed further.
|
||||||
@@ -3985,6 +4012,11 @@ void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
|||||||
wxWakeUpIdle();
|
wxWakeUpIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::note_preview_interaction()
|
||||||
|
{
|
||||||
|
m_preview_interaction_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(150);
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||||
{
|
{
|
||||||
// Schedule idle event right now
|
// Schedule idle event right now
|
||||||
@@ -5631,6 +5663,9 @@ void GLCanvas3D::mouse_up_cleanup()
|
|||||||
m_mouse.ignore_left_up = false;
|
m_mouse.ignore_left_up = false;
|
||||||
m_mouse.ignore_right_up = false;
|
m_mouse.ignore_right_up = false;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
// the frame that follows a release puts the preview's toolpaths back, and on some platforms
|
||||||
|
// no idle event follows a button release until the next input
|
||||||
|
wxWakeUpIdle();
|
||||||
|
|
||||||
if (m_canvas->HasCapture())
|
if (m_canvas->HasCapture())
|
||||||
m_canvas->ReleaseMouse();
|
m_canvas->ReleaseMouse();
|
||||||
@@ -7779,13 +7814,20 @@ bool GLCanvas3D::_is_scene_cacheable() const
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The scene follows the cursor during a drag, under a gizmo that draws at the cursor, and while
|
// The scene follows the cursor while the user drags, under a gizmo that draws at the cursor, and
|
||||||
// the cursor is on the layer height bar, where the object shader draws a band at its height.
|
// while the cursor is on the layer height bar, where the object shader draws a band at its height.
|
||||||
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||||
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
||||||
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
||||||
return !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging() &&
|
return !is_user_interacting() && (gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
||||||
(gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
}
|
||||||
|
|
||||||
|
// Whether the user is holding something that moves the scene: the camera, the navigator, a gizmo,
|
||||||
|
// the rectangle selection or a preview slider.
|
||||||
|
bool GLCanvas3D::is_user_interacting() const
|
||||||
|
{
|
||||||
|
return m_mouse.dragging || m_navigator_dragging || m_gizmos.is_dragging() || m_rectangle_selection.is_dragging() ||
|
||||||
|
m_gcode_viewer.is_slider_dragging();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||||
@@ -8776,6 +8818,24 @@ void GLCanvas3D::_render_wireframe_overlay()
|
|||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The reduced set is drawn while the camera, the navigator or either slider is dragged. A wheel
|
||||||
|
// step has no duration, so it holds the reduced set for a settle time instead, and the frame that
|
||||||
|
// restores the full toolpaths is scheduled for when that time runs out. Returns whether what the scene
|
||||||
|
// pass draws changed, since a frame that reuses the cached scene would hide the change.
|
||||||
|
bool GLCanvas3D::_update_preview_interaction()
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const bool settling = now < m_preview_interaction_until;
|
||||||
|
const bool dragging = is_user_interacting();
|
||||||
|
const bool was_reduced = m_gcode_viewer.is_reduced_detail();
|
||||||
|
m_gcode_viewer.set_interacting(dragging || settling);
|
||||||
|
if (settling && !dragging && m_gcode_viewer.is_reduced_detail()) {
|
||||||
|
m_preview_settle_pending = true;
|
||||||
|
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
||||||
|
}
|
||||||
|
return m_gcode_viewer.is_reduced_detail() != was_reduced;
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -656,6 +656,10 @@ private:
|
|||||||
ECursorType m_cursor_type;
|
ECursorType m_cursor_type;
|
||||||
GLSelectionRectangle m_rectangle_selection;
|
GLSelectionRectangle m_rectangle_selection;
|
||||||
bool m_navigator_dragging{ false };
|
bool m_navigator_dragging{ false };
|
||||||
|
// until when a wheel step keeps the preview's reduced set drawn
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> m_preview_interaction_until{};
|
||||||
|
// whether the frame that restores the toolpaths once that time is up is still owed
|
||||||
|
bool m_preview_settle_pending{ false };
|
||||||
|
|
||||||
//BBS:add plate related logic
|
//BBS:add plate related logic
|
||||||
mutable std::vector<int> m_hover_volume_idxs;
|
mutable std::vector<int> m_hover_volume_idxs;
|
||||||
@@ -1222,6 +1226,10 @@ public:
|
|||||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||||
|
|
||||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||||
|
// whether the user is holding the camera, the navigator, a gizmo, the rectangle selection or a preview slider
|
||||||
|
bool is_user_interacting() const;
|
||||||
|
// a wheel step is over before the next frame, so it holds the preview's reduced set for a settle time
|
||||||
|
void note_preview_interaction();
|
||||||
|
|
||||||
void schedule_extra_frame(int milliseconds);
|
void schedule_extra_frame(int milliseconds);
|
||||||
|
|
||||||
@@ -1372,6 +1380,9 @@ private:
|
|||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void _render_gcode(int canvas_width, int canvas_height);
|
void _render_gcode(int canvas_width, int canvas_height);
|
||||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||||
|
// decides whether the preview draws its reduced set this frame and returns whether what the scene
|
||||||
|
// pass draws changed; runs before the cached scene is consulted
|
||||||
|
bool _update_preview_interaction();
|
||||||
//BBS: render a plane for assemble
|
//BBS: render a plane for assemble
|
||||||
void _render_plane() const;
|
void _render_plane() const;
|
||||||
void _render_selection();
|
void _render_selection();
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/any.hpp>
|
#include <boost/any.hpp>
|
||||||
|
|
||||||
#include <wx/filename.h>
|
|
||||||
#include <wx/filesys.h>
|
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
@@ -30,13 +27,11 @@
|
|||||||
|
|
||||||
#include "AboutDialog.hpp"
|
#include "AboutDialog.hpp"
|
||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "Plater.hpp"
|
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
|
|
||||||
#include "WebUserLoginDialog.hpp"
|
#include "WebUserLoginDialog.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@@ -536,11 +531,6 @@ boost::filesystem::path into_path(const wxString &str)
|
|||||||
return boost::filesystem::path(str.wx_str());
|
return boost::filesystem::path(str.wx_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString file_url_from_path(const boost::filesystem::path &path)
|
|
||||||
{
|
|
||||||
return wxFileSystem::FileNameToURL(wxFileName(from_path(path)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void about()
|
void about()
|
||||||
{
|
{
|
||||||
AboutDialog dlg;
|
AboutDialog dlg;
|
||||||
@@ -645,26 +635,4 @@ void desktop_open_any_folder( const std::string& path )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool desktop_open_project_attachment(wxWindow *parent, const boost::filesystem::path &path)
|
|
||||||
{
|
|
||||||
// The auxiliary path is UTF-8, which is what boost::filesystem reads a narrow string as.
|
|
||||||
const boost::filesystem::path aux_root(wxGetApp().plater()->model().get_auxiliary_file_temp_path());
|
|
||||||
boost::system::error_code ec;
|
|
||||||
if (!is_absolute_path_within_root(path, aux_root) || !boost::filesystem::is_regular_file(path, ec))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Attachments come with the project and carry no download mark, so the desktop would open them without a warning.
|
|
||||||
if (!is_safe_to_open_file_name(path.filename().string())) {
|
|
||||||
MessageDialog dlg(parent,
|
|
||||||
wxString::Format(_L("\"%s\" is not a plain document, image or model file. Opening it may run it as a "
|
|
||||||
"program or script on this computer.\n\n"
|
|
||||||
"Only open attachments from projects you trust. Open it anyway?"),
|
|
||||||
from_path(path.filename())),
|
|
||||||
_L("Open attachment"), wxICON_WARNING | wxYES_NO);
|
|
||||||
if (dlg.ShowModal() != wxID_YES)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return wxLaunchDefaultApplication(from_path(path), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|||||||
@@ -76,9 +76,6 @@ std::string into_u8(const wxString &str);
|
|||||||
wxString from_path(const boost::filesystem::path &path);
|
wxString from_path(const boost::filesystem::path &path);
|
||||||
// boost path from wxString
|
// boost path from wxString
|
||||||
boost::filesystem::path into_path(const wxString &str);
|
boost::filesystem::path into_path(const wxString &str);
|
||||||
// file:// URL of a local path, percent-encoded so characters such as '#', '%' and '?' stay part of the path.
|
|
||||||
// Append any query or fragment to the result.
|
|
||||||
wxString file_url_from_path(const boost::filesystem::path &path);
|
|
||||||
|
|
||||||
// Display an About dialog
|
// Display an About dialog
|
||||||
extern void about();
|
extern void about();
|
||||||
@@ -86,9 +83,6 @@ extern void about();
|
|||||||
extern void desktop_open_datadir_folder();
|
extern void desktop_open_datadir_folder();
|
||||||
// Ask the destop to open one folder
|
// Ask the destop to open one folder
|
||||||
extern void desktop_open_any_folder(const std::string& path);
|
extern void desktop_open_any_folder(const std::string& path);
|
||||||
// Ask the desktop to open a file from the project's auxiliary directory, after a confirmation
|
|
||||||
// unless its type is known to be plain content. Returns false if the file was not opened.
|
|
||||||
extern bool desktop_open_project_attachment(wxWindow *parent, const boost::filesystem::path &path);
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|||||||
@@ -1512,33 +1512,11 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP
|
|||||||
size_t n = mz_zip_reader_get_extra(&archive, stat.m_file_index, extra.data(), extra.size());
|
size_t n = mz_zip_reader_get_extra(&archive, stat.m_file_index, extra.data(), extra.size());
|
||||||
dest_file = decode(extra.substr(0, n), stat.m_filename);
|
dest_file = decode(extra.substr(0, n), stat.m_filename);
|
||||||
}
|
}
|
||||||
if (!is_path_within_root(dest_file, plugin_folder)) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "[install_plugin] entry " << dest_file << " resolves outside " << plugin_folder.string();
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
if (pro_fn) { pro_fn(InstallStatusUnzipFailed, 0, cancel); }
|
|
||||||
return InstallStatusUnzipFailed;
|
|
||||||
}
|
|
||||||
auto dest_path = plugin_folder / dest_file;
|
auto dest_path = plugin_folder / dest_file;
|
||||||
std::string dest_zip_file = encode_path(dest_path.string().c_str());
|
|
||||||
#ifndef WIN32
|
|
||||||
// Validate a symlink's target before anything at the destination is replaced.
|
|
||||||
const bool is_link = S_ISLNK(stat.m_external_attr >> 16);
|
|
||||||
std::string link;
|
|
||||||
if (is_link) {
|
|
||||||
link.assign(stat.m_uncomp_size, 0);
|
|
||||||
if (!mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, link.data(), stat.m_uncomp_size, 0) ||
|
|
||||||
!is_symlink_target_within_root(dest_file, link, plugin_folder)) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "[install_plugin] link " << dest_file << " -> " << link << " is unreadable or resolves outside " << plugin_folder.string();
|
|
||||||
close_zip_reader(&archive);
|
|
||||||
if (pro_fn) { pro_fn(InstallStatusUnzipFailed, 0, cancel); }
|
|
||||||
return InstallStatusUnzipFailed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
try {
|
|
||||||
boost::filesystem::create_directories(dest_path.parent_path());
|
boost::filesystem::create_directories(dest_path.parent_path());
|
||||||
// symlink_status so that an existing symlink, dangling or not, is replaced rather than written through.
|
std::string dest_zip_file = encode_path(dest_path.string().c_str());
|
||||||
if (fs::exists(fs::symlink_status(dest_path))) {
|
try {
|
||||||
|
if (fs::exists(dest_path)) {
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
fs::remove(dest_path, ec);
|
fs::remove(dest_path, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
@@ -1566,8 +1544,9 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP
|
|||||||
}
|
}
|
||||||
mz_bool res = 0;
|
mz_bool res = 0;
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
if (is_link) {
|
if (S_ISLNK(stat.m_external_attr >> 16)) {
|
||||||
res = 1;
|
std::string link(stat.m_uncomp_size + 1, 0);
|
||||||
|
res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, link.data(), stat.m_uncomp_size, 0);
|
||||||
try {
|
try {
|
||||||
boost::filesystem::create_symlink(link, dest_path);
|
boost::filesystem::create_symlink(link, dest_path);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
|
|||||||
@@ -483,6 +483,11 @@ void IMSlider::draw_background_and_groove(const ImRect& bg_rect, const ImRect& g
|
|||||||
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IMSlider::is_dragging() const
|
||||||
|
{
|
||||||
|
return GImGui != nullptr && m_imgui_id != 0 && GImGui->ActiveId == m_imgui_id && GImGui->IO.MouseDown[0];
|
||||||
|
}
|
||||||
|
|
||||||
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
@@ -491,6 +496,7 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
|
m_imgui_id = id;
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
@@ -883,6 +889,7 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
|
m_imgui_id = id;
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ public:
|
|||||||
|
|
||||||
//BBS update scroll value changed
|
//BBS update scroll value changed
|
||||||
bool is_dirty() { return m_dirty; }
|
bool is_dirty() { return m_dirty; }
|
||||||
|
// whether the mouse is holding this slider's handle, read from ImGui's active id rather than
|
||||||
|
// from the dirty flag, which is raised and consumed inside a single frame
|
||||||
|
bool is_dragging() const;
|
||||||
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
||||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||||
void reset_post_tick_event(bool val = false) {
|
void reset_post_tick_event(bool val = false) {
|
||||||
@@ -182,6 +185,8 @@ private:
|
|||||||
int m_higher_value;
|
int m_higher_value;
|
||||||
int m_one_layer_value; // ORCA
|
int m_one_layer_value; // ORCA
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
|
// the ImGui id of the slider widget, as of its last render
|
||||||
|
unsigned int m_imgui_id = 0;
|
||||||
|
|
||||||
bool m_render_as_disabled{ false };
|
bool m_render_as_disabled{ false };
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ void MarkdownTip::LoadStyle()
|
|||||||
ph /= "tooltip/styled.html";
|
ph /= "tooltip/styled.html";
|
||||||
_data_dir = false;
|
_data_dir = false;
|
||||||
}
|
}
|
||||||
_tipView->LoadURL(file_url_from_path(ph));
|
auto url = ph.string();
|
||||||
|
std::replace(url.begin(), url.end(), '\\', '/');
|
||||||
|
url = "file:///" + url;
|
||||||
|
_tipView->LoadURL(from_u8(url));
|
||||||
_lastTip.clear();
|
_lastTip.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-28
@@ -286,30 +286,6 @@ static void set_config_values(DynamicPrintConfig *config, const std::string &key
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orca: a calibration print sets pressure advance explicitly to 0 on each extruder variant that
|
|
||||||
// has it disabled, so the value stored in the printer does not skew the result. Variants with
|
|
||||||
// pressure advance enabled keep their own value.
|
|
||||||
static void zero_pressure_advance_where_disabled(DynamicPrintConfig *filament_config)
|
|
||||||
{
|
|
||||||
auto enable_pa = filament_config->option<ConfigOptionBools>("enable_pressure_advance");
|
|
||||||
auto pa = filament_config->option<ConfigOptionFloats>("pressure_advance");
|
|
||||||
auto adaptive_pa = filament_config->option<ConfigOptionBools>("adaptive_pressure_advance");
|
|
||||||
if (!enable_pa || !pa || !adaptive_pa || pa->empty() || adaptive_pa->empty())
|
|
||||||
return;
|
|
||||||
// All variant keys share the variant count; widen a short one rather than index past it.
|
|
||||||
if (pa->size() < enable_pa->size())
|
|
||||||
pa->resize(enable_pa->size());
|
|
||||||
if (adaptive_pa->size() < enable_pa->size())
|
|
||||||
adaptive_pa->resize(enable_pa->size());
|
|
||||||
for (size_t variant = 0; variant < enable_pa->size(); ++variant) {
|
|
||||||
if (enable_pa->get_at(variant))
|
|
||||||
continue;
|
|
||||||
enable_pa->values[variant] = true;
|
|
||||||
pa->values[variant] = 0.0;
|
|
||||||
adaptive_pa->values[variant] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
|
bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
|
||||||
{
|
{
|
||||||
std::string name = into_u8(wxs_name);
|
std::string name = into_u8(wxs_name);
|
||||||
@@ -3696,7 +3672,7 @@ void Sidebar::update_all_preset_comboboxes()
|
|||||||
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
|
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
|
||||||
wxString apikey;
|
wxString apikey;
|
||||||
if(url.empty())
|
if(url.empty())
|
||||||
url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/orca/missing_connection.html");
|
url = wxString::Format("file://%s/web/orca/missing_connection.html", from_u8(resources_dir()));
|
||||||
else {
|
else {
|
||||||
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||||
if (cfg.has("printhost_apikey") && (host_type != htSimplyPrint))
|
if (cfg.has("printhost_apikey") && (host_type != htSimplyPrint))
|
||||||
@@ -17088,7 +17064,11 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
|
|||||||
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
zero_pressure_advance_where_disabled(filament_config);
|
if (!filament_config->option<ConfigOptionBools>("enable_pressure_advance")->get_at(0)) {
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "enable_pressure_advance", true);
|
||||||
|
set_config_values<double, ConfigOptionFloatsNullable>(filament_config, "pressure_advance", 0.0);
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "adaptive_pressure_advance", false);
|
||||||
|
}
|
||||||
|
|
||||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||||
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
|
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
|
||||||
@@ -17150,7 +17130,11 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
|
|||||||
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
zero_pressure_advance_where_disabled(filament_config);
|
if (!filament_config->option<ConfigOptionBools>("enable_pressure_advance")->get_at(0)) {
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "enable_pressure_advance", true);
|
||||||
|
set_config_values<double, ConfigOptionFloatsNullable>(filament_config, "pressure_advance", 0.0);
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "adaptive_pressure_advance", false);
|
||||||
|
}
|
||||||
|
|
||||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||||
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
|
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
|
||||||
@@ -17212,7 +17196,11 @@ void Plater::Calib_Cornering(const Calib_Params& params)
|
|||||||
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
set_config_values<double, ConfigOptionFloatsNullable>(print_config, "default_jerk", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
zero_pressure_advance_where_disabled(filament_config);
|
if (!filament_config->option<ConfigOptionBools>("enable_pressure_advance")->get_at(0)) {
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "enable_pressure_advance", true);
|
||||||
|
set_config_values<double, ConfigOptionFloatsNullable>(filament_config, "pressure_advance", 0.0);
|
||||||
|
set_config_values<bool, ConfigOptionBools>(filament_config, "adaptive_pressure_advance", false);
|
||||||
|
}
|
||||||
|
|
||||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||||
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true});
|
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true});
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ wxBoxSizer* PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
return sizer;
|
return sizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url)
|
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange, const wxString wiki_url)
|
||||||
{
|
{
|
||||||
assert(vlist.size() == config_name_index.size());
|
assert(vlist.size() == config_name_index.size());
|
||||||
unsigned int current_index = 0;
|
unsigned int current_index = 0;
|
||||||
@@ -338,8 +338,9 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
||||||
|
|
||||||
//// save config
|
//// save config
|
||||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index](wxCommandEvent& e) {
|
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index, onchange](wxCommandEvent& e) {
|
||||||
app_config->set(param, config_name_index[e.GetSelection()]);
|
app_config->set(param, config_name_index[e.GetSelection()]);
|
||||||
|
if (onchange != nullptr) onchange(config_name_index[e.GetSelection()]);
|
||||||
e.Skip();
|
e.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2044,6 +2045,32 @@ void PreferencesDialog::create_items()
|
|||||||
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
||||||
g_sizer->Add(item_preview_view_type);
|
g_sizer->Add(item_preview_view_type);
|
||||||
|
|
||||||
|
auto item_reduced_detail_mode = create_item_combobox(
|
||||||
|
_L("Simplify preview while dragging"),
|
||||||
|
_L("What the sliced preview draws while you drag the camera or a preview slider, or zoom with the mouse wheel, so that large prints stay responsive. "
|
||||||
|
"The full toolpaths are restored as soon as you let go.\n"
|
||||||
|
"Off: the full toolpaths.\n"
|
||||||
|
"Solid model: the sliced objects and the prime tower as solid shapes in their filament colors, cut to the visible layer range, "
|
||||||
|
"with its bottom and top layers drawn as toolpaths. Supports are not shown, and negative volumes are not cut out.\n"
|
||||||
|
"Shell only: every layer without its sparse infill, internal solid infill and gap fill, which lie under the walls and skins. "
|
||||||
|
"Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so the print looks the same from outside.\n"
|
||||||
|
"The bottom and top of the visible layer range are always drawn whole."),
|
||||||
|
"preview_reduced_detail_mode",
|
||||||
|
{_L("Off"), _L("Solid model"), _L("Shell only")},
|
||||||
|
{"off", "solid", "shell"},
|
||||||
|
// apply the new mode immediately to the currently loaded preview
|
||||||
|
[](std::string value) {
|
||||||
|
if (Plater* plater = wxGetApp().plater()) {
|
||||||
|
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||||
|
canvas->get_gcode_viewer().set_reduced_detail_mode(value);
|
||||||
|
canvas->set_as_dirty();
|
||||||
|
canvas->request_extra_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
g_sizer->Add(item_reduced_detail_mode);
|
||||||
|
|
||||||
auto item_dim_previous_layers = create_item_checkbox(
|
auto item_dim_previous_layers = create_item_checkbox(
|
||||||
_L("Dim lower layers"),
|
_L("Dim lower layers"),
|
||||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
wxBoxSizer *create_item_title(wxString title);
|
wxBoxSizer *create_item_title(wxString title);
|
||||||
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange = {}, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ PrivacyUpdateDialog::PrivacyUpdateDialog(wxWindow* parent, wxWindowID id, const
|
|||||||
|
|
||||||
fs::path ph(resources_dir());
|
fs::path ph(resources_dir());
|
||||||
ph /= "tooltip/privacyupdate.html";
|
ph /= "tooltip/privacyupdate.html";
|
||||||
m_host_url = into_u8(file_url_from_path(ph));
|
m_host_url = ph.string();
|
||||||
|
std::replace(m_host_url.begin(), m_host_url.end(), '\\', '/');
|
||||||
|
m_host_url = "file:///" + m_host_url;
|
||||||
m_vebview_release_note->LoadURL(from_u8(m_host_url));
|
m_vebview_release_note->LoadURL(from_u8(m_host_url));
|
||||||
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
|
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ const std::vector<std::string> license_list = {
|
|||||||
ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style)
|
ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style)
|
||||||
{
|
{
|
||||||
SetBackgroundColour(*wxWHITE);
|
SetBackgroundColour(*wxWHITE);
|
||||||
m_project_home_url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/model/index.html");
|
m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir()));
|
||||||
wxString strlang = wxGetApp().current_language_code_safe();
|
wxString strlang = wxGetApp().current_language_code_safe();
|
||||||
if (strlang != "")
|
if (strlang != "")
|
||||||
m_project_home_url += "?lang=" + strlang;
|
m_project_home_url = wxString::Format("file://%s/web/model/index.html?lang=%s", from_u8(resources_dir()), strlang);
|
||||||
|
|
||||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
@@ -293,8 +293,10 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt)
|
|||||||
if (!accessory_path.empty()) {
|
if (!accessory_path.empty()) {
|
||||||
std::string decode_path = wxGetApp().url_decode(accessory_path.ToStdString());
|
std::string decode_path = wxGetApp().url_decode(accessory_path.ToStdString());
|
||||||
fs::path path(decode_path);
|
fs::path path(decode_path);
|
||||||
if (!desktop_open_project_attachment(this, path))
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "open_3mf_accessory: not opening " << decode_path;
|
if (fs::exists(path)) {
|
||||||
|
wxLaunchDefaultApplication(path.wstring(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strCmd == "request_3mf_info") {
|
else if (strCmd == "request_3mf_info") {
|
||||||
|
|||||||
@@ -510,7 +510,7 @@ void UpdateVersionDialog::update_version_info(wxString release_note, wxString ve
|
|||||||
out_buf->append(text, size);
|
out_buf->append(text, size);
|
||||||
}, (void*) &html_source, MD_DIALECT_GITHUB | MD_FLAG_STRIKETHROUGH | MD_FLAG_WIKILINKS, 0);
|
}, (void*) &html_source, MD_DIALECT_GITHUB | MD_FLAG_STRIKETHROUGH | MD_FLAG_WIKILINKS, 0);
|
||||||
html_source.append("</body></html>");
|
html_source.append("</body></html>");
|
||||||
m_vebview_release_note->LoadURL(file_url_from_path(boost::filesystem::path(resources_dir()) / "web/guide/0/index.html"));
|
m_vebview_release_note->LoadURL("file://" + (boost::filesystem::path (resources_dir()) / "web/guide/0/index.html").string());
|
||||||
|
|
||||||
SetMinSize(GetSize());
|
SetMinSize(GetSize());
|
||||||
SetMaxSize(GetSize());
|
SetMaxSize(GetSize());
|
||||||
|
|||||||
+19
-20
@@ -4415,15 +4415,15 @@ void TabFilament::build()
|
|||||||
optgroup->append_single_option_line("pellet_flow_coefficient", "printer_basic_information_advanced#pellet-modded-printer");
|
optgroup->append_single_option_line("pellet_flow_coefficient", "printer_basic_information_advanced#pellet-modded-printer");
|
||||||
optgroup->append_single_option_line("filament_flow_ratio", "material_flow_ratio_and_pressure_advance#flow-ratio", 0);
|
optgroup->append_single_option_line("filament_flow_ratio", "material_flow_ratio_and_pressure_advance#flow-ratio", 0);
|
||||||
|
|
||||||
optgroup->append_single_option_line("enable_pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance", 0);
|
optgroup->append_single_option_line("enable_pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance");
|
||||||
optgroup->append_single_option_line("pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance", 0);
|
optgroup->append_single_option_line("pressure_advance", "material_flow_ratio_and_pressure_advance#pressure-advance");
|
||||||
|
|
||||||
// Orca: adaptive pressure advance and calibration model
|
// Orca: adaptive pressure advance and calibration model
|
||||||
optgroup->append_single_option_line("adaptive_pressure_advance", "material_flow_ratio_and_pressure_advance#enable-adaptive-pressure-advance-beta", 0);
|
optgroup->append_single_option_line("adaptive_pressure_advance", "material_flow_ratio_and_pressure_advance#enable-adaptive-pressure-advance-beta");
|
||||||
optgroup->append_single_option_line("adaptive_pressure_advance_overhangs", "material_flow_ratio_and_pressure_advance#enable-adaptive-pressure-advance-for-overhangs-beta", 0);
|
optgroup->append_single_option_line("adaptive_pressure_advance_overhangs", "material_flow_ratio_and_pressure_advance#enable-adaptive-pressure-advance-for-overhangs-beta");
|
||||||
optgroup->append_single_option_line("adaptive_pressure_advance_bridges", "material_flow_ratio_and_pressure_advance#pressure-advance-for-bridges", 0);
|
optgroup->append_single_option_line("adaptive_pressure_advance_bridges", "material_flow_ratio_and_pressure_advance#pressure-advance-for-bridges");
|
||||||
|
|
||||||
Option option = optgroup->get_option("adaptive_pressure_advance_model", 0);
|
Option option = optgroup->get_option("adaptive_pressure_advance_model");
|
||||||
option.opt.full_width = true;
|
option.opt.full_width = true;
|
||||||
option.opt.is_code = true;
|
option.opt.is_code = true;
|
||||||
option.opt.height = 15;
|
option.opt.height = 15;
|
||||||
@@ -4432,7 +4432,7 @@ void TabFilament::build()
|
|||||||
DynamicPrintConfig& filament_config = m_preset_bundle->filaments.get_edited_preset().config;
|
DynamicPrintConfig& filament_config = m_preset_bundle->filaments.get_edited_preset().config;
|
||||||
|
|
||||||
update_dirty();
|
update_dirty();
|
||||||
if (opt_key.substr(0, opt_key.find('#')) == "adaptive_pressure_advance_model")
|
if (opt_key == "adaptive_pressure_advance_model")
|
||||||
m_config_manipulation.check_adaptive_pressure_advance_model(&filament_config);
|
m_config_manipulation.check_adaptive_pressure_advance_model(&filament_config);
|
||||||
|
|
||||||
on_value_change(opt_key, value);
|
on_value_change(opt_key, value);
|
||||||
@@ -4829,11 +4829,8 @@ void TabFilament::toggle_options()
|
|||||||
}
|
}
|
||||||
if (m_active_page->title() == L("Filament"))
|
if (m_active_page->title() == L("Filament"))
|
||||||
{
|
{
|
||||||
const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0;
|
bool pa = m_config->opt_bool("enable_pressure_advance", 0);
|
||||||
const unsigned int variant_idx = (unsigned int) std::max(selection, 0);
|
toggle_option("pressure_advance", pa);
|
||||||
|
|
||||||
bool pa = m_config->opt_bool("enable_pressure_advance", variant_idx);
|
|
||||||
toggle_option("pressure_advance", pa, 0);
|
|
||||||
|
|
||||||
//Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type.
|
//Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type.
|
||||||
DynamicConfig& proj_cfg = m_preset_bundle->project_config;
|
DynamicConfig& proj_cfg = m_preset_bundle->project_config;
|
||||||
@@ -4861,12 +4858,12 @@ void TabFilament::toggle_options()
|
|||||||
// Orca: adaptive pressure advance and calibration model
|
// Orca: adaptive pressure advance and calibration model
|
||||||
// If PA is not enabled, disable adaptive pressure advance and hide the model section
|
// If PA is not enabled, disable adaptive pressure advance and hide the model section
|
||||||
// If adaptive PA is not enabled, hide the adaptive PA model section
|
// If adaptive PA is not enabled, hide the adaptive PA model section
|
||||||
toggle_option("adaptive_pressure_advance", pa, 0);
|
toggle_option("adaptive_pressure_advance", pa);
|
||||||
toggle_option("adaptive_pressure_advance_overhangs", pa, 0);
|
toggle_option("adaptive_pressure_advance_overhangs", pa);
|
||||||
bool has_adaptive_pa = m_config->opt_bool("adaptive_pressure_advance", variant_idx);
|
bool has_adaptive_pa = m_config->opt_bool("adaptive_pressure_advance", 0);
|
||||||
toggle_line("adaptive_pressure_advance_overhangs", has_adaptive_pa && pa, 0);
|
toggle_line("adaptive_pressure_advance_overhangs", has_adaptive_pa && pa);
|
||||||
toggle_line("adaptive_pressure_advance_model", has_adaptive_pa && pa, 0);
|
toggle_line("adaptive_pressure_advance_model", has_adaptive_pa && pa);
|
||||||
toggle_line("adaptive_pressure_advance_bridges", has_adaptive_pa && pa, 0);
|
toggle_line("adaptive_pressure_advance_bridges", has_adaptive_pa && pa);
|
||||||
|
|
||||||
bool is_pellet_printer = printer_cfg.opt_bool("pellet_modded_printer");
|
bool is_pellet_printer = printer_cfg.opt_bool("pellet_modded_printer");
|
||||||
toggle_line("pellet_flow_coefficient", is_pellet_printer);
|
toggle_line("pellet_flow_coefficient", is_pellet_printer);
|
||||||
@@ -4874,6 +4871,8 @@ void TabFilament::toggle_options()
|
|||||||
|
|
||||||
toggle_line("activate_chamber_temp_control", printer_cfg.opt_bool("support_chamber_temp_control"));
|
toggle_line("activate_chamber_temp_control", printer_cfg.opt_bool("support_chamber_temp_control"));
|
||||||
|
|
||||||
|
const int selection = m_variant_combo ? m_variant_combo->GetSelection() : 0;
|
||||||
|
const unsigned int variant_idx = (unsigned int) std::max(selection, 0);
|
||||||
std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", variant_idx);
|
std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", variant_idx);
|
||||||
bool enable_fit = volumetric_speed_cos != "0 0 0 0 0 0";
|
bool enable_fit = volumetric_speed_cos != "0 0 0 0 0 0";
|
||||||
toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + variant_idx);
|
toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + variant_idx);
|
||||||
@@ -5206,6 +5205,7 @@ void TabPrinter::build_fff()
|
|||||||
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin");
|
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Accessory"), "param_accessory");
|
optgroup = page->new_optgroup(L("Accessory"), "param_accessory");
|
||||||
|
optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type", 0);
|
||||||
optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc");
|
optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc");
|
||||||
optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan");
|
optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan");
|
||||||
optgroup->append_single_option_line("fan_direction");
|
optgroup->append_single_option_line("fan_direction");
|
||||||
@@ -5720,7 +5720,6 @@ if (is_marlin_flavor)
|
|||||||
|
|
||||||
auto optgroup = page->new_optgroup(L("Basic information"), L"param_information", -1, true);
|
auto optgroup = page->new_optgroup(L("Basic information"), L"param_information", -1, true);
|
||||||
optgroup->append_single_option_line("nozzle_diameter", "printer_extruder_basic_information#nozzle-diameter", extruder_idx);
|
optgroup->append_single_option_line("nozzle_diameter", "printer_extruder_basic_information#nozzle-diameter", extruder_idx);
|
||||||
optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type", extruder_idx);
|
|
||||||
//optgroup->append_single_option_line("nozzle_volume_type", "", extruder_idx);
|
//optgroup->append_single_option_line("nozzle_volume_type", "", extruder_idx);
|
||||||
|
|
||||||
optgroup->append_single_option_line("nozzle_volume", "printer_extruder_basic_information#nozzle-volume", extruder_idx);
|
optgroup->append_single_option_line("nozzle_volume", "printer_extruder_basic_information#nozzle-volume", extruder_idx);
|
||||||
@@ -5732,7 +5731,7 @@ if (is_marlin_flavor)
|
|||||||
optgroup->m_on_change = [this, extruder_idx](const t_config_option_key& opt_key, boost::any value)
|
optgroup->m_on_change = [this, extruder_idx](const t_config_option_key& opt_key, boost::any value)
|
||||||
{
|
{
|
||||||
bool is_SEMM = m_config->opt_bool("single_extruder_multi_material");
|
bool is_SEMM = m_config->opt_bool("single_extruder_multi_material");
|
||||||
if (is_SEMM && m_extruders_count > 1 && boost::starts_with(opt_key, "nozzle_diameter"))
|
if (is_SEMM && m_extruders_count > 1 && opt_key.find_first_of("nozzle_diameter") != std::string::npos)
|
||||||
{
|
{
|
||||||
SuppressBackgroundProcessingUpdate sbpu;
|
SuppressBackgroundProcessingUpdate sbpu;
|
||||||
const double new_nd = boost::any_cast<double>(value);
|
const double new_nd = boost::any_cast<double>(value);
|
||||||
|
|||||||
@@ -31,15 +31,14 @@ int UserManager::parse_json(std::string payload)
|
|||||||
{
|
{
|
||||||
bool restored_json = false;
|
bool restored_json = false;
|
||||||
json j;
|
json j;
|
||||||
|
|
||||||
//bind/unbind
|
|
||||||
|
|
||||||
try {
|
|
||||||
json j_pre = json::parse(payload);
|
json j_pre = json::parse(payload);
|
||||||
if (j_pre.empty()) {
|
if (j_pre.empty()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//bind/unbind
|
||||||
|
|
||||||
|
try {
|
||||||
if (j_pre.contains("bind")) {
|
if (j_pre.contains("bind")) {
|
||||||
if (j_pre["bind"].contains("command")) {
|
if (j_pre["bind"].contains("command")) {
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ DownPluginFrame::DownPluginFrame(GUI_App *pGUI) : wxDialog((wxWindow *) (pGUI->m
|
|||||||
|
|
||||||
// set the frame icon
|
// set the frame icon
|
||||||
wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
wxString TargetUrl = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/guide/6/index.html");
|
wxString TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/6/index.html").make_preferred().string());
|
||||||
|
|
||||||
|
TargetUrl = "file://" + TargetUrl;
|
||||||
|
|
||||||
// Create the webview
|
// Create the webview
|
||||||
m_browser = WebView::CreateWebView(this, TargetUrl);
|
m_browser = WebView::CreateWebView(this, TargetUrl);
|
||||||
|
|||||||
@@ -218,38 +218,37 @@ wxString GuideFrame::SetStartPage(GuidePage startpage, bool load)
|
|||||||
m_page = startpage;
|
m_page = startpage;
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" enter, load=%1%, start_page=%2%")%load%int(startpage);
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" enter, load=%1%, start_page=%2%")%load%int(startpage);
|
||||||
//wxLogMessage("GUIDE: webpage_1 %s", (boost::filesystem::path(resources_dir()) / "web\\guide\\1\\index.html").make_preferred().string().c_str() );
|
//wxLogMessage("GUIDE: webpage_1 %s", (boost::filesystem::path(resources_dir()) / "web\\guide\\1\\index.html").make_preferred().string().c_str() );
|
||||||
const wxString guide_url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/guide/0/index.html");
|
wxString TargetUrl = from_u8( (boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string() );
|
||||||
wxString TargetUrl = guide_url + "?target=1";
|
|
||||||
//wxLogMessage("GUIDE: webpage_2 %s", TargetUrl.mb_str());
|
//wxLogMessage("GUIDE: webpage_2 %s", TargetUrl.mb_str());
|
||||||
|
|
||||||
if (startpage == BBL_WELCOME){
|
if (startpage == BBL_WELCOME){
|
||||||
SetTitle(_L("Setup Wizard"));
|
SetTitle(_L("Setup Wizard"));
|
||||||
TargetUrl = guide_url + "?target=1";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string());
|
||||||
} else if (startpage == BBL_REGION) {
|
} else if (startpage == BBL_REGION) {
|
||||||
SetTitle(_L("Setup Wizard"));
|
SetTitle(_L("Setup Wizard"));
|
||||||
TargetUrl = guide_url + "?target=11";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=11").make_preferred().string());
|
||||||
} else if (startpage == BBL_MODELS) {
|
} else if (startpage == BBL_MODELS) {
|
||||||
SetTitle(_L("Setup Wizard"));
|
SetTitle(_L("Setup Wizard"));
|
||||||
TargetUrl = guide_url + "?target=21";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string());
|
||||||
} else if (startpage == BBL_FILAMENTS) {
|
} else if (startpage == BBL_FILAMENTS) {
|
||||||
SetTitle(_L("Setup Wizard"));
|
SetTitle(_L("Setup Wizard"));
|
||||||
|
|
||||||
int nSize = m_ProfileJson["model"].size();
|
int nSize = m_ProfileJson["model"].size();
|
||||||
|
|
||||||
if (nSize>0)
|
if (nSize>0)
|
||||||
TargetUrl = guide_url + "?target=22";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=22").make_preferred().string());
|
||||||
else
|
else
|
||||||
TargetUrl = guide_url + "?target=21";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string());
|
||||||
} else if (startpage == BBL_FILAMENT_ONLY) {
|
} else if (startpage == BBL_FILAMENT_ONLY) {
|
||||||
SetTitle("");
|
SetTitle("");
|
||||||
TargetUrl = guide_url + "?target=23";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=23").make_preferred().string());
|
||||||
} else if (startpage == BBL_MODELS_ONLY) {
|
} else if (startpage == BBL_MODELS_ONLY) {
|
||||||
SetTitle("");
|
SetTitle("");
|
||||||
TargetUrl = guide_url + "?target=24";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=24").make_preferred().string());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SetTitle(_L("Setup Wizard"));
|
SetTitle(_L("Setup Wizard"));
|
||||||
TargetUrl = guide_url + "?target=21";
|
TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString strlang = wxGetApp().current_language_code_safe();
|
wxString strlang = wxGetApp().current_language_code_safe();
|
||||||
@@ -257,6 +256,7 @@ wxString GuideFrame::SetStartPage(GuidePage startpage, bool load)
|
|||||||
if (strlang != "")
|
if (strlang != "")
|
||||||
TargetUrl = wxString::Format("%s&lang=%s", w2s(TargetUrl), strlang);
|
TargetUrl = wxString::Format("%s&lang=%s", w2s(TargetUrl), strlang);
|
||||||
|
|
||||||
|
TargetUrl = "file://" + TargetUrl;
|
||||||
if (load)
|
if (load)
|
||||||
load_url(TargetUrl);
|
load_url(TargetUrl);
|
||||||
|
|
||||||
@@ -1284,7 +1284,7 @@ bool GuideFrame::BuildProfileJson(const PresetBundle& bundle, bool require_all_r
|
|||||||
entry["vendor"] = vp.id;
|
entry["vendor"] = vp.id;
|
||||||
entry["nozzle_diameter"] = nozzle_str;
|
entry["nozzle_diameter"] = nozzle_str;
|
||||||
entry["materials"] = materials_str;
|
entry["materials"] = materials_str;
|
||||||
entry["cover"] = into_u8(file_url_from_path(cover_path));
|
entry["cover"] = cover_path.string();
|
||||||
entry["nozzle_selected"] = "";
|
entry["nozzle_selected"] = "";
|
||||||
entry["sub_path"] = "";
|
entry["sub_path"] = "";
|
||||||
m_ProfileJson["model"].push_back(entry);
|
m_ProfileJson["model"].push_back(entry);
|
||||||
@@ -1732,7 +1732,7 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath
|
|||||||
cover_path = (boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") / cover_file)
|
cover_path = (boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") / cover_file)
|
||||||
.make_preferred();
|
.make_preferred();
|
||||||
}
|
}
|
||||||
OneModel["cover"] = into_u8(file_url_from_path(cover_path));
|
OneModel["cover"] = cover_path.string();
|
||||||
|
|
||||||
OneModel["nozzle_selected"] = "";
|
OneModel["nozzle_selected"] = "";
|
||||||
|
|
||||||
|
|||||||
@@ -599,7 +599,7 @@ void ZUserLogin::OnScriptResponseMessage(wxCommandEvent &WXUNUSED(evt))
|
|||||||
|
|
||||||
bool ZUserLogin::ShowErrorPage()
|
bool ZUserLogin::ShowErrorPage()
|
||||||
{
|
{
|
||||||
wxString ErrortUrl = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/login/error.html");
|
wxString ErrortUrl = from_u8((boost::filesystem::path(resources_dir()) / "web\\login\\error.html").make_preferred().string());
|
||||||
load_url(ErrortUrl);
|
load_url(ErrortUrl);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ namespace GUI {
|
|||||||
WebViewPanel::WebViewPanel(wxWindow *parent)
|
WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||||
{
|
{
|
||||||
wxString url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/homepage/index.html");
|
wxString url = wxString::Format("file://%s/web/homepage/index.html", from_u8(resources_dir()));
|
||||||
wxString strlang = wxGetApp().current_language_code_safe();
|
wxString strlang = wxGetApp().current_language_code_safe();
|
||||||
if (strlang != "")
|
if (strlang != "")
|
||||||
url += "?lang=" + strlang;
|
url = wxString::Format("file://%s/web/homepage/index.html?lang=%s", from_u8(resources_dir()), strlang);
|
||||||
|
|
||||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "MultiNozzleSync.hpp"
|
#include "MultiNozzleSync.hpp"
|
||||||
|
|
||||||
#include "../GUI.hpp"
|
|
||||||
#include "../GUI_App.hpp"
|
#include "../GUI_App.hpp"
|
||||||
#include "../I18N.hpp"
|
#include "../I18N.hpp"
|
||||||
#include "../Plater.hpp"
|
#include "../Plater.hpp"
|
||||||
@@ -22,6 +21,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <wx/choice.h>
|
#include <wx/choice.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
|
#include <wx/filesys.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
|
|
||||||
@@ -640,7 +641,8 @@ NozzleListTable::NozzleListTable(wxWindow* parent) : wxPanel(parent,wxID_ANY,wxD
|
|||||||
m_web_view->AddScriptMessageHandler("nozzleListTable");
|
m_web_view->AddScriptMessageHandler("nozzleListTable");
|
||||||
m_web_view->EnableContextMenu(false);
|
m_web_view->EnableContextMenu(false);
|
||||||
fs::path filepath = fs::path(resources_dir()) / "web/flush/NozzleListTable.html";
|
fs::path filepath = fs::path(resources_dir()) / "web/flush/NozzleListTable.html";
|
||||||
wxString url = file_url_from_path(filepath);
|
wxFileName fn(wxString::FromUTF8(filepath.string()));
|
||||||
|
wxString url = wxFileSystem::FileNameToURL(fn);
|
||||||
m_web_view->LoadURL(url);
|
m_web_view->LoadURL(url);
|
||||||
|
|
||||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|||||||
@@ -48,19 +48,20 @@ constexpr char ORCA_BRIDGE_JS[] = R"JS(
|
|||||||
|
|
||||||
wxString bootstrap_url()
|
wxString bootstrap_url()
|
||||||
{
|
{
|
||||||
return file_url_from_path(boost::filesystem::path(resources_dir()) / BOOTSTRAP_PAGE);
|
return wxString("file://") + from_u8((boost::filesystem::path(resources_dir()) / BOOTSTRAP_PAGE).make_preferred().string());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString content_base_url()
|
wxString content_base_url()
|
||||||
{
|
{
|
||||||
return file_url_from_path(boost::filesystem::path(resources_dir()) / "web") + "/";
|
const std::string dir = (boost::filesystem::path(resources_dir()) / "web").make_preferred().string();
|
||||||
|
return wxString("file://") + from_u8(dir) + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_content_url(const wxString& url)
|
bool is_content_url(const wxString& url)
|
||||||
{
|
{
|
||||||
// The web view reports the URL it parsed, which may escape the resources path differently
|
// The web view reports the URL it parsed, which escapes anything the resources path holds
|
||||||
// from content_base_url().
|
// (a space, a non-ASCII character), while content_base_url() is the raw path.
|
||||||
return wxURI::Unescape(url.BeforeFirst('#')) == wxURI::Unescape(content_base_url());
|
return wxURI::Unescape(url.BeforeFirst('#')) == content_base_url();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* orca_bridge_script() { return ORCA_BRIDGE_JS; }
|
const char* orca_bridge_script() { return ORCA_BRIDGE_JS; }
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ bool WebViewHostDialog::create_webview(const std::string& resource_path,
|
|||||||
|
|
||||||
wxString WebViewHostDialog::build_resource_url(const std::string& resource_path) const
|
wxString WebViewHostDialog::build_resource_url(const std::string& resource_path) const
|
||||||
{
|
{
|
||||||
wxString target_url = file_url_from_path(boost::filesystem::path(resources_dir()) / resource_path);
|
wxString target_url = from_u8((boost::filesystem::path(resources_dir()) / resource_path).make_preferred().string());
|
||||||
|
|
||||||
if (append_language_to_url()) {
|
if (append_language_to_url()) {
|
||||||
const wxString lang = wxGetApp().current_language_code_safe();
|
const wxString lang = wxGetApp().current_language_code_safe();
|
||||||
@@ -202,7 +202,7 @@ wxString WebViewHostDialog::build_resource_url(const std::string& resource_path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return target_url;
|
return wxString("file://") + target_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewHostDialog::load_url(const wxString& url)
|
void WebViewHostDialog::load_url(const wxString& url)
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ WipingDialog::WipingDialog(wxWindow* parent, const int max_flush_volume) :
|
|||||||
wxString filepath_str = from_path(filepath);
|
wxString filepath_str = from_path(filepath);
|
||||||
wxFileName fn(filepath_str);
|
wxFileName fn(filepath_str);
|
||||||
if(fn.FileExists()) {
|
if(fn.FileExists()) {
|
||||||
wxString url = file_url_from_path(filepath);
|
wxString url = wxFileSystem::FileNameToURL(fn);
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< "File exists and load url " << url.ToStdString();
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< "File exists and load url " << url.ToStdString();
|
||||||
m_webview->LoadURL(url);
|
m_webview->LoadURL(url);
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< "Successfully loaded url: " << url.ToStdString();
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< "Successfully loaded url: " << url.ToStdString();
|
||||||
|
|||||||
@@ -274,4 +274,13 @@ bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionTyp
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Duet::get_err_code_from_body(const std::string &body) const
|
||||||
|
{
|
||||||
|
pt::ptree root;
|
||||||
|
std::istringstream iss (body); // wrap returned json to istringstream
|
||||||
|
pt::read_json(iss, root);
|
||||||
|
|
||||||
|
return root.get<int>("err", 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ private:
|
|||||||
ConnectionType connect(wxString &msg) const;
|
ConnectionType connect(wxString &msg) const;
|
||||||
void disconnect(ConnectionType connectionType) const;
|
void disconnect(ConnectionType connectionType) const;
|
||||||
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const;
|
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const;
|
||||||
|
int get_err_code_from_body(const std::string &body) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,15 @@ bool ESP3D::start_print(wxString& msg, const std::string& filename) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ESP3D::get_err_code_from_body(const std::string& body) const
|
||||||
|
{
|
||||||
|
pt::ptree root;
|
||||||
|
std::istringstream iss(body); // wrap returned json to istringstream
|
||||||
|
pt::read_json(iss, root);
|
||||||
|
|
||||||
|
return root.get<int>("err", 0);
|
||||||
|
}
|
||||||
|
|
||||||
// ESP3D only accepts 8.3 filenames else it crashes marlin and other undefined behaviour
|
// ESP3D only accepts 8.3 filenames else it crashes marlin and other undefined behaviour
|
||||||
std::string ESP3D::get_short_name(const std::string& filename) const
|
std::string ESP3D::get_short_name(const std::string& filename) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ private:
|
|||||||
std::string m_console_port;
|
std::string m_console_port;
|
||||||
|
|
||||||
bool start_print(wxString& msg, const std::string& filename) const;
|
bool start_print(wxString& msg, const std::string& filename) const;
|
||||||
|
int get_err_code_from_body(const std::string& body) const;
|
||||||
std::string get_short_name(const std::string& filename) const;
|
std::string get_short_name(const std::string& filename) const;
|
||||||
std::string format_command(const std::string& path, const std::string& arg, const std::string& val) const;
|
std::string format_command(const std::string& path, const std::string& arg, const std::string& val) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -340,7 +340,9 @@ namespace Slic3r {
|
|||||||
if (classify_printer_model(config->opt_string("printer_model")) != ElegooPrinterType::CC2)
|
if (classify_printer_model(config->opt_string("printer_model")) != ElegooPrinterType::CC2)
|
||||||
return fallback_webui;
|
return fallback_webui;
|
||||||
|
|
||||||
std::string web_path = GUI::into_u8(GUI::file_url_from_path(boost::filesystem::path(resources_dir()) / "web/elegoolink/lan_service_web/index.html"));
|
std::string web_path = resources_dir() + "/web/elegoolink/lan_service_web/index.html";
|
||||||
|
std::replace(web_path.begin(), web_path.end(), '\\', '/');
|
||||||
|
web_path = "file://" + web_path;
|
||||||
const std::string token = get_cc2_token(config->opt_string("printhost_apikey"));
|
const std::string token = get_cc2_token(config->opt_string("printhost_apikey"));
|
||||||
const std::string host_ip = Http::get_host_header_value(host);
|
const std::string host_ip = Http::get_host_header_value(host);
|
||||||
|
|
||||||
|
|||||||
@@ -510,22 +510,12 @@ bool Flashforge::fetch_material_slots(std::vector<FlashforgeMaterialSlot>& slots
|
|||||||
if (!request_local_api_json("detail", json{{"serialNumber", m_serial_number}, {"checkCode", m_check_code}}.dump(), body, msg))
|
if (!request_local_api_json("detail", json{{"serialNumber", m_serial_number}, {"checkCode", m_check_code}}.dump(), body, msg))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!parse_material_slots(body, slots, supports_material_station)) {
|
const auto parsed = json::parse(body, nullptr, false, true);
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
msg = _(L("Flashforge returned an invalid JSON response."));
|
msg = _(L("Flashforge returned an invalid JSON response."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Flashforge::parse_material_slots(const std::string& body, std::vector<FlashforgeMaterialSlot>& slots, bool* supports_material_station)
|
|
||||||
{
|
|
||||||
slots.clear();
|
|
||||||
|
|
||||||
const auto parsed = json::parse(body, nullptr, false, true);
|
|
||||||
if (parsed.is_discarded())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto& detail = parsed.contains("detail") ? parsed["detail"] : parsed;
|
const auto& detail = parsed.contains("detail") ? parsed["detail"] : parsed;
|
||||||
const auto& station = detail.contains("matlStationInfo") ? detail["matlStationInfo"] :
|
const auto& station = detail.contains("matlStationInfo") ? detail["matlStationInfo"] :
|
||||||
detail.contains("MatlStationInfo") ? detail["MatlStationInfo"] : json();
|
detail.contains("MatlStationInfo") ? detail["MatlStationInfo"] : json();
|
||||||
@@ -552,21 +542,12 @@ bool Flashforge::parse_material_slots(const std::string& body, std::vector<Flash
|
|||||||
if (supports_material_station != nullptr)
|
if (supports_material_station != nullptr)
|
||||||
*supports_material_station = reports_material_station;
|
*supports_material_station = reports_material_station;
|
||||||
|
|
||||||
// Fields are read leniently: firmware may send numbers as strings or flags as numbers.
|
|
||||||
for (const auto& slot : slot_infos) {
|
for (const auto& slot : slot_infos) {
|
||||||
if (!slot.is_object())
|
|
||||||
continue;
|
|
||||||
FlashforgeMaterialSlot info;
|
FlashforgeMaterialSlot info;
|
||||||
info.slot_id = static_cast<int>(slots.size()) + 1;
|
info.slot_id = slot.value("slotId", static_cast<int>(slots.size()) + 1);
|
||||||
if (const auto it = slot.find("slotId"); it != slot.end())
|
info.has_filament = slot.value("hasFilament", false);
|
||||||
try_parse_json_int(*it, info.slot_id);
|
info.material_name = slot.value("materialName", std::string());
|
||||||
int has_filament = 0;
|
info.material_color = slot.value("materialColor", std::string());
|
||||||
if (const auto it = slot.find("hasFilament"); it != slot.end() && try_parse_json_int(*it, has_filament))
|
|
||||||
info.has_filament = has_filament != 0;
|
|
||||||
if (const auto it = slot.find("materialName"); it != slot.end() && it->is_string())
|
|
||||||
info.material_name = it->get<std::string>();
|
|
||||||
if (const auto it = slot.find("materialColor"); it != slot.end() && it->is_string())
|
|
||||||
info.material_color = it->get<std::string>();
|
|
||||||
slots.emplace_back(std::move(info));
|
slots.emplace_back(std::move(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,4 +670,13 @@ std::string Flashforge::extract_host_name() const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Flashforge::get_err_code_from_body(const std::string& body) const
|
||||||
|
{
|
||||||
|
pt::ptree root;
|
||||||
|
std::istringstream iss(body); // wrap returned json to istringstream
|
||||||
|
pt::read_json(iss, root);
|
||||||
|
|
||||||
|
return root.get<int>("err", 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ public:
|
|||||||
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
|
||||||
std::string get_host() const override { return m_host; }
|
std::string get_host() const override { return m_host; }
|
||||||
bool fetch_material_slots(std::vector<FlashforgeMaterialSlot>& slots, bool* supports_material_station, wxString& msg) const;
|
bool fetch_material_slots(std::vector<FlashforgeMaterialSlot>& slots, bool* supports_material_station, wxString& msg) const;
|
||||||
// Parses a local API "detail" reply. Returns false when the body is not valid JSON.
|
|
||||||
static bool parse_material_slots(const std::string& body, std::vector<FlashforgeMaterialSlot>& slots, bool* supports_material_station);
|
|
||||||
static bool discover_printers(std::vector<FlashforgeDiscoveredPrinter>& printers, wxString& msg, int timeout_ms = 10000, int idle_timeout_ms = 1500, int max_retries = 3);
|
static bool discover_printers(std::vector<FlashforgeDiscoveredPrinter>& printers, wxString& msg, int timeout_ms = 10000, int idle_timeout_ms = 1500, int max_retries = 3);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -70,6 +68,7 @@ private:
|
|||||||
bool request_local_api_json(const std::string& path, const std::string& body, std::string& response_body, wxString& error_msg) const;
|
bool request_local_api_json(const std::string& path, const std::string& body, std::string& response_body, wxString& error_msg) const;
|
||||||
std::string make_http_url(const std::string& path) const;
|
std::string make_http_url(const std::string& path) const;
|
||||||
std::string extract_host_name() const;
|
std::string extract_host_name() const;
|
||||||
|
int get_err_code_from_body(const std::string &body) const;
|
||||||
bool connect(wxString& msg) const;
|
bool connect(wxString& msg) const;
|
||||||
bool start_print(wxString& msg, const std::string& filename) const;
|
bool start_print(wxString& msg, const std::string& filename) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,4 +141,13 @@ bool MKS::start_print(wxString& msg, const std::string& filename) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MKS::get_err_code_from_body(const std::string& body) const
|
||||||
|
{
|
||||||
|
pt::ptree root;
|
||||||
|
std::istringstream iss(body); // wrap returned json to istringstream
|
||||||
|
pt::read_json(iss, root);
|
||||||
|
|
||||||
|
return root.get<int>("err", 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ private:
|
|||||||
|
|
||||||
std::string get_upload_url(const std::string& filename) const;
|
std::string get_upload_url(const std::string& filename) const;
|
||||||
bool start_print(wxString& msg, const std::string& filename) const;
|
bool start_print(wxString& msg, const std::string& filename) const;
|
||||||
|
int get_err_code_from_body(const std::string& body) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,8 +339,62 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe
|
|||||||
//BBS: refine preset update logic
|
//BBS: refine preset update logic
|
||||||
bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::path &dest_path)
|
bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::path &dest_path)
|
||||||
{
|
{
|
||||||
const std::string parent_path = (!dest_path.empty() ? dest_path : source_path.parent_path()).string();
|
bool res = true;
|
||||||
return extract_archive_confined(source_path.string(), parent_path);
|
std::string file_path = source_path.string();
|
||||||
|
std::string parent_path = (!dest_path.empty() ? dest_path : source_path.parent_path()).string();
|
||||||
|
mz_zip_archive archive;
|
||||||
|
mz_zip_zero_struct(&archive);
|
||||||
|
|
||||||
|
if (!open_zip_reader(&archive, file_path))
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Unable to open zip reader for "<<file_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||||
|
|
||||||
|
mz_zip_archive_file_stat stat;
|
||||||
|
// we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
|
||||||
|
for (mz_uint i = 0; i < num_entries; ++i)
|
||||||
|
{
|
||||||
|
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||||
|
{
|
||||||
|
std::string dest_file = parent_path+"/"+stat.m_filename;
|
||||||
|
if (stat.m_is_directory) {
|
||||||
|
fs::path dest_path(dest_file);
|
||||||
|
if (!fs::exists(dest_path))
|
||||||
|
fs::create_directories(dest_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (stat.m_uncomp_size == 0) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater]Unzip: invalid size for file "<<stat.m_filename;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_file.c_str(), 0);
|
||||||
|
if (!res) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "[Orca Updater]extract file "<<stat.m_filename<<" to dest "<<dest_file<<" failed";
|
||||||
|
close_zip_reader(&archive);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "[Orca Updater]successfully extract file " << stat.m_file_index << " to "<<dest_file;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
// ensure the zip archive is closed and rethrow the exception
|
||||||
|
close_zip_reader(&archive);
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "[Orca Updater]Archive read exception:"<<e.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater]Unzip: read file stat failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close_zip_reader(&archive);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a leftover partial archive for the vendor about to be synchronized.
|
// Remove a leftover partial archive for the vendor about to be synchronized.
|
||||||
|
|||||||
@@ -3,13 +3,10 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <sstream>
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
@@ -175,20 +172,6 @@ std::string moonraker_error_reason(const std::string &body)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int PrintHost::get_err_code_from_body(const std::string &body)
|
|
||||||
{
|
|
||||||
boost::property_tree::ptree root;
|
|
||||||
std::istringstream iss(body);
|
|
||||||
try {
|
|
||||||
boost::property_tree::read_json(iss, root);
|
|
||||||
} catch (const std::exception &ex) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "PrintHost: response is not valid JSON: " << ex.what();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return root.get<int>("err", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const
|
wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const
|
||||||
{
|
{
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
@@ -432,10 +415,12 @@ void PrintHostJobQueue::priv::remove_source()
|
|||||||
source_to_remove.clear();
|
source_to_remove.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrintHostJobQueue::upload_job(PrintHostJob &job, PrintHost::ProgressFn progress_fn, PrintHost::ErrorFn error_fn, PrintHost::InfoFn info_fn)
|
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||||
{
|
{
|
||||||
|
emit_progress(0); // Indicate the upload is starting
|
||||||
|
|
||||||
// Captured before upload_data is moved into upload() below.
|
// Captured before upload_data is moved into upload() below.
|
||||||
const std::string upload_filename = job.upload_data.source_path.filename().string();
|
const std::string upload_filename = the_job.upload_data.source_path.filename().string();
|
||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
@@ -444,37 +429,19 @@ bool PrintHostJobQueue::upload_job(PrintHostJob &job, PrintHost::ProgressFn prog
|
|||||||
fire_lifecycle_event(LifecycleEvent::UploadStarted, ctx);
|
fire_lifecycle_event(LifecycleEvent::UploadStarted, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||||
std::string error;
|
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||||
// A throwing upload must not stop the worker, or later jobs would stay queued forever.
|
[this](wxString error) { this->error_fn(std::move(error)); },
|
||||||
try {
|
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
||||||
success = job.printhost->upload(std::move(job.upload_data), std::move(progress_fn), error_fn, std::move(info_fn));
|
);
|
||||||
} catch (const std::exception &e) {
|
|
||||||
error = e.what();
|
|
||||||
error_fn(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.name = upload_filename;
|
ctx.name = upload_filename;
|
||||||
ctx.code = success ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error;
|
ctx.code = success ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error;
|
||||||
ctx.msg = error;
|
|
||||||
fire_lifecycle_event(LifecycleEvent::UploadFinished, ctx);
|
fire_lifecycle_event(LifecycleEvent::UploadFinished, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
|
||||||
{
|
|
||||||
emit_progress(0); // Indicate the upload is starting
|
|
||||||
|
|
||||||
bool success = PrintHostJobQueue::upload_job(the_job,
|
|
||||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
|
||||||
[this](wxString error) { this->error_fn(std::move(error)); },
|
|
||||||
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
emit_progress(100);
|
emit_progress(100);
|
||||||
if (the_job.switch_to_device_tab) {
|
if (the_job.switch_to_device_tab) {
|
||||||
|
|||||||
@@ -87,8 +87,6 @@ public:
|
|||||||
|
|
||||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||||
static std::string get_print_host_webui(DynamicPrintConfig *config);
|
static std::string get_print_host_webui(DynamicPrintConfig *config);
|
||||||
// Reads the "err" field of a JSON reply, 0 when absent. Returns -1 when the body is not valid JSON.
|
|
||||||
static int get_err_code_from_body(const std::string &body);
|
|
||||||
|
|
||||||
//Support for cloud webui login
|
//Support for cloud webui login
|
||||||
virtual bool is_cloud() const { return false; }
|
virtual bool is_cloud() const { return false; }
|
||||||
@@ -152,10 +150,6 @@ public:
|
|||||||
void enqueue(PrintHostJob job);
|
void enqueue(PrintHostJob job);
|
||||||
void cancel(size_t id);
|
void cancel(size_t id);
|
||||||
|
|
||||||
// Uploads the job, firing UploadStarted and a matching UploadFinished. An exception thrown by
|
|
||||||
// the upload is reported through error_fn and makes the upload fail.
|
|
||||||
static bool upload_job(PrintHostJob &job, PrintHost::ProgressFn progress_fn, PrintHost::ErrorFn error_fn, PrintHost::InfoFn info_fn);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct priv;
|
struct priv;
|
||||||
std::shared_ptr<priv> p;
|
std::shared_ptr<priv> p;
|
||||||
|
|||||||
@@ -654,4 +654,13 @@ bool UltiMaker::start_print(wxString &msg, const std::string &filename, Connecti
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int UltiMaker::get_err_code_from_body(const std::string &body) const
|
||||||
|
{
|
||||||
|
pt::ptree root;
|
||||||
|
std::istringstream iss (body); // wrap returned json to istringstream
|
||||||
|
pt::read_json(iss, root);
|
||||||
|
|
||||||
|
return root.get<int>("err", 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ private:
|
|||||||
void set_auth(Http& http) const;
|
void set_auth(Http& http) const;
|
||||||
void disconnect(ConnectionType connectionType) const;
|
void disconnect(ConnectionType connectionType) const;
|
||||||
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const;
|
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const;
|
||||||
|
int get_err_code_from_body(const std::string &body) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -768,128 +768,3 @@ TEST_CASE("Each filament prints with its variant of the extruder's variant strin
|
|||||||
CHECK(gcode.find("; nozzle_temperature = " + resolved + "\n") != std::string::npos);
|
CHECK(gcode.find("; nozzle_temperature = " + resolved + "\n") != std::string::npos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An adaptive pressure advance model predicting the same pressure advance at every flow and acceleration.
|
|
||||||
static std::string constant_pressure_advance_model(const std::string &pa)
|
|
||||||
{
|
|
||||||
return pa + ",1,1000\n" + pa + ",500,1000\n" + pa + ",1,100000\n" + pa + ",500,100000";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The pressure advance values a Klipper G-code sets.
|
|
||||||
static std::set<std::string> pressure_advance_values(const std::string &gcode)
|
|
||||||
{
|
|
||||||
const std::string token = "SET_PRESSURE_ADVANCE ADVANCE=";
|
|
||||||
std::set<std::string> values;
|
|
||||||
std::istringstream stream(gcode);
|
|
||||||
for (std::string line; std::getline(stream, line);)
|
|
||||||
if (line.rfind(token, 0) == 0)
|
|
||||||
values.insert(line.substr(token.size(), line.find(';') - token.size()));
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pressure advance, and the adaptive pressure advance model, are tuned per extruder variant like the
|
|
||||||
// other filament variant settings.
|
|
||||||
TEST_CASE("Each filament sets the pressure advance of its extruder variant", "[MultiFilament]")
|
|
||||||
{
|
|
||||||
auto [nozzle_volume_type, filament, pressure_advance, adaptive_pressure_advance] = GENERATE(table<NozzleVolumeType, int, std::string, std::string>({
|
|
||||||
{ nvtStandard, 1, "0.021", "0.012" },
|
|
||||||
{ nvtHighFlow, 1, "0.037", "0.034" },
|
|
||||||
{ nvtHighFlow, 2, "0.043", "0.056" }, // filament 2 defines no High Flow variant
|
|
||||||
}));
|
|
||||||
const bool adaptive = GENERATE(false, true);
|
|
||||||
DYNAMIC_SECTION(get_nozzle_volume_type_string(nozzle_volume_type) << " nozzle, filament " << filament << (adaptive ? ", adaptive" : "")) {
|
|
||||||
DynamicPrintConfig config = multifilament_config(2, {
|
|
||||||
{ "gcode_flavor", "klipper" },
|
|
||||||
{ "extruder_variant_list", "Direct Drive Standard,Direct Drive High Flow" },
|
|
||||||
// filament 1 defines Standard (0.021) and High Flow (0.037), filament 2 Standard (0.043)
|
|
||||||
{ "filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow;Direct Drive Standard" },
|
|
||||||
{ "filament_self_index", "1,1,2" },
|
|
||||||
{ "enable_pressure_advance", "1,1,1" },
|
|
||||||
{ "pressure_advance", "0.021,0.037,0.043" },
|
|
||||||
{ "adaptive_pressure_advance", adaptive ? "1,1,1" : "0,0,0" },
|
|
||||||
{ "sparse_infill_filament_id", filament },
|
|
||||||
{ "internal_solid_filament_id", filament },
|
|
||||||
{ "top_surface_filament_id", filament },
|
|
||||||
{ "bottom_surface_filament_id", filament },
|
|
||||||
{ "outer_wall_filament_id", filament },
|
|
||||||
{ "inner_wall_filament_id", filament },
|
|
||||||
{ "enable_prime_tower", 0 },
|
|
||||||
{ "skirt_loops", 0 },
|
|
||||||
{ "brim_type", "no_brim" },
|
|
||||||
// custom G-code indexes the per-filament arrays by filament
|
|
||||||
{ "machine_start_gcode", "; start pressure advance {pressure_advance[initial_extruder]}" },
|
|
||||||
});
|
|
||||||
config.option<ConfigOptionStrings>("adaptive_pressure_advance_model")->values = {
|
|
||||||
constant_pressure_advance_model("0.012"), constant_pressure_advance_model("0.034"), constant_pressure_advance_model("0.056") };
|
|
||||||
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = { nozzle_volume_type };
|
|
||||||
const std::string gcode = slice({ cube(20) }, config);
|
|
||||||
|
|
||||||
// The toolchange sets the variant's pressure advance; with adaptive pressure advance, the
|
|
||||||
// prediction of the variant's model then replaces it.
|
|
||||||
std::set<std::string> expected{ pressure_advance };
|
|
||||||
if (adaptive)
|
|
||||||
expected.insert(adaptive_pressure_advance);
|
|
||||||
CHECK(pressure_advance_values(gcode) == expected);
|
|
||||||
CHECK(gcode.find("; start pressure advance " + pressure_advance + "\n") != std::string::npos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On a printer with two extruders, a filament takes the pressure advance of the variant of the extruder
|
|
||||||
// it is mapped to, whichever filament and extruder that is.
|
|
||||||
TEST_CASE("Each filament sets the pressure advance of its extruder variant on a two-extruder printer", "[MultiFilament]")
|
|
||||||
{
|
|
||||||
auto [filament, extruder, pressure_advance, adaptive_pressure_advance] = GENERATE(table<int, int, std::string, std::string>({
|
|
||||||
{ 1, 1, "0.021", "0.012" },
|
|
||||||
{ 1, 2, "0.037", "0.034" },
|
|
||||||
{ 2, 1, "0.043", "0.056" },
|
|
||||||
{ 2, 2, "0.049", "0.078" },
|
|
||||||
}));
|
|
||||||
const bool adaptive = GENERATE(false, true);
|
|
||||||
DYNAMIC_SECTION("filament " << filament << " on extruder " << extruder << (adaptive ? ", adaptive" : "")) {
|
|
||||||
// the other filament goes on the other extruder
|
|
||||||
const std::string filament_map = filament == 1 ? std::to_string(extruder) + "," + std::to_string(3 - extruder) :
|
|
||||||
std::to_string(3 - extruder) + "," + std::to_string(extruder);
|
|
||||||
DynamicPrintConfig config = multifilament_config(2, {
|
|
||||||
{ "gcode_flavor", "klipper" },
|
|
||||||
{ "single_extruder_multi_material", 0 },
|
|
||||||
{ "nozzle_diameter", "0.4,0.4" },
|
|
||||||
{ "extruder_printable_height", "0,0" },
|
|
||||||
// extruder 1 has a Standard nozzle, extruder 2 a High Flow one
|
|
||||||
{ "printer_extruder_id", "1,2" },
|
|
||||||
{ "printer_extruder_variant", "Direct Drive Standard;Direct Drive High Flow" },
|
|
||||||
{ "extruder_variant_list", "Direct Drive Standard;Direct Drive High Flow" },
|
|
||||||
{ "filament_map", filament_map },
|
|
||||||
// both filaments define Standard and High Flow
|
|
||||||
{ "filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow;Direct Drive Standard;Direct Drive High Flow" },
|
|
||||||
{ "filament_self_index", "1,1,2,2" },
|
|
||||||
{ "enable_pressure_advance", "1,1,1,1" },
|
|
||||||
{ "pressure_advance", "0.021,0.037,0.043,0.049" },
|
|
||||||
{ "adaptive_pressure_advance", adaptive ? "1,1,1,1" : "0,0,0,0" },
|
|
||||||
{ "sparse_infill_filament_id", filament },
|
|
||||||
{ "internal_solid_filament_id", filament },
|
|
||||||
{ "top_surface_filament_id", filament },
|
|
||||||
{ "bottom_surface_filament_id", filament },
|
|
||||||
{ "outer_wall_filament_id", filament },
|
|
||||||
{ "inner_wall_filament_id", filament },
|
|
||||||
{ "enable_prime_tower", 0 },
|
|
||||||
{ "skirt_loops", 0 },
|
|
||||||
{ "brim_type", "no_brim" },
|
|
||||||
// custom G-code indexes the per-filament arrays by filament
|
|
||||||
{ "machine_start_gcode", "; start pressure advance {pressure_advance[initial_extruder]}" },
|
|
||||||
});
|
|
||||||
config.option<ConfigOptionStrings>("adaptive_pressure_advance_model")->values = {
|
|
||||||
constant_pressure_advance_model("0.012"), constant_pressure_advance_model("0.034"),
|
|
||||||
constant_pressure_advance_model("0.056"), constant_pressure_advance_model("0.078") };
|
|
||||||
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = { etDirectDrive, etDirectDrive };
|
|
||||||
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = { nvtStandard, nvtHighFlow };
|
|
||||||
// keep the mapping above rather than grouping the filaments automatically
|
|
||||||
config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode", true)->value = fmmManual;
|
|
||||||
const std::string gcode = slice({ cube(20) }, config);
|
|
||||||
|
|
||||||
std::set<std::string> expected{ pressure_advance };
|
|
||||||
if (adaptive)
|
|
||||||
expected.insert(adaptive_pressure_advance);
|
|
||||||
CHECK(pressure_advance_values(gcode) == expected);
|
|
||||||
CHECK(gcode.find("; start pressure advance " + pressure_advance + "\n") != std::string::npos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_lay_on_face.cpp
|
test_lay_on_face.cpp
|
||||||
test_model.cpp
|
test_model.cpp
|
||||||
test_utils.cpp
|
test_utils.cpp
|
||||||
test_miniz_extension.cpp
|
|
||||||
test_timeutils.cpp
|
test_timeutils.cpp
|
||||||
test_voronoi.cpp
|
test_voronoi.cpp
|
||||||
test_wipe_tower_estimate.cpp
|
test_wipe_tower_estimate.cpp
|
||||||
|
|||||||
@@ -717,15 +717,3 @@ TEST_CASE("get_index_for_extruder scales the variant column by the requested str
|
|||||||
REQUIRE(col0_stride2 == 0);
|
REQUIRE(col0_stride2 == 0);
|
||||||
REQUIRE(col1_stride2 == 2);
|
REQUIRE(col1_stride2 == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A per-variant filament option read with a single value gives it to every filament variant. A project
|
|
||||||
// exported by an older CLI holds a single value for an option no loaded preset defined, such as
|
|
||||||
// filament_ironing_flow.
|
|
||||||
TEST_CASE("A per-variant filament option read with a single value gives it to every filament variant", "[Config]")
|
|
||||||
{
|
|
||||||
// filament 1 defines Standard and High Flow, filament 2 Standard
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2};
|
|
||||||
config.load_from_ini_string("pressure_advance = 0.021", ForwardCompatibilitySubstitutionRule::Disable);
|
|
||||||
REQUIRE(config.option<ConfigOptionFloats>("pressure_advance")->values == std::vector<double>({0.021, 0.021, 0.021}));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,194 +0,0 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
|
||||||
|
|
||||||
#include "libslic3r/miniz_extension.hpp"
|
|
||||||
|
|
||||||
#include "test_utils.hpp"
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iterator>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void write_zip(const fs::path &zip_file, const std::vector<std::pair<std::string, std::string>> &entries)
|
|
||||||
{
|
|
||||||
mz_zip_archive zip;
|
|
||||||
mz_zip_zero_struct(&zip);
|
|
||||||
REQUIRE(open_zip_writer(&zip, zip_file.string()));
|
|
||||||
for (const auto &[name, content] : entries)
|
|
||||||
REQUIRE(mz_zip_writer_add_mem(&zip, name.c_str(), content.data(), content.size(), MZ_DEFAULT_COMPRESSION));
|
|
||||||
REQUIRE(mz_zip_writer_finalize_archive(&zip));
|
|
||||||
REQUIRE(close_zip_writer(&zip));
|
|
||||||
}
|
|
||||||
|
|
||||||
// miniz refuses to write a name starting with '/', so write a placeholder of the same length and patch it in place.
|
|
||||||
void rename_entry(const fs::path &zip_file, const std::string &from, const std::string &to)
|
|
||||||
{
|
|
||||||
REQUIRE(from.size() == to.size());
|
|
||||||
std::string bytes;
|
|
||||||
{
|
|
||||||
std::ifstream in(zip_file.string(), std::ios::binary);
|
|
||||||
bytes.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
|
||||||
}
|
|
||||||
size_t count = 0;
|
|
||||||
for (size_t pos = bytes.find(from); pos != std::string::npos; pos = bytes.find(from, pos + to.size()), ++count)
|
|
||||||
bytes.replace(pos, from.size(), to);
|
|
||||||
// Once in the local header and once in the central directory.
|
|
||||||
REQUIRE(count == 2);
|
|
||||||
std::ofstream out(zip_file.string(), std::ios::binary | std::ios::trunc);
|
|
||||||
out << bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> list_dir(const fs::path &dir)
|
|
||||||
{
|
|
||||||
std::vector<std::string> names;
|
|
||||||
for (const fs::directory_entry &entry : fs::directory_iterator(dir))
|
|
||||||
names.push_back(entry.path().filename().string());
|
|
||||||
std::sort(names.begin(), names.end());
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string read_file(const fs::path &file)
|
|
||||||
{
|
|
||||||
std::ifstream in(file.string(), std::ios::binary);
|
|
||||||
return std::string(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction writes a well-formed archive under the target directory", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
write_zip(zip_file, {{"vendor/", ""}, {"vendor/machine/", ""}, {"vendor.json", "{\"a\":1}"}, {"vendor/machine/printer.json", "{\"b\":2}"}});
|
|
||||||
|
|
||||||
REQUIRE(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK(fs::is_directory(target / "vendor"));
|
|
||||||
CHECK(read_file(target / "vendor.json") == "{\"a\":1}");
|
|
||||||
CHECK(read_file(target / "vendor" / "machine" / "printer.json") == "{\"b\":2}");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction rejects an archive with an entry outside the target directory", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
|
|
||||||
const std::string escaping_entry = GENERATE(std::string("../escape.txt"), std::string("..\\escape.txt"),
|
|
||||||
std::string("sub/../../escape.txt"), std::string("C:/escape.txt"),
|
|
||||||
std::string("C:escape.txt"), std::string("\\escape.txt"));
|
|
||||||
// The normal entry comes first so a per-entry check would already have written it.
|
|
||||||
write_zip(zip_file, {{"normal.json", "{}"}, {escaping_entry, "escaped"}});
|
|
||||||
|
|
||||||
CAPTURE(escaping_entry);
|
|
||||||
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
|
|
||||||
CHECK(fs::is_empty(target));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction rejects an archive with an absolute entry name", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
|
|
||||||
const std::string absolute = (tmp.path() / "escape.txt").generic_string();
|
|
||||||
const std::string placeholder = "#" + absolute.substr(1);
|
|
||||||
write_zip(zip_file, {{"normal.json", "{}"}, {placeholder, "escaped"}});
|
|
||||||
rename_entry(zip_file, placeholder, absolute);
|
|
||||||
|
|
||||||
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
|
|
||||||
CHECK(fs::is_empty(target));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction rejects a directory entry outside the target directory", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
write_zip(zip_file, {{"vendor/", ""}, {"../outside/", ""}});
|
|
||||||
|
|
||||||
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK_FALSE(fs::exists(tmp.path() / "outside"));
|
|
||||||
CHECK(fs::is_empty(target));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction validates zero-size entries like any other", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
|
|
||||||
SECTION("an empty file inside the target does not fail the archive") {
|
|
||||||
write_zip(zip_file, {{"empty.json", ""}, {"vendor.json", "{}"}});
|
|
||||||
CHECK(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK(read_file(target / "vendor.json") == "{}");
|
|
||||||
}
|
|
||||||
SECTION("an empty file outside the target rejects the archive") {
|
|
||||||
write_zip(zip_file, {{"vendor.json", "{}"}, {"../escape.txt", ""}});
|
|
||||||
CHECK_FALSE(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK_FALSE(fs::exists(tmp.path() / "escape.txt"));
|
|
||||||
CHECK(fs::is_empty(target));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Confined extraction writes nothing outside the target for Windows-specific name forms", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
fs::create_directories(target);
|
|
||||||
|
|
||||||
// Windows strips trailing dots and spaces and maps device names; whether these extract depends on the
|
|
||||||
// platform, but none of them may land beside the target.
|
|
||||||
const std::string name = GENERATE(std::string("name."), std::string("name "), std::string("..."), std::string(".. "),
|
|
||||||
std::string(".. /escape.txt"), std::string(".../escape.txt"), std::string("CON"),
|
|
||||||
std::string("sub/NUL.txt"), std::string("C:escape.txt"));
|
|
||||||
write_zip(zip_file, {{name, "payload"}});
|
|
||||||
|
|
||||||
CAPTURE(name);
|
|
||||||
extract_archive_confined(zip_file.string(), target.string());
|
|
||||||
CHECK(list_dir(tmp.path()) == std::vector<std::string>{"bundle.zip", "cache"});
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
TEST_CASE("Confined extraction replaces a symlink at the destination instead of writing through it", "[MinizExtension]")
|
|
||||||
{
|
|
||||||
ScopedTemporaryDir tmp;
|
|
||||||
const fs::path zip_file = tmp.path() / "bundle.zip";
|
|
||||||
const fs::path target = tmp.path() / "cache";
|
|
||||||
const fs::path outside = tmp.path() / "outside";
|
|
||||||
fs::create_directories(target);
|
|
||||||
fs::create_directories(outside);
|
|
||||||
write_zip(zip_file, {{"vendor.json", "{\"a\":1}"}});
|
|
||||||
|
|
||||||
SECTION("a dangling symlink") {
|
|
||||||
fs::create_symlink(outside / "vendor.json", target / "vendor.json");
|
|
||||||
CHECK(extract_archive_confined(zip_file.string(), target.string()));
|
|
||||||
CHECK_FALSE(fs::exists(outside / "vendor.json"));
|
|
||||||
CHECK_FALSE(fs::is_symlink(fs::symlink_status(target / "vendor.json")));
|
|
||||||
CHECK(read_file(target / "vendor.json") == "{\"a\":1}");
|
|
||||||
}
|
|
||||||
SECTION("a symlink to an existing file") {
|
|
||||||
{ std::ofstream((outside / "vendor.json").string()) << "original"; }
|
|
||||||
fs::create_symlink(outside / "vendor.json", target / "vendor.json");
|
|
||||||
extract_archive_confined(zip_file.string(), target.string());
|
|
||||||
CHECK(read_file(outside / "vendor.json") == "original");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -19,8 +19,9 @@ struct LoadedObj
|
|||||||
ObjInfo info;
|
ObjInfo info;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Loads an OBJ made of the given lines, with a material library that defines material "a".
|
// A tetrahedron with a material and two texture coordinates, (0.25, 0.5) and (0.75, 1).
|
||||||
LoadedObj load_textured_obj(const std::string &body)
|
// Only the first face and the vt lines are varied; the other three faces reference vt 1.
|
||||||
|
LoadedObj load_textured_tetrahedron(const std::string &first_face, const std::string &vts = "vt 0.25 0.5\nvt 0.75 1\n")
|
||||||
{
|
{
|
||||||
ScopedTemporaryFile obj(".obj");
|
ScopedTemporaryFile obj(".obj");
|
||||||
ScopedTemporaryFile mtl(".mtl");
|
ScopedTemporaryFile mtl(".mtl");
|
||||||
@@ -30,7 +31,12 @@ LoadedObj load_textured_obj(const std::string &body)
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
boost::nowide::ofstream out(obj.string());
|
boost::nowide::ofstream out(obj.string());
|
||||||
out << "mtllib " << mtl.path().filename().string() << "\n" << body;
|
out << "mtllib " << mtl.path().filename().string() << "\n"
|
||||||
|
<< "v 0 0 0\nv 10 0 0\nv 0 10 0\nv 0 0 10\n"
|
||||||
|
<< vts
|
||||||
|
<< "usemtl a\n"
|
||||||
|
<< first_face << "\n"
|
||||||
|
<< "f 1/1 2/1 4/1\nf 1/1 4/1 3/1\nf 2/1 3/1 4/1\n";
|
||||||
}
|
}
|
||||||
LoadedObj loaded;
|
LoadedObj loaded;
|
||||||
std::string message;
|
std::string message;
|
||||||
@@ -38,21 +44,6 @@ LoadedObj load_textured_obj(const std::string &body)
|
|||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A tetrahedron with a material and two texture coordinates, (0.25, 0.5) and (0.75, 1).
|
|
||||||
// Only the first face and the vt lines are varied; the other three faces reference vt 1.
|
|
||||||
LoadedObj load_textured_tetrahedron(const std::string &first_face, const std::string &vts = "vt 0.25 0.5\nvt 0.75 1\n")
|
|
||||||
{
|
|
||||||
return load_textured_obj("v 0 0 0\nv 10 0 0\nv 0 10 0\nv 0 0 10\n" + vts + "usemtl a\n" + first_face + "\n" +
|
|
||||||
"f 1/1 2/1 4/1\nf 1/1 4/1 3/1\nf 2/1 3/1 4/1\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture coordinate n is (n / 10, n / 20), so a UV identifies the vt it came from.
|
|
||||||
void check_uv_is_vt(const Vec2f &uv, int vt)
|
|
||||||
{
|
|
||||||
CHECK_THAT(uv.x(), WithinAbs(vt / 10., 1e-6));
|
|
||||||
CHECK_THAT(uv.y(), WithinAbs(vt / 20., 1e-6));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("An out-of-range texture index falls back to a zero UV and keeps the geometry", "[OBJ][Regression]")
|
TEST_CASE("An out-of-range texture index falls back to a zero UV and keeps the geometry", "[OBJ][Regression]")
|
||||||
@@ -129,39 +120,3 @@ TEST_CASE("A texture coordinate with w does not shift the indices of the ones af
|
|||||||
CHECK_THAT(uv[2].x(), WithinAbs(0.75, 1e-6));
|
CHECK_THAT(uv[2].x(), WithinAbs(0.75, 1e-6));
|
||||||
CHECK_THAT(uv[2].y(), WithinAbs(1., 1e-6));
|
CHECK_THAT(uv[2].y(), WithinAbs(1., 1e-6));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Both triangles of a quad take the texture coordinates of their own corners", "[OBJ][Regression]")
|
|
||||||
{
|
|
||||||
const LoadedObj loaded = load_textured_obj("v 0 0 0\nv 10 0 0\nv 10 10 0\nv 0 10 0\n"
|
|
||||||
"vt 0.1 0.05\nvt 0.2 0.1\nvt 0.3 0.15\nvt 0.4 0.2\n"
|
|
||||||
"usemtl a\n"
|
|
||||||
"f 1/1 2/2 3/3 4/4\n");
|
|
||||||
|
|
||||||
REQUIRE(loaded.ok);
|
|
||||||
REQUIRE(loaded.mesh.facets_count() == 2);
|
|
||||||
REQUIRE(loaded.info.uvs.size() == 2);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[0][0], 1);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[0][1], 2);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[0][2], 3);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[1][0], 1);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[1][1], 3);
|
|
||||||
check_uv_is_vt(loaded.info.uvs[1][2], 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Texture coordinates follow the corners of a mesh that is flipped on load", "[OBJ][Regression]")
|
|
||||||
{
|
|
||||||
// The faces wind inwards, so the loader flips them. Vertex n uses vt n.
|
|
||||||
const LoadedObj loaded = load_textured_obj("v 0 0 0\nv 10 0 0\nv 0 10 0\nv 0 0 10\n"
|
|
||||||
"vt 0.1 0.05\nvt 0.2 0.1\nvt 0.3 0.15\nvt 0.4 0.2\n"
|
|
||||||
"usemtl a\n"
|
|
||||||
"f 1/1 2/2 3/3\nf 1/1 4/4 2/2\nf 1/1 3/3 4/4\nf 2/2 4/4 3/3\n");
|
|
||||||
|
|
||||||
REQUIRE(loaded.ok);
|
|
||||||
const indexed_triangle_set &its = loaded.mesh.its;
|
|
||||||
CHECK(its_volume(its) > 0.f);
|
|
||||||
REQUIRE(its.indices.size() == 4);
|
|
||||||
REQUIRE(loaded.info.uvs.size() == 4);
|
|
||||||
for (size_t face = 0; face < its.indices.size(); ++face)
|
|
||||||
for (int corner = 0; corner < 3; ++corner)
|
|
||||||
check_uv_is_vt(loaded.info.uvs[face][corner], its.indices[face][corner] + 1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5482,39 +5482,3 @@ TEST_CASE("Config import confines zip entries, preset names and bundle ids to th
|
|||||||
CHECK_FALSE(any_filename_contains(temp_dir.path(), "bundle-escape"));
|
CHECK_FALSE(any_filename_contains(temp_dir.path(), "bundle-escape"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A project saved before a key joined filament_options_with_variant stores it once per filament,
|
|
||||||
// while the keys that were already per variant store it once per filament variant. Loading such a
|
|
||||||
// project gives every variant of a filament that filament's value.
|
|
||||||
TEST_CASE("A project saved with pressure advance per filament applies it to every variant of the filament", "[Preset][Bundle]")
|
|
||||||
{
|
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
|
||||||
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000", "#00FF00" };
|
|
||||||
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75 };
|
|
||||||
config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "Project PLA", "Project PETG" };
|
|
||||||
// A multi-variant printer: full_print_config() leaves the list out, and the loader splits the
|
|
||||||
// variant keys per filament only when the project carries it.
|
|
||||||
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = { "Direct Drive Standard,Direct Drive High Flow" };
|
|
||||||
// filament 1 defines Standard and High Flow, filament 2 Standard
|
|
||||||
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard" };
|
|
||||||
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1, 1, 2 };
|
|
||||||
config.opt<ConfigOptionFloatsNullable>("filament_flow_ratio")->values = { 0.95, 0.96, 0.97 }; // one per filament variant
|
|
||||||
// One per filament, read through the loader that project files go through.
|
|
||||||
config.load_from_ini_string("pressure_advance = 0.021,0.043", ForwardCompatibilitySubstitutionRule::Disable);
|
|
||||||
// The CLI slices the config as loaded.
|
|
||||||
check_double_vector(config.opt<ConfigOptionFloats>("pressure_advance")->values, { 0.021, 0.021, 0.043 });
|
|
||||||
check_double_vector(config.opt<ConfigOptionFloatsNullable>("filament_flow_ratio")->values, { 0.95, 0.96, 0.97 });
|
|
||||||
// The GUI normalizes the config before load; mirror that so only the production path runs.
|
|
||||||
Preset::normalize(config);
|
|
||||||
|
|
||||||
PresetBundle bundle;
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config));
|
|
||||||
|
|
||||||
REQUIRE(bundle.filament_presets.size() == 2);
|
|
||||||
const DynamicPrintConfig &pla = bundle.filaments.find_preset(bundle.filament_presets[0], false, true)->config;
|
|
||||||
const DynamicPrintConfig &petg = bundle.filaments.find_preset(bundle.filament_presets[1], false, true)->config;
|
|
||||||
check_double_vector(pla.opt<ConfigOptionFloats>("pressure_advance")->values, { 0.021, 0.021 });
|
|
||||||
check_double_vector(petg.opt<ConfigOptionFloats>("pressure_advance")->values, { 0.043 });
|
|
||||||
check_double_vector(pla.opt<ConfigOptionFloatsNullable>("filament_flow_ratio")->values, { 0.95, 0.96 });
|
|
||||||
check_double_vector(petg.opt<ConfigOptionFloatsNullable>("filament_flow_ratio")->values, { 0.97 });
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user