mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-02 13:20:59 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fb07b328a | ||
|
|
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)).
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ jobs:
|
|||||||
"repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
|
"repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
|
||||||
-f branch="$branch" -f event=workflow_dispatch -f per_page=100)"
|
-f branch="$branch" -f event=workflow_dispatch -f per_page=100)"
|
||||||
run_id="$(jq -r --arg marker "[OFL cron $dispatch_id]" \
|
run_id="$(jq -r --arg marker "[OFL cron $dispatch_id]" \
|
||||||
'[.workflow_runs[] | select((.display_title // "") | contains($marker))]
|
'[.workflow_runs[] | select((.display_title // "") | contains($marker))] \
|
||||||
| sort_by(.created_at) | last | .id // empty' <<< "$runs_json")"
|
| sort_by(.created_at) | last | .id // empty' <<< "$runs_json")"
|
||||||
[ -n "$run_id" ] && break
|
[ -n "$run_id" ] && break
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# 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: from the top of the range's
|
||||||
|
bottom layer to the bottom of its top layer, since those two layers are drawn as toolpaths and a
|
||||||
|
face of the model in either layer's plane would flicker against them. The translucent ghost is
|
||||||
|
drawn first, as in every other mode, so the part above the cut stays visible while dragging. The
|
||||||
|
cut itself is capped, one body at a time: a pass counts, per pixel and with the depth test off,
|
||||||
|
the body's back faces less its front faces into the stencil buffer, which is zero for a closed
|
||||||
|
body and one where the cut has opened it, and a quad in each cut plane, in the body's colour
|
||||||
|
darkened, is drawn where it is not zero. Between the lines of a sparse layer on the cut, that quad is what shows, rather than
|
||||||
|
the first layer or the bed far below moving with the camera. A view through both cut planes of
|
||||||
|
a body with neither wall in the way is not capped. 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.
|
||||||
+19
-45
@@ -28,8 +28,8 @@ Per-vendor granularity is what makes the system practical:
|
|||||||
- A vendor whose profile is bumped invalidates only its own cache. The other 60-odd
|
- A vendor whose profile is bumped invalidates only its own cache. The other 60-odd
|
||||||
vendors keep theirs — even when the bumped vendor is the shared Orca filament
|
vendors keep theirs — even when the bumped vendor is the shared Orca filament
|
||||||
library everyone else inherits from.
|
library everyone else inherits from.
|
||||||
- The setup wizard loads its vendors through the same routine as startup, so it
|
- The setup wizard, which loads vendors one at a time, gets the same speedup as
|
||||||
gets the same speedup without a second code path.
|
startup without a second code path.
|
||||||
- A vendor with no cache, or a broken one, costs only that vendor a parse.
|
- A vendor with no cache, or a broken one, costs only that vendor a parse.
|
||||||
|
|
||||||
A cache holds *system* presets only. User presets, project settings and modified
|
A cache holds *system* presets only. User presets, project settings and modified
|
||||||
@@ -162,20 +162,15 @@ cache nothing can invalidate is worse than no cache.
|
|||||||
|
|
||||||
Vendors load in a fixed order, because filament inheritance crosses exactly one
|
Vendors load in a fixed order, because filament inheritance crosses exactly one
|
||||||
boundary: any vendor's filament may inherit from the shared Orca filament library,
|
boundary: any vendor's filament may inherit from the shared Orca filament library,
|
||||||
and nothing else reaches across vendors — an `include` is always vendor-local. Only
|
and nothing else reaches across vendors — an `include` is always vendor-local. The
|
||||||
installing a vendor's presets crosses it; reading the vendor, from its cache or its
|
library therefore goes first, alone; every other vendor follows in parallel, resolving
|
||||||
JSONs, needs nothing from the library. So every other vendor is read while the library
|
against it; and the results are merged in a stable order:
|
||||||
loads, each is installed against it as soon as both are done, and the results are
|
|
||||||
merged in a stable order:
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
lib["1 · OrcaFilamentLibrary loaded;<br/>meanwhile every other vendor read<br/>from its cache or its JSONs"] --> par["2 · every other vendor installed<br/>in parallel, each into its own bundle,<br/>filaments resolving against the library"] --> merge["3 · bundles merged into one,<br/>in one pass per collection,<br/>in stable vendor order"]
|
lib["1 · OrcaFilamentLibrary<br/>loaded first, synchronously"] --> par["2 · every other vendor in parallel,<br/>each into its own bundle, filaments<br/>resolving against the loaded library"] --> merge["3 · bundles merged into one,<br/>sequentially, in stable vendor order"]
|
||||||
```
|
```
|
||||||
|
|
||||||
`PresetBundle::load_vendors` runs these steps for startup and for the setup wizard,
|
|
||||||
which hand it the vendors to load and the directory each is installed in.
|
|
||||||
|
|
||||||
Whether a vendor comes from its cache or from a parse changes nothing in that
|
Whether a vendor comes from its cache or from a parse changes nothing in that
|
||||||
order — both produce the same bundle, so cached and parsed vendors mix freely in
|
order — both produce the same bundle, so cached and parsed vendors mix freely in
|
||||||
one startup.
|
one startup.
|
||||||
@@ -216,12 +211,11 @@ shipped cache answered first, so the profile in `<data_dir>/system/` was never p
|
|||||||
and its cache was never written back.
|
and its cache was never written back.
|
||||||
|
|
||||||
Serving from a cache is not a memory-image restore. The entries are deserialized and
|
Serving from a cache is not a memory-image restore. The entries are deserialized and
|
||||||
then installed by `install_vendor`, the routine the JSON path hands the vendor's entries
|
then installed one by one — inheritance resolved against the presets installed before
|
||||||
to once it has parsed the sub-files: inheritance resolved against the presets installed
|
them and the currently loaded filament library, includes layered in, configs flattened
|
||||||
before them and the currently loaded filament library, includes layered in, configs
|
onto the collection defaults, validated and registered — by the same function the JSON
|
||||||
flattened onto the collection defaults, validated and registered. An `include` layers
|
path calls straight after parsing a sub-file. An `include` layers what the included
|
||||||
what the included base states, between the parent and the preset's own keys: the base's
|
base states, between the parent and the preset's own keys: the base's diff against the
|
||||||
diff against the
|
|
||||||
default, taken when the base itself was installed and before the per-variant padding
|
default, taken when the base itself was installed and before the per-variant padding
|
||||||
`inherits` sees, so only what a template sets reaches the presets including it. The two
|
`inherits` sees, so only what a template sets reaches the presets including it. The two
|
||||||
paths share everything below the parse, which is what makes a cache-loaded bundle
|
paths share everything below the parse, which is what makes a cache-loaded bundle
|
||||||
@@ -229,16 +223,6 @@ indistinguishable from a JSON-loaded one by construction rather than by test cov
|
|||||||
Installation also rebuilds each preset's file path from the local data directory, so a
|
Installation also rebuilds each preset's file path from the local data directory, so a
|
||||||
shipped cache never carries the generating machine's paths.
|
shipped cache never carries the generating machine's paths.
|
||||||
|
|
||||||
Installing an entry is split in two. `resolve_vendor_preset` flattens it, reading only
|
|
||||||
what is registered under the names it inherits and includes, and `commit_vendor_preset`
|
|
||||||
registers it, the only step that writes anything shared. Entries resolve across threads
|
|
||||||
in runs and commit in the order the vendor lists them. A run ends before an entry that
|
|
||||||
inherits or includes one already in it, since that one's commit registers what the
|
|
||||||
entry resolves against, so no entry in a run reads what another in it registers. An
|
|
||||||
entry's parse messages are held until it commits. The bundle, the log's parse and
|
|
||||||
install messages and the error count therefore come out as parsing and installing one
|
|
||||||
entry at a time would leave them, whatever the listing order.
|
|
||||||
|
|
||||||
App upgrades work because a cache normally survives one. Only a deliberate
|
App upgrades work because a cache normally survives one. Only a deliberate
|
||||||
`CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at
|
`CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at
|
||||||
install time rather than at load: a vendor whose cache this build cannot read counts
|
install time rather than at load: a vendor whose cache this build cannot read counts
|
||||||
@@ -270,20 +254,17 @@ the wizard caches the *derived JSON*, not another form of the inputs:
|
|||||||
open, the wizard computes the current stamps (one version peek per vendor) and, when
|
open, the wizard computes the current stamps (one version peek per vendor) and, when
|
||||||
they match, serves the catalog from the file — no bundle built, no preset installed.
|
they match, serves the catalog from the file — no bundle built, no preset installed.
|
||||||
Caching bundle inputs instead was tried and measured: rebuilding the bundle from
|
Caching bundle inputs instead was tried and measured: rebuilding the bundle from
|
||||||
per-vendor caches costs over a second of preset installation whatever feeds it, so
|
per-vendor caches costs ~2 s of preset installation whatever feeds it, so only
|
||||||
only skipping the rebuild entirely wins.
|
skipping the rebuild entirely wins.
|
||||||
|
|
||||||
Any change to the set — a vendor added, removed or updated, or its cache-only
|
Any change to the set — a vendor added, removed or updated, or its cache-only
|
||||||
`.opc` replaced by a newer one — changes the stamps and retires the whole file;
|
`.opc` replaced by a newer one — changes the stamps and retires the whole file;
|
||||||
the wizard then rebuilds the bundle with `PresetBundle::load_vendors`, the load
|
the wizard then rebuilds the bundle vendor by vendor (per-vendor caches serving where
|
||||||
startup uses (per-vendor caches serving where they cover), and writes the catalog
|
they cover) and writes the catalog back. Selections, region and per-open decorations
|
||||||
back. When a vendor fails to load, the filament library included, that open falls
|
are applied downstream of the cache either way, so a served catalog is
|
||||||
back to the wizard's own scan of the vendor JSONs, as when no bundle can be built,
|
indistinguishable from a rebuilt one. Nothing ships this file and the updater never
|
||||||
and writes nothing. Selections, region and per-open decorations are applied
|
touches it; it is a locally written artifact, re-derived whenever stale, written
|
||||||
downstream of the cache either way, so a served catalog is indistinguishable from a
|
through a temp file and rename so half a cache is never readable.
|
||||||
rebuilt one. Nothing ships this file and the updater never touches it; it is a
|
|
||||||
locally written artifact, re-derived whenever stale, written through a temp file and
|
|
||||||
rename so half a cache is never readable.
|
|
||||||
|
|
||||||
The cache lives under `<data_dir>/cache/`, not beside the vendors: everything that
|
The cache lives under `<data_dir>/cache/`, not beside the vendors: everything that
|
||||||
scans `<data_dir>/system/` treats any `.opc` there as a vendor, so a non-vendor
|
scans `<data_dir>/system/` treats any `.opc` there as a vendor, so a non-vendor
|
||||||
@@ -397,13 +378,6 @@ enumerates only `*.json` will find no vendors at all in a packaged build.
|
|||||||
the `CachedPreset` field list — written and read by `visit_entry` in
|
the `CachedPreset` field list — written and read by `visit_entry` in
|
||||||
`PresetCacheFormat.cpp`, one list for the save, the load and the name peek alike — or
|
`PresetCacheFormat.cpp`, one list for the save, the load and the name peek alike — or
|
||||||
the cache's own layout or stamps, requires bumping `CACHE_VERSION` by hand.
|
the cache's own layout or stamps, requires bumping `CACHE_VERSION` by hand.
|
||||||
- **Adding a kind of reference between presets**, as `inherits` and `include` are:
|
|
||||||
parse the names into `CachedPreset` (a field change, so `CACHE_VERSION` is bumped),
|
|
||||||
have `install_vendor_entries` end a run before an entry that names one already in it
|
|
||||||
and retain what the names point at, look them up only in `resolve_vendor_preset`, and
|
|
||||||
register what they point at only in `commit_vendor_preset`. The listing-order test in
|
|
||||||
`test_vendor_cache.cpp` fails for a kind the runs do not check once its fixture uses
|
|
||||||
it.
|
|
||||||
- **The dictionary indexes with a `uint16`**, so `print_config_def` may hold at most
|
- **The dictionary indexes with a `uint16`**, so `print_config_def` may hold at most
|
||||||
65535 options and one cache at most 65535 distinct enum value names.
|
65535 options and one cache at most 65535 distinct enum value names.
|
||||||
`CacheDictionary::save` throws past that, which surfaces when CI generates the
|
`CacheDictionary::save` throws past that, which surfaces when CI generates the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "OrcaFilamentLibrary",
|
"name": "OrcaFilamentLibrary",
|
||||||
"version": "02.04.00.17",
|
"version": "02.04.00.15",
|
||||||
"force_update": "0",
|
"force_update": "0",
|
||||||
"description": "Orca Filament Library",
|
"description": "Orca Filament Library",
|
||||||
"filament_list": [
|
"filament_list": [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"filament_id": "OFvgE0Zh",
|
"filament_id": "OFvgE0Zh",
|
||||||
"instantiation": "false",
|
"instantiation": "false",
|
||||||
"fan_cooling_layer_time": [
|
"fan_cooling_layer_time": [
|
||||||
"85"
|
"81"
|
||||||
],
|
],
|
||||||
"fan_min_speed": [
|
"fan_min_speed": [
|
||||||
"50"
|
"50"
|
||||||
|
|||||||
@@ -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>');
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#'+ID+' .FileListBoard').html(strHtml);
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
+9
-85
@@ -1913,10 +1913,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
old_printable_width = static_cast<int>(old_printable_bbox.size().x());
|
old_printable_width = static_cast<int>(old_printable_bbox.size().x());
|
||||||
old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
|
old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
|
||||||
}
|
}
|
||||||
// A 3mf can carry an empty project_settings.config - the models in
|
old_printable_height = (int)(config.opt_float("printable_height"));
|
||||||
// resources/handy_models do - and opt_float() dereferences without checking.
|
|
||||||
if (config.option<ConfigOptionFloat>("printable_height"))
|
|
||||||
old_printable_height = (int)(config.opt_float("printable_height"));
|
|
||||||
|
|
||||||
if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_rod"))
|
if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_rod"))
|
||||||
old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod");
|
old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod");
|
||||||
@@ -2119,14 +2116,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
|
|
||||||
// One resolver for the whole run, so presets from the same vendor tree share its load.
|
// One resolver for the whole run, so presets from the same vendor tree share its load.
|
||||||
std::unique_ptr<PresetBundle> system_preset_resolver;
|
std::unique_ptr<PresetBundle> system_preset_resolver;
|
||||||
auto ensure_system_preset_resolver = [&system_preset_resolver]() -> PresetBundle & {
|
auto resolve_preset = [&ensure_cli_preset_bundle, &system_preset_resolver](const std::string &file, DynamicPrintConfig &config,
|
||||||
if (!system_preset_resolver)
|
std::string &config_type, const std::string &config_from,
|
||||||
system_preset_resolver = std::make_unique<PresetBundle>();
|
bool probe_type, std::string &error) {
|
||||||
return *system_preset_resolver;
|
|
||||||
};
|
|
||||||
auto resolve_preset = [&ensure_cli_preset_bundle, &ensure_system_preset_resolver](const std::string &file, DynamicPrintConfig &config,
|
|
||||||
std::string &config_type, const std::string &config_from,
|
|
||||||
bool probe_type, std::string &error) {
|
|
||||||
const auto *inherits = config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS);
|
const auto *inherits = config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS);
|
||||||
if (!probe_type && (inherits == nullptr || inherits->value.empty()))
|
if (!probe_type && (inherits == nullptr || inherits->value.empty()))
|
||||||
return true;
|
return true;
|
||||||
@@ -2134,7 +2126,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
PresetBundle *bundle = nullptr;
|
PresetBundle *bundle = nullptr;
|
||||||
bool allow_source_manifest = false;
|
bool allow_source_manifest = false;
|
||||||
if (config_from == "system") {
|
if (config_from == "system") {
|
||||||
bundle = &ensure_system_preset_resolver();
|
if (!system_preset_resolver)
|
||||||
|
system_preset_resolver = std::make_unique<PresetBundle>();
|
||||||
|
bundle = system_preset_resolver.get();
|
||||||
allow_source_manifest = true;
|
allow_source_manifest = true;
|
||||||
} else {
|
} else {
|
||||||
bundle = ensure_cli_preset_bundle(error);
|
bundle = ensure_cli_preset_bundle(error);
|
||||||
@@ -2595,8 +2589,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
orig_printable_width = static_cast<int>(orig_printable_bbox.size().x());
|
orig_printable_width = static_cast<int>(orig_printable_bbox.size().x());
|
||||||
orig_printable_depth = static_cast<int>(orig_printable_bbox.size().y());
|
orig_printable_depth = static_cast<int>(orig_printable_bbox.size().y());
|
||||||
}
|
}
|
||||||
if (config.option<ConfigOptionFloat>("printable_height"))
|
orig_printable_height = (int)(config.opt_float("printable_height"));
|
||||||
orig_printable_height = (int)(config.opt_float("printable_height"));
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%")
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%")
|
||||||
%__LINE__ %old_printable_width %orig_printable_width %old_printable_depth %orig_printable_depth %old_printable_height %orig_printable_height;
|
%__LINE__ %old_printable_width %orig_printable_width %old_printable_depth %orig_printable_depth %old_printable_height %orig_printable_height;
|
||||||
if ((orig_printable_width > 0) && (orig_printable_depth > 0) && (orig_printable_height > 0))
|
if ((orig_printable_width > 0) && (orig_printable_depth > 0) && (orig_printable_height > 0))
|
||||||
@@ -3127,44 +3120,6 @@ int CLI::run(int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load the project's printer and process settings as the GUI loads its presets: over the default preset,
|
|
||||||
// with every key the project does not list as changed, including keys saved before an option existed,
|
|
||||||
// taken from its current system preset.
|
|
||||||
auto load_project_preset = [this, &ensure_system_preset_resolver, ¤t_different_settings, filament_count](const std::string &system_name, Preset::Type type) {
|
|
||||||
if (system_name.empty())
|
|
||||||
return;
|
|
||||||
// Preset bookkeeping the CLI keeps in its own groups, e.g. inherits_group and print_compatible_printers.
|
|
||||||
static const std::set<std::string> bookkeeping_keys = {"inherits", "compatible_printers", "compatible_prints", "compatible_printers_condition",
|
|
||||||
"compatible_prints_condition", "print_settings_id", "printer_settings_id"};
|
|
||||||
const size_t index = type == Preset::TYPE_PRINTER ? filament_count + 1 : 0;
|
|
||||||
PresetBundle &resolver = ensure_system_preset_resolver();
|
|
||||||
DynamicPrintConfig system_config;
|
|
||||||
t_config_option_keys keys;
|
|
||||||
const DynamicPrintConfig config = Preset::load_external_config(type,
|
|
||||||
type == Preset::TYPE_PRINTER ? resolver.printers.default_preset_for(m_print_config).config : resolver.prints.default_preset().config,
|
|
||||||
m_print_config, PresetBundle::project_different_keys(index < current_different_settings.size() ? current_different_settings[index] : std::string()),
|
|
||||||
[&](const std::string &) -> DynamicPrintConfig * {
|
|
||||||
std::string error;
|
|
||||||
if (resolver.resolve_system_preset(system_config, type, system_name, config_substitution_rule, error))
|
|
||||||
return &system_config;
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: system preset '%1%' not resolved (%2%); the project keeps its values") % system_name % error;
|
|
||||||
return nullptr;
|
|
||||||
}, &keys);
|
|
||||||
for (const std::string &key : keys) {
|
|
||||||
const ConfigOption *opt = config.option(key);
|
|
||||||
const ConfigOption *old = m_print_config.option(key);
|
|
||||||
if (bookkeeping_keys.count(key) != 0 || opt == nullptr || (old != nullptr && *old == *opt))
|
|
||||||
continue;
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("CLI: %1% from '%2%': %3% -> %4%") % key % system_name % (old ? old->serialize() : std::string("(missing)")) % opt->serialize();
|
|
||||||
m_print_config.set_key_value(key, opt->clone());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// The --uptodate path refreshes the project from its own system configs.
|
|
||||||
if (new_printer_name.empty() && load_machine_config.empty())
|
|
||||||
load_project_preset(current_printer_system_name, Preset::TYPE_PRINTER);
|
|
||||||
if (new_process_name.empty() && load_process_config.empty())
|
|
||||||
load_project_preset(current_process_system_name, Preset::TYPE_PRINT);
|
|
||||||
|
|
||||||
std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
|
std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
|
||||||
std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
|
std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
|
||||||
inherits_group.resize(filament_count + 2, std::string());
|
inherits_group.resize(filament_count + 2, std::string());
|
||||||
@@ -3521,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];
|
||||||
@@ -3700,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)
|
||||||
{
|
{
|
||||||
@@ -4153,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);
|
||||||
@@ -4662,8 +4587,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
BoundingBoxf temp_printable_bbox(temp_printable_area);
|
BoundingBoxf temp_printable_bbox(temp_printable_area);
|
||||||
printer_plate.printable_width = static_cast<int>(temp_printable_bbox.size().x());
|
printer_plate.printable_width = static_cast<int>(temp_printable_bbox.size().x());
|
||||||
printer_plate.printable_depth = static_cast<int>(temp_printable_bbox.size().y());
|
printer_plate.printable_depth = static_cast<int>(temp_printable_bbox.size().y());
|
||||||
if (config.option<ConfigOptionFloat>("printable_height"))
|
printer_plate.printable_height = (int)(config.opt_float("printable_height"));
|
||||||
printer_plate.printable_height = (int)(config.opt_float("printable_height"));
|
|
||||||
}
|
}
|
||||||
if (temp_exclude_area.size() >= 4) {
|
if (temp_exclude_area.size() >= 4) {
|
||||||
printer_plate.exclude_width = (int)(temp_exclude_area[2].x() - temp_exclude_area[0].x());
|
printer_plate.exclude_width = (int)(temp_exclude_area[2].x() - temp_exclude_area[0].x());
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -511,10 +518,6 @@ void AppConfig::set_defaults()
|
|||||||
set_bool("enable_high_low_temp_mixed_printing", false);
|
set_bool("enable_high_low_temp_mixed_printing", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get("remember_print_action").empty()) {
|
|
||||||
set_bool("remember_print_action", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get("ignore_ext_filament_in_filament_map").empty()){
|
if (get("ignore_ext_filament_in_filament_map").empty()){
|
||||||
set_bool("ignore_ext_filament_in_filament_map", false);
|
set_bool("ignore_ext_filament_in_filament_map", false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,7 +355,6 @@ set(lisbslic3r_sources
|
|||||||
Optimize/Optimizer.hpp
|
Optimize/Optimizer.hpp
|
||||||
Orient.cpp
|
Orient.cpp
|
||||||
Orient.hpp
|
Orient.hpp
|
||||||
ParallelResolve.hpp
|
|
||||||
ParameterUtils.cpp
|
ParameterUtils.cpp
|
||||||
ParameterUtils.hpp
|
ParameterUtils.hpp
|
||||||
pchheader.cpp
|
pchheader.cpp
|
||||||
|
|||||||
+13
-26
@@ -849,19 +849,6 @@ ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardC
|
|||||||
return std::move(substitutions_ctxt.substitutions);
|
return std::move(substitutions_ctxt.substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case-insensitive compare of a JSON key against a fixed ASCII one, without
|
|
||||||
// boost::iequals, whose std::locale() takes a lock the whole process shares in the
|
|
||||||
// MSVC runtime.
|
|
||||||
static bool ascii_iequals(const std::string &key, const char *literal)
|
|
||||||
{
|
|
||||||
auto lower = [](char c) { return (c >= 'A' && c <= 'Z') ? char(c - 'A' + 'a') : c; };
|
|
||||||
size_t i = 0;
|
|
||||||
for (; i < key.size() && literal[i] != '\0'; ++ i)
|
|
||||||
if (lower(key[i]) != lower(literal[i]))
|
|
||||||
return false;
|
|
||||||
return i == key.size() && literal[i] == '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map<std::string, std::string>& key_values, std::string& reason)
|
int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map<std::string, std::string>& key_values, std::string& reason)
|
||||||
{
|
{
|
||||||
json j;
|
json j;
|
||||||
@@ -929,44 +916,44 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||||||
}
|
}
|
||||||
//parse the json elements
|
//parse the json elements
|
||||||
for (auto it = j.begin(); it != j.end(); it++) {
|
for (auto it = j.begin(); it != j.end(); it++) {
|
||||||
if (ascii_iequals(it.key(), BBL_JSON_KEY_VERSION)) {
|
if (boost::iequals(it.key(),BBL_JSON_KEY_VERSION)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
|
key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
|
||||||
//skip it
|
//skip it
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_NAME)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
|
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
|
||||||
if (it.value() == "project_settings")
|
if (it.value() == "project_settings")
|
||||||
is_project_settings = true;
|
is_project_settings = true;
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_URL)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_URL)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_URL, it.value());
|
key_values.emplace(BBL_JSON_KEY_URL, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_TYPE)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_TYPE)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_TYPE, it.value());
|
key_values.emplace(BBL_JSON_KEY_TYPE, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_SETTING_ID)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_SETTING_ID)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_SETTING_ID, it.value());
|
key_values.emplace(BBL_JSON_KEY_SETTING_ID, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_FILAMENT_ID, it.value());
|
key_values.emplace(BBL_JSON_KEY_FILAMENT_ID, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_FROM)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_FROM)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_FROM, it.value());
|
key_values.emplace(BBL_JSON_KEY_FROM, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_DESCRIPTION, it.value());
|
key_values.emplace(BBL_JSON_KEY_DESCRIPTION, it.value());
|
||||||
}
|
}
|
||||||
else if (ascii_iequals(it.key(), BBL_JSON_KEY_INSTANTIATION)) {
|
else if (boost::iequals(it.key(), BBL_JSON_KEY_INSTANTIATION)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_INSTANTIATION, it.value());
|
key_values.emplace(BBL_JSON_KEY_INSTANTIATION, it.value());
|
||||||
}
|
}
|
||||||
else if (!load_inherits_to_config && ascii_iequals(it.key(), BBL_JSON_KEY_INHERITS)) {
|
else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INHERITS)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_INHERITS, it.value());
|
key_values.emplace(BBL_JSON_KEY_INHERITS, it.value());
|
||||||
}
|
}
|
||||||
else if (!load_inherits_to_config && ascii_iequals(it.key(), BBL_JSON_KEY_INCLUDES)) {
|
else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INCLUDES)) {
|
||||||
key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump());
|
key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump());
|
||||||
} else if (ascii_iequals(it.key(), ORCA_JSON_KEY_RENAMED_FROM)) {
|
} else if (boost::iequals(it.key(), ORCA_JSON_KEY_RENAMED_FROM)) {
|
||||||
key_values.emplace(ORCA_JSON_KEY_RENAMED_FROM, it.value());
|
key_values.emplace(ORCA_JSON_KEY_RENAMED_FROM, it.value());
|
||||||
} else {
|
} else {
|
||||||
t_config_option_key opt_key = it.key();
|
t_config_option_key opt_key = it.key();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
|
||||||
#include "FillBase.hpp"
|
#include "FillBase.hpp"
|
||||||
#include "FillGyroid.hpp"
|
#include "FillGyroid.hpp"
|
||||||
|
|
||||||
@@ -124,41 +123,44 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeats one period of a wave from the last sample at or before x_min to the first one at or after x_max.
|
|
||||||
static inline Polyline make_wave(
|
static inline Polyline make_wave(
|
||||||
const std::vector<Vec2d>& one_period, double x_min, double x_max, double offset, double scaleFactor, bool vertical)
|
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
||||||
|
double z_cos, double z_sin, bool vertical, bool flip)
|
||||||
{
|
{
|
||||||
const double period = one_period.back().x();
|
std::vector<Vec2d> points = one_period;
|
||||||
// The last sample of a period is the first one of the next.
|
double period = points.back()(0);
|
||||||
const size_t n = one_period.size() - 1;
|
if (width != period) // do not extend if already truncated
|
||||||
double x0 = std::floor(x_min / period) * period;
|
{
|
||||||
size_t i = 0;
|
points.reserve(one_period.size() * size_t(floor(width / period)));
|
||||||
while (i + 1 < n && x0 + one_period[i + 1].x() <= x_min)
|
points.pop_back();
|
||||||
++i;
|
|
||||||
|
|
||||||
|
size_t n = points.size();
|
||||||
|
do {
|
||||||
|
points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
|
||||||
|
} while (points.back()(0) < width - EPSILON);
|
||||||
|
|
||||||
|
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// and construct the final polyline to return:
|
||||||
Polyline polyline;
|
Polyline polyline;
|
||||||
polyline.points.reserve(size_t((x_max - x0) / period + 1.) * n + 1);
|
polyline.points.reserve(points.size());
|
||||||
for (;;) {
|
for (auto& point : points) {
|
||||||
Vec2d point(x0 + one_period[i].x(), one_period[i].y() + offset);
|
point(1) += offset;
|
||||||
const bool last = point.x() >= x_max;
|
point(1) = std::clamp(double(point.y()), 0., height);
|
||||||
if (vertical)
|
if (vertical)
|
||||||
std::swap(point(0), point(1));
|
std::swap(point(0), point(1));
|
||||||
polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
|
polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
|
||||||
if (last)
|
|
||||||
break;
|
|
||||||
if (++i == n) {
|
|
||||||
i = 0;
|
|
||||||
x0 += period;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return polyline;
|
return polyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Vec2d> make_one_period(double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
||||||
{
|
{
|
||||||
std::vector<Vec2d> points;
|
std::vector<Vec2d> points;
|
||||||
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
||||||
double limit = 2*M_PI;
|
double limit = std::min(2*M_PI, width);
|
||||||
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
||||||
|
|
||||||
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||||
@@ -237,8 +239,7 @@ static inline double compute_omega_factor(double density_adjusted, double line_s
|
|||||||
return std::clamp(raw, 1.0, 2.0);
|
return std::clamp(raw, 1.0, 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waves covering bbox, with the pattern anchored at origin.
|
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
||||||
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, const BoundingBox &bbox, const Point &origin)
|
|
||||||
{
|
{
|
||||||
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
||||||
|
|
||||||
@@ -253,36 +254,29 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
|
|||||||
const double z_cos = cos(z);
|
const double z_cos = cos(z);
|
||||||
|
|
||||||
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
|
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
|
||||||
// Range to cover in pattern units, with the waves running along x.
|
|
||||||
Vec2d lo = (bbox.min - origin).cast<double>() / scaleFactor;
|
|
||||||
Vec2d hi = (bbox.max - origin).cast<double>() / scaleFactor;
|
|
||||||
double lower_bound = 0.;
|
double lower_bound = 0.;
|
||||||
|
double upper_bound = height;
|
||||||
bool flip = true;
|
bool flip = true;
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
flip = false;
|
flip = false;
|
||||||
lower_bound = -M_PI;
|
lower_bound = -M_PI;
|
||||||
std::swap(lo(0), lo(1));
|
upper_bound = width - M_PI_2;
|
||||||
std::swap(hi(0), hi(1));
|
std::swap(width,height);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vec2d> one_period_odd = make_one_period(scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
||||||
flip = !flip; // even polylines are a bit shifted
|
flip = !flip; // even polylines are a bit shifted
|
||||||
std::vector<Vec2d> one_period_even = make_one_period(scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
||||||
|
|
||||||
// Every wave spans [offset + f_min, offset + f_max] across.
|
|
||||||
double f_min = std::numeric_limits<double>::max();
|
|
||||||
double f_max = std::numeric_limits<double>::lowest();
|
|
||||||
for (const std::vector<Vec2d> *one_period : { &one_period_odd, &one_period_even })
|
|
||||||
for (const Vec2d &point : *one_period) {
|
|
||||||
f_min = std::min(f_min, point.y());
|
|
||||||
f_max = std::max(f_max, point.y());
|
|
||||||
}
|
|
||||||
|
|
||||||
Polylines result;
|
Polylines result;
|
||||||
for (int i = int(std::ceil((lo.y() - f_max - lower_bound) / M_PI)); lower_bound + i * M_PI + f_min <= hi.y(); ++i) {
|
|
||||||
Polyline &wave = result.emplace_back(make_wave(i % 2 == 0 ? one_period_odd : one_period_even, lo.x(), hi.x(),
|
for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
|
||||||
lower_bound + i * M_PI, scaleFactor, vertical));
|
// creates odd polylines
|
||||||
wave.translate(origin);
|
result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||||
|
// creates even polylines
|
||||||
|
y0 += M_PI;
|
||||||
|
if (y0 < upper_bound + EPSILON) {
|
||||||
|
result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -308,12 +302,12 @@ void FillGyroid::_fill_surface_single(
|
|||||||
// Distance between the gyroid waves in scaled coordinates.
|
// Distance between the gyroid waves in scaled coordinates.
|
||||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||||
|
|
||||||
// Anchor the pattern to our grid module; the 10-line shift keeps its established phase.
|
// align bounding box to a multiple of our grid module
|
||||||
const coord_t shift = coord_t(10 * scale_(this->spacing));
|
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||||
const Point origin = align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)) - Point(shift, shift);
|
|
||||||
|
|
||||||
// Keep the pattern ends and the multiline copies outside the contour.
|
// Expand the bounding box to avoid artifacts at the edges
|
||||||
bb.offset(scale_(this->spacing * params.multiline));
|
coord_t expand = 10 * (scale_(this->spacing));
|
||||||
|
bb.offset(expand);
|
||||||
|
|
||||||
// generate pattern
|
// generate pattern
|
||||||
Polylines polylines;
|
Polylines polylines;
|
||||||
@@ -333,14 +327,23 @@ void FillGyroid::_fill_surface_single(
|
|||||||
const float density_factor = std::max(0.001f, float(params.density * DensityAdjust / params.multiline));
|
const float density_factor = std::max(0.001f, float(params.density * DensityAdjust / params.multiline));
|
||||||
const float period = float(2.0 * M_PI) * float(this->spacing) / density_factor;
|
const float period = float(2.0 * M_PI) * float(this->spacing) / density_factor;
|
||||||
|
|
||||||
// A cell of margin for the rings closed along the raster border, and a fixed sampling grid for every region.
|
// bb is already expanded above by 10 * scale_(spacing) for edge artifacts;
|
||||||
const coord_t cell = scaled(marchsq::GyroidField::gsizef);
|
// skip a second offset here to avoid raster-area bloat in the marching squares pass.
|
||||||
bb.offset(cell);
|
|
||||||
bb.merge(align_to_grid(bb.min, Point(cell, cell)));
|
|
||||||
marchsq::GyroidField sf(bb, this->z, period, float(omega));
|
marchsq::GyroidField sf(bb, this->z, period, float(omega));
|
||||||
polylines = marchsq::get_gyroid_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION);
|
polylines = marchsq::get_gyroid_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION);
|
||||||
} else {
|
} else {
|
||||||
polylines = make_gyroid_waves(scale_(this->z), density_adjusted, this->spacing, bb, origin);
|
polylines = make_gyroid_waves(
|
||||||
|
scale_(this->z),
|
||||||
|
density_adjusted,
|
||||||
|
this->spacing,
|
||||||
|
ceil(bb.size()(0) / distance) + 1.,
|
||||||
|
ceil(bb.size()(1) / distance) + 1.);
|
||||||
|
|
||||||
|
// The parametric generator produces wave coords relative to the grid origin;
|
||||||
|
// shift them into absolute layer coords. The marching-squares branch above
|
||||||
|
// already emits absolute coords via GyroidField::to_Point, so it skips this.
|
||||||
|
for (Polyline &pl : polylines)
|
||||||
|
pl.translate(bb.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply multiline offset if needed
|
// Apply multiline offset if needed
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+89
-71
@@ -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.
|
||||||
@@ -4174,28 +4188,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
|
|
||||||
print.throw_if_canceled();
|
print.throw_if_canceled();
|
||||||
|
|
||||||
// Some firmwares only scan the last N lines for the time estimate, so the stats are written
|
|
||||||
// after the config block.
|
|
||||||
if (!is_bbl_printers && !skip_config_block) {
|
|
||||||
file.write("; CONFIG_BLOCK_START\n");
|
|
||||||
std::string full_config;
|
|
||||||
append_full_config(print, full_config);
|
|
||||||
if (!full_config.empty())
|
|
||||||
file.write(full_config);
|
|
||||||
|
|
||||||
// SoftFever: write compatiple info
|
|
||||||
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
|
|
||||||
file.write_format("; first_layer_bed_temperature = %d\n", first_layer_bed_temperature);
|
|
||||||
file.write_format("; bed_shape = %s\n", print.full_print_config().opt_serialize("printable_area").c_str());
|
|
||||||
file.write_format("; first_layer_temperature = %d\n", print.config().nozzle_temperature_initial_layer.get_at(0));
|
|
||||||
file.write_format("; first_layer_height = %.3f\n", print.config().initial_layer_print_height.value);
|
|
||||||
|
|
||||||
//SF TODO
|
|
||||||
// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
|
|
||||||
|
|
||||||
file.write("; CONFIG_BLOCK_END\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get filament stats.
|
// Get filament stats.
|
||||||
file.write(DoExport::update_print_stats_and_format_filament_stats(
|
file.write(DoExport::update_print_stats_and_format_filament_stats(
|
||||||
// Const inputs
|
// Const inputs
|
||||||
@@ -4222,6 +4214,26 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder)
|
GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder)
|
||||||
.c_str());
|
.c_str());
|
||||||
file.write("\n");
|
file.write("\n");
|
||||||
|
if (!skip_config_block) {
|
||||||
|
file.write("; CONFIG_BLOCK_START\n");
|
||||||
|
std::string full_config;
|
||||||
|
append_full_config(print, full_config);
|
||||||
|
if (!full_config.empty())
|
||||||
|
file.write(full_config);
|
||||||
|
|
||||||
|
// SoftFever: write compatiple info
|
||||||
|
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
|
||||||
|
file.write_format("; first_layer_bed_temperature = %d\n", first_layer_bed_temperature);
|
||||||
|
file.write_format("; bed_shape = %s\n", print.full_print_config().opt_serialize("printable_area").c_str());
|
||||||
|
file.write_format("; first_layer_temperature = %d\n", print.config().nozzle_temperature_initial_layer.get_at(0));
|
||||||
|
file.write_format("; first_layer_height = %.3f\n", print.config().initial_layer_print_height.value);
|
||||||
|
|
||||||
|
//SF TODO
|
||||||
|
// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
|
||||||
|
|
||||||
|
file.write("; CONFIG_BLOCK_END\n\n");
|
||||||
|
} // !skip_config_block
|
||||||
|
|
||||||
}
|
}
|
||||||
file.write("\n");
|
file.write("\n");
|
||||||
|
|
||||||
@@ -8378,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.
|
||||||
@@ -8470,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)
|
||||||
@@ -8481,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)),
|
||||||
@@ -8494,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)),
|
||||||
@@ -8602,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");
|
||||||
@@ -8610,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)),
|
||||||
@@ -8625,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)),
|
||||||
@@ -8831,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");
|
||||||
@@ -8839,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)),
|
||||||
@@ -8854,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)),
|
||||||
@@ -9407,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 ---
|
||||||
@@ -9439,10 +9458,10 @@ 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)
|
||||||
flush_v_speed[i] = filament_max_v[i];
|
flush_v_speed[i] = filament_max_v[i];
|
||||||
@@ -9494,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())
|
||||||
@@ -9885,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);
|
return it->second.get();
|
||||||
// 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 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
|
||||||
|
|||||||
@@ -13,19 +13,8 @@
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
// How many setters this thread holds, so the ones nested in another can skip
|
|
||||||
// setlocale, which takes a lock the whole process shares on Windows.
|
|
||||||
static thread_local int s_numeric_locale_depth = 0;
|
|
||||||
|
|
||||||
CNumericLocalesSetter::CNumericLocalesSetter()
|
CNumericLocalesSetter::CNumericLocalesSetter()
|
||||||
{
|
{
|
||||||
// Nested in another setter on this thread, whose "C" the separator check
|
|
||||||
// confirms is still set.
|
|
||||||
if (s_numeric_locale_depth > 0 && is_decimal_separator_point()) {
|
|
||||||
m_nested = true;
|
|
||||||
++ s_numeric_locale_depth;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||||
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
|
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
|
||||||
@@ -40,17 +29,12 @@ CNumericLocalesSetter::CNumericLocalesSetter()
|
|||||||
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
|
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
|
||||||
uselocale(m_new_locale);
|
uselocale(m_new_locale);
|
||||||
#endif
|
#endif
|
||||||
// Counted last, since the destructor does not run for a constructor that throws.
|
|
||||||
++ s_numeric_locale_depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CNumericLocalesSetter::~CNumericLocalesSetter()
|
CNumericLocalesSetter::~CNumericLocalesSetter()
|
||||||
{
|
{
|
||||||
-- s_numeric_locale_depth;
|
|
||||||
if (m_nested)
|
|
||||||
return;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::setlocale(LC_NUMERIC, m_orig_numeric_locale.data());
|
std::setlocale(LC_NUMERIC, m_orig_numeric_locale.data());
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -19,13 +19,8 @@ class CNumericLocalesSetter {
|
|||||||
public:
|
public:
|
||||||
CNumericLocalesSetter();
|
CNumericLocalesSetter();
|
||||||
~CNumericLocalesSetter();
|
~CNumericLocalesSetter();
|
||||||
// A copy would restore the locale twice, and count down once more than up.
|
|
||||||
CNumericLocalesSetter(const CNumericLocalesSetter&) = delete;
|
|
||||||
CNumericLocalesSetter& operator=(const CNumericLocalesSetter&) = delete;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Inside another setter on this thread, which does the setting and restoring.
|
|
||||||
bool m_nested { false };
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::string m_orig_numeric_locale;
|
std::string m_orig_numeric_locale;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <tbb/blocked_range.h>
|
|
||||||
#include <tbb/parallel_for.h>
|
|
||||||
#include <tbb/task_arena.h>
|
|
||||||
#include <tbb/task_group.h>
|
|
||||||
|
|
||||||
#include "Exception.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
// A batch of resolved presets, each a whole config, is what resolve_then_commit adds
|
|
||||||
// to peak memory, so it stays far below a vendor's preset count and above any core count.
|
|
||||||
inline constexpr size_t resolve_batch_size = 64;
|
|
||||||
|
|
||||||
// Resolve `count` items that do not depend on each other and install them one at
|
|
||||||
// a time.
|
|
||||||
//
|
|
||||||
// `resolve(i)` runs on any thread and must read only, since the items are
|
|
||||||
// resolved side by side. `commit(i, resolved)` is called for every item in index
|
|
||||||
// order, on the calling thread, and is where shared state is written.
|
|
||||||
//
|
|
||||||
// The items are worked through in batches, so what is resolved and held at once
|
|
||||||
// does not grow with `count`. An exception from either callable propagates after
|
|
||||||
// the batches before it have been committed. A cancellation of the caller's task
|
|
||||||
// group, which stops a batch partway without an exception, throws RuntimeError
|
|
||||||
// before that batch commits.
|
|
||||||
//
|
|
||||||
// `ChunkSetup`, when given, is constructed once for each piece of a batch TBB hands
|
|
||||||
// out, for per-thread state a resolve would otherwise set up per item, such as the
|
|
||||||
// C numeric locale, whose setting takes a lock the whole process shares.
|
|
||||||
template<class ChunkSetup = std::monostate, class Resolve, class Commit>
|
|
||||||
void resolve_then_commit(size_t count, Resolve resolve, Commit commit)
|
|
||||||
{
|
|
||||||
using Resolved = std::invoke_result_t<Resolve&, size_t>;
|
|
||||||
|
|
||||||
std::vector<Resolved> resolved(std::min(count, resolve_batch_size));
|
|
||||||
for (size_t first = 0; first < count; first += resolve_batch_size) {
|
|
||||||
const size_t last = std::min(first + resolve_batch_size, count);
|
|
||||||
// Isolated, so a thread waiting on the batch runs none of the caller's other
|
|
||||||
// tasks before finishing it.
|
|
||||||
tbb::this_task_arena::isolate([&] {
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(first, last),
|
|
||||||
[&](const tbb::blocked_range<size_t>& range) {
|
|
||||||
ChunkSetup setup;
|
|
||||||
(void) setup;
|
|
||||||
for (size_t i = range.begin(); i < range.end(); ++ i)
|
|
||||||
resolved[i - first] = resolve(i);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (tbb::is_current_task_group_canceling())
|
|
||||||
throw RuntimeError("resolve_then_commit: canceled before the batch was resolved");
|
|
||||||
for (size_t i = first; i < last; ++ i)
|
|
||||||
commit(i, std::move(resolved[i - first]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
|
||||||
+187
-286
@@ -5,8 +5,6 @@
|
|||||||
#include "Preset.hpp"
|
#include "Preset.hpp"
|
||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
#include "LocalesUtils.hpp"
|
|
||||||
#include "ParallelResolve.hpp"
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
@@ -508,11 +506,10 @@ void Preset::normalize(DynamicPrintConfig &config)
|
|||||||
handle_legacy_sla(config);
|
handle_legacy_sla(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config,
|
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
|
||||||
const DynamicPrintConfig *added)
|
|
||||||
{
|
{
|
||||||
std::string incorrect_keys;
|
std::string incorrect_keys;
|
||||||
for (const std::string &key : (added != nullptr ? *added : config).keys())
|
for (const std::string &key : config.keys())
|
||||||
if (! default_config.has(key)) {
|
if (! default_config.has(key)) {
|
||||||
if (incorrect_keys.empty())
|
if (incorrect_keys.empty())
|
||||||
incorrect_keys = key;
|
incorrect_keys = key;
|
||||||
@@ -624,6 +621,7 @@ void Preset::load_info(const std::string& file)
|
|||||||
}
|
}
|
||||||
else if (v.first.compare("base_id") == 0) {
|
else if (v.first.compare("base_id") == 0) {
|
||||||
this->base_id = v.second.get_value<std::string>();
|
this->base_id = v.second.get_value<std::string>();
|
||||||
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load info from: " << file << " and base_id: " << this->base_id;
|
||||||
if (this->base_id.compare("null") == 0)
|
if (this->base_id.compare("null") == 0)
|
||||||
this->base_id.clear();
|
this->base_id.clear();
|
||||||
}
|
}
|
||||||
@@ -976,37 +974,6 @@ void Preset::get_extruder_names_and_keysets(Type type, std::string& extruder_id_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicPrintConfig Preset::load_external_config(Type type, const DynamicPrintConfig &default_config, const DynamicPrintConfig &project_config,
|
|
||||||
const std::set<std::string> &different_settings_list,
|
|
||||||
const std::function<DynamicPrintConfig *(const std::string &inherits)> &find_base,
|
|
||||||
t_config_option_keys *keys)
|
|
||||||
{
|
|
||||||
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
|
|
||||||
DynamicPrintConfig cfg(default_config);
|
|
||||||
// SoftFever: ignore print connection info from project
|
|
||||||
auto cfg_keys = cfg.keys();
|
|
||||||
cfg_keys.erase(std::remove_if(cfg_keys.begin(), cfg_keys.end(),
|
|
||||||
[](std::string &val) {
|
|
||||||
return val == "print_host" || val == "print_host_webui" || val == "printhost_apikey" ||
|
|
||||||
val == "printhost_cafile" || val == "printhost_user" || val == "printhost_password" || val == "printhost_port";
|
|
||||||
}),
|
|
||||||
cfg_keys.end());
|
|
||||||
cfg.apply_only(project_config, cfg_keys, true);
|
|
||||||
|
|
||||||
//add different settings check logic, replace the old system preset's default value with new system preset's default values
|
|
||||||
if (!different_settings_list.empty()) {
|
|
||||||
if (DynamicPrintConfig *base_config = find_base(Preset::inherits(cfg))) {
|
|
||||||
std::string extruder_id_name, extruder_variant_name;
|
|
||||||
std::set<std::string> *key_set1 = nullptr, *key_set2 = nullptr;
|
|
||||||
Preset::get_extruder_names_and_keysets(type, extruder_id_name, extruder_variant_name, &key_set1, &key_set2);
|
|
||||||
cfg.update_non_diff_values_to_base_config(*base_config, cfg_keys, different_settings_list, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keys)
|
|
||||||
*keys = std::move(cfg_keys);
|
|
||||||
return cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Preset::has_lidar(PresetBundle *preset_bundle)
|
bool Preset::has_lidar(PresetBundle *preset_bundle)
|
||||||
{
|
{
|
||||||
bool has_lidar = false;
|
bool has_lidar = false;
|
||||||
@@ -1702,169 +1669,6 @@ std::string PresetCollection::canonical_preset_name(const std::string &name, con
|
|||||||
return get_preset_canonical_name(parsed.bare, origin);
|
return get_preset_canonical_name(parsed.bare, origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
PresetCollection::UserPresetLoad PresetCollection::resolve_user_preset(
|
|
||||||
const boost::filesystem::path &file, const std::string &canonical_name,
|
|
||||||
const PresetOrigin &load_origin, ForwardCompatibilitySubstitutionRule substitution_rule,
|
|
||||||
const std::string &extruder_id_name, const std::string &extruder_variant_name,
|
|
||||||
std::set<std::string> *key_set1, std::set<std::string> *key_set2) const
|
|
||||||
{
|
|
||||||
UserPresetLoad out;
|
|
||||||
out.preset = Preset(m_type, canonical_name, false);
|
|
||||||
Preset &preset = out.preset;
|
|
||||||
preset.bundle_id = load_origin.bundle_id;
|
|
||||||
preset.file = file.string();
|
|
||||||
// Load the preset file, apply preset values on top of defaults.
|
|
||||||
try {
|
|
||||||
fs::path idx_path(preset.file);
|
|
||||||
idx_path.replace_extension(".info");
|
|
||||||
if (fs::exists(idx_path)) {
|
|
||||||
out.info_file = idx_path.string();
|
|
||||||
preset.load_info(out.info_file);
|
|
||||||
}
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
//BBS: change to json format
|
|
||||||
//ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
|
|
||||||
std::map<std::string, std::string> key_values;
|
|
||||||
std::string reason;
|
|
||||||
ConfigSubstitutions config_substitutions = config.load_from_json(preset.file, substitution_rule, key_values, reason);
|
|
||||||
if (! config_substitutions.empty())
|
|
||||||
out.substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(config_substitutions) });
|
|
||||||
if (!reason.empty()) {
|
|
||||||
out.discard_file = true;
|
|
||||||
out.errors.push_back((boost::format("parse config %1% failed") % preset.file).str());
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
|
|
||||||
boost::optional<Semver> version = Semver::parse(version_str);
|
|
||||||
if (!version) return out;
|
|
||||||
preset.version = *version;
|
|
||||||
|
|
||||||
if (key_values.find(BBL_JSON_KEY_FILAMENT_ID) != key_values.end())
|
|
||||||
preset.filament_id = key_values[BBL_JSON_KEY_FILAMENT_ID];
|
|
||||||
if (key_values.find(BBL_JSON_KEY_DESCRIPTION) != key_values.end())
|
|
||||||
preset.description = key_values[BBL_JSON_KEY_DESCRIPTION];
|
|
||||||
if (key_values.find(BBL_JSON_KEY_INSTANTIATION) != key_values.end())
|
|
||||||
preset.is_visible = key_values[BBL_JSON_KEY_INSTANTIATION] != "false";
|
|
||||||
|
|
||||||
//Orca: find and use the inherit config as the base
|
|
||||||
const Preset* inherit_preset = nullptr;
|
|
||||||
ConfigOption* inherits_config = config.option(BBL_JSON_KEY_INHERITS);
|
|
||||||
|
|
||||||
// check inherits_config
|
|
||||||
if (inherits_config) {
|
|
||||||
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
|
|
||||||
std::string inherits_value = option_str->value;
|
|
||||||
// Orca: try to find if the parent preset has been renamed
|
|
||||||
inherit_preset = this->find_preset2(inherits_value);
|
|
||||||
Preset::normalize_inherits(config, inherit_preset);
|
|
||||||
}
|
|
||||||
const Preset& default_preset = this->default_preset_for(config);
|
|
||||||
if (inherit_preset) {
|
|
||||||
preset.config = inherit_preset->config;
|
|
||||||
preset.filament_id = inherit_preset->filament_id;
|
|
||||||
extend_default_config_length(config, false, {});
|
|
||||||
preset.config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
|
|
||||||
if ((inherits_config2 && !inherits_config2->value.empty())) {
|
|
||||||
out.errors.push_back((boost::format("can not find parent %1% for config %2%!") % inherits_config2->value % preset.file).str());
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
// We support custom root preset now
|
|
||||||
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
|
|
||||||
preset.config = default_preset.config;
|
|
||||||
preset.config.apply(std::move(config));
|
|
||||||
extend_default_config_length(preset.config, true, default_preset.config);
|
|
||||||
}
|
|
||||||
|
|
||||||
Preset::normalize(preset.config);
|
|
||||||
// Report configuration fields, which are misplaced into a wrong group.
|
|
||||||
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
|
|
||||||
if (!incorrect_keys.empty())
|
|
||||||
out.errors.push_back("Error in a preset file: The preset \"" + preset.file +
|
|
||||||
"\" contains the following incorrect keys: " + incorrect_keys + ", which were removed");
|
|
||||||
|
|
||||||
if (preset.type == Preset::TYPE_FILAMENT && preset.is_user() && preset.inherits().empty()) {
|
|
||||||
auto compatible_printers = dynamic_cast<ConfigOptionStrings *>(preset.config.option("compatible_printers", true));
|
|
||||||
if (compatible_printers && compatible_printers->values.empty()) {
|
|
||||||
size_t at_pos = canonical_name.find('@');
|
|
||||||
if (at_pos != std::string::npos && at_pos + 1 < canonical_name.length()) {
|
|
||||||
compatible_printers->values.push_back(canonical_name.substr(at_pos + 1));
|
|
||||||
out.save_compatible_printers = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preset.loaded = true;
|
|
||||||
out.complete = true;
|
|
||||||
} catch (const std::ifstream::failure &err) {
|
|
||||||
out.discard_file = true;
|
|
||||||
out.errors.push_back((boost::format("The user-config cannot be loaded: %1%. Reason: %2%") % preset.file % err.what()).str());
|
|
||||||
//throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
|
|
||||||
} catch (const std::runtime_error &err) {
|
|
||||||
out.discard_file = true;
|
|
||||||
out.errors.push_back((boost::format("Failed loading the user-config file: %1%. Reason: %2%") % preset.file % err.what()).str());
|
|
||||||
//throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
|
|
||||||
}
|
|
||||||
out.install = true;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PresetCollection::commit_user_preset(UserPresetLoad &&loaded, std::deque<Preset> &presets_loaded,
|
|
||||||
PresetsConfigSubstitutions &substitutions,
|
|
||||||
const std::function<void(Preset&)> &preset_loaded_fn,
|
|
||||||
bool read_only)
|
|
||||||
{
|
|
||||||
Preset &preset = loaded.preset;
|
|
||||||
if (! loaded.info_file.empty())
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load info from: " << loaded.info_file << " and base_id: " << preset.base_id;
|
|
||||||
append(substitutions, std::move(loaded.substitutions));
|
|
||||||
for (const std::string &error : loaded.errors) {
|
|
||||||
++m_errors;
|
|
||||||
BOOST_LOG_TRIVIAL(error) << error;
|
|
||||||
}
|
|
||||||
if (loaded.discard_file && !read_only) {
|
|
||||||
fs::path file_path(loaded.preset.file);
|
|
||||||
if (fs::exists(file_path))
|
|
||||||
fs::remove(file_path);
|
|
||||||
file_path.replace_extension(".info");
|
|
||||||
if (fs::exists(file_path))
|
|
||||||
fs::remove(file_path);
|
|
||||||
}
|
|
||||||
if (!loaded.install)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (loaded.complete) {
|
|
||||||
if (loaded.save_compatible_printers) {
|
|
||||||
// A filesystem error from the rewrite is counted, and the preset still loads.
|
|
||||||
try {
|
|
||||||
if (!read_only)
|
|
||||||
preset.save(nullptr);
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " added compatible_printers for preset: " << preset.name;
|
|
||||||
} catch (const std::runtime_error &err) {
|
|
||||||
++m_errors;
|
|
||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " can not write compatible_printers back to " << preset.file << ": " << err.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//BBS: add some workaround for previous incorrect settings
|
|
||||||
if ((!preset.setting_id.empty())&&(preset.setting_id == preset.base_id))
|
|
||||||
preset.setting_id.clear();
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load preset: " << preset.name << " and filament_id: " << preset.filament_id << " and base_id: " << preset.base_id;
|
|
||||||
//BBS: add config related logs
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
|
|
||||||
// add alias for custom filament preset
|
|
||||||
set_custom_preset_alias(preset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preset_loaded_fn != nullptr)
|
|
||||||
preset_loaded_fn(preset);
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " load config successful and preset name is:" << preset.name;
|
|
||||||
presets_loaded.emplace_back(std::move(preset));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load all presets found in dir_path.
|
// Load all presets found in dir_path.
|
||||||
// Throws an exception on error.
|
// Throws an exception on error.
|
||||||
void PresetCollection::load_presets(
|
void PresetCollection::load_presets(
|
||||||
@@ -1902,8 +1706,6 @@ void PresetCollection::load_presets(
|
|||||||
std::set<std::string> *key_set1 = nullptr, *key_set2 = nullptr;
|
std::set<std::string> *key_set1 = nullptr, *key_set2 = nullptr;
|
||||||
Preset::get_extruder_names_and_keysets(m_type, extruder_id_name, extruder_variant_name, &key_set1, &key_set2);
|
Preset::get_extruder_names_and_keysets(m_type, extruder_id_name, extruder_variant_name, &key_set1, &key_set2);
|
||||||
|
|
||||||
struct UserPresetFile { fs::path path; std::string canonical_name; };
|
|
||||||
std::vector<UserPresetFile> files;
|
|
||||||
//BBS: change to json format
|
//BBS: change to json format
|
||||||
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
||||||
{
|
{
|
||||||
@@ -1919,26 +1721,149 @@ void PresetCollection::load_presets(
|
|||||||
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << canonical_name;
|
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << canonical_name;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
files.push_back({ dir_entry.path(), std::move(canonical_name) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_then_commit<CNumericLocalesSetter>(files.size(),
|
|
||||||
[&](size_t i) {
|
|
||||||
return this->resolve_user_preset(files[i].path, files[i].canonical_name, resolved_origin, substitution_rule,
|
|
||||||
extruder_id_name, extruder_variant_name, key_set1, key_set2);
|
|
||||||
},
|
|
||||||
[&](size_t, UserPresetLoad &&loaded) {
|
|
||||||
// Committing can remove an unreadable preset's file, and a filesystem error
|
|
||||||
// there is reported without stopping the rest of the directory.
|
|
||||||
try {
|
try {
|
||||||
this->commit_user_preset(std::move(loaded), presets_loaded, substitutions, preset_loaded_fn, read_only);
|
Preset preset(m_type, canonical_name, false);
|
||||||
|
preset.bundle_id = resolved_origin.bundle_id;
|
||||||
|
preset.file = dir_entry.path().string();
|
||||||
|
// Load the preset file, apply preset values on top of defaults.
|
||||||
|
try {
|
||||||
|
fs::path idx_path(preset.file);
|
||||||
|
idx_path.replace_extension(".info");
|
||||||
|
if (fs::exists(idx_path)) {
|
||||||
|
preset.load_info(idx_path.string());
|
||||||
|
}
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
//BBS: change to json format
|
||||||
|
//ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
|
||||||
|
std::map<std::string, std::string> key_values;
|
||||||
|
std::string reason;
|
||||||
|
ConfigSubstitutions config_substitutions = config.load_from_json(preset.file, substitution_rule, key_values, reason);
|
||||||
|
if (! config_substitutions.empty())
|
||||||
|
substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(config_substitutions) });
|
||||||
|
if (!reason.empty()) {
|
||||||
|
fs::path file_path(preset.file);
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
file_path.replace_extension(".info");
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("parse config %1% failed")%preset.file;
|
||||||
|
++m_errors;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
|
||||||
|
boost::optional<Semver> version = Semver::parse(version_str);
|
||||||
|
if (!version) continue;
|
||||||
|
preset.version = *version;
|
||||||
|
|
||||||
|
if (key_values.find(BBL_JSON_KEY_FILAMENT_ID) != key_values.end())
|
||||||
|
preset.filament_id = key_values[BBL_JSON_KEY_FILAMENT_ID];
|
||||||
|
if (key_values.find(BBL_JSON_KEY_DESCRIPTION) != key_values.end())
|
||||||
|
preset.description = key_values[BBL_JSON_KEY_DESCRIPTION];
|
||||||
|
if (key_values.find(BBL_JSON_KEY_INSTANTIATION) != key_values.end())
|
||||||
|
preset.is_visible = key_values[BBL_JSON_KEY_INSTANTIATION] != "false";
|
||||||
|
|
||||||
|
//Orca: find and use the inherit config as the base
|
||||||
|
Preset* inherit_preset = nullptr;
|
||||||
|
ConfigOption* inherits_config = config.option(BBL_JSON_KEY_INHERITS);
|
||||||
|
|
||||||
|
// check inherits_config
|
||||||
|
if (inherits_config) {
|
||||||
|
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
|
||||||
|
std::string inherits_value = option_str->value;
|
||||||
|
// Orca: try to find if the parent preset has been renamed
|
||||||
|
inherit_preset = this->find_preset2(inherits_value);
|
||||||
|
Preset::normalize_inherits(config, inherit_preset);
|
||||||
|
} else {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
const Preset& default_preset = this->default_preset_for(config);
|
||||||
|
if (inherit_preset) {
|
||||||
|
preset.config = inherit_preset->config;
|
||||||
|
preset.filament_id = inherit_preset->filament_id;
|
||||||
|
extend_default_config_length(config, false, {});
|
||||||
|
preset.config.update_diff_values_to_child_config(config, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
|
||||||
|
if ((inherits_config2 && !inherits_config2->value.empty())) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent %1% for config %2%!")%inherits_config2->value %preset.file;
|
||||||
|
++m_errors;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// We support custom root preset now
|
||||||
|
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
|
||||||
|
preset.config = default_preset.config;
|
||||||
|
preset.config.apply(std::move(config));
|
||||||
|
extend_default_config_length(preset.config, true, default_preset.config);
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load preset: " << name << " and filament_id: " << preset.filament_id << " and base_id: " << preset.base_id;
|
||||||
|
|
||||||
|
Preset::normalize(preset.config);
|
||||||
|
// Report configuration fields, which are misplaced into a wrong group.
|
||||||
|
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
|
||||||
|
if (!incorrect_keys.empty()) {
|
||||||
|
++m_errors;
|
||||||
|
BOOST_LOG_TRIVIAL(error)
|
||||||
|
<< "Error in a preset file: The preset \"" << preset.file
|
||||||
|
<< "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset.type == Preset::TYPE_FILAMENT && preset.is_user() && preset.inherits().empty()) {
|
||||||
|
auto compatible_printers = dynamic_cast<ConfigOptionStrings *>(preset.config.option("compatible_printers", true));
|
||||||
|
if (compatible_printers && compatible_printers->values.empty()) {
|
||||||
|
size_t at_pos = name.find('@');
|
||||||
|
if (at_pos != std::string::npos && at_pos + 1 < name.length()) {
|
||||||
|
compatible_printers->values.push_back(name.substr(at_pos + 1));
|
||||||
|
if (!read_only)
|
||||||
|
preset.save(nullptr);
|
||||||
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " added compatible_printers for preset: " << name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preset.loaded = true;
|
||||||
|
//BBS: add some workaround for previous incorrect settings
|
||||||
|
if ((!preset.setting_id.empty())&&(preset.setting_id == preset.base_id))
|
||||||
|
preset.setting_id.clear();
|
||||||
|
//BBS: add config related logs
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
|
||||||
|
// add alias for custom filament preset
|
||||||
|
set_custom_preset_alias(preset);
|
||||||
|
} catch (const std::ifstream::failure &err) {
|
||||||
|
++m_errors;
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("The user-config cannot be loaded: %1%. Reason: %2%")%preset.file %err.what();
|
||||||
|
fs::path file_path(preset.file);
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
file_path.replace_extension(".info");
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
//throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
|
||||||
|
} catch (const std::runtime_error &err) {
|
||||||
|
++m_errors;
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading the user-config file: %1%. Reason: %2%")%preset.file %err.what();
|
||||||
|
//throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
|
||||||
|
fs::path file_path(preset.file);
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
file_path.replace_extension(".info");
|
||||||
|
if (!read_only && fs::exists(file_path))
|
||||||
|
fs::remove(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset_loaded_fn != nullptr)
|
||||||
|
preset_loaded_fn(preset);
|
||||||
|
|
||||||
|
presets_loaded.emplace_back(preset);
|
||||||
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " load config successful and preset name is:" << preset.name;
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
errors_cummulative += "\n";
|
errors_cummulative += "\n";
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
if (presets_loaded.size() > 0)
|
if (presets_loaded.size() > 0)
|
||||||
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
|
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
|
||||||
sort_presets();
|
sort_presets();
|
||||||
@@ -2671,6 +2596,20 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
|
|||||||
const Semver file_version,
|
const Semver file_version,
|
||||||
const std::string filament_id)
|
const std::string filament_id)
|
||||||
{
|
{
|
||||||
|
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
|
||||||
|
DynamicPrintConfig cfg(this->default_preset_for(combined_config).config);
|
||||||
|
// SoftFever: ignore print connection info from project
|
||||||
|
auto keys = cfg.keys();
|
||||||
|
keys.erase(std::remove_if(keys.begin(), keys.end(),
|
||||||
|
[](std::string &val) {
|
||||||
|
return val == "print_host" || val == "print_host_webui" || val == "printhost_apikey" ||
|
||||||
|
val == "printhost_cafile" || val == "printhost_user" || val == "printhost_password" || val == "printhost_port";
|
||||||
|
}),
|
||||||
|
keys.end());
|
||||||
|
cfg.apply_only(combined_config, keys, true);
|
||||||
|
std::string &inherits = Preset::inherits(cfg);
|
||||||
|
|
||||||
|
//add different settings check logic, replace the old system preset's default value with new system preset's default values
|
||||||
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
|
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
|
||||||
bool found = it != m_presets.end() && it->name == original_name;
|
bool found = it != m_presets.end() && it->name == original_name;
|
||||||
if (! found) {
|
if (! found) {
|
||||||
@@ -2679,25 +2618,24 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
|
|||||||
found = it != m_presets.end();
|
found = it != m_presets.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
t_config_option_keys keys;
|
std::string extruder_id_name, extruder_variant_name;
|
||||||
DynamicPrintConfig cfg = Preset::load_external_config(m_type, this->default_preset_for(combined_config).config, combined_config, different_settings_list,
|
std::set<std::string> *key_set1 = nullptr, *key_set2 = nullptr;
|
||||||
[this, &original_name, &path, found, it](const std::string &inherits) -> DynamicPrintConfig * {
|
Preset::get_extruder_names_and_keysets(m_type, extruder_id_name, extruder_variant_name, &key_set1, &key_set2);
|
||||||
if (!inherits.empty()) {
|
|
||||||
auto iter = this->find_preset_internal(inherits);
|
if (!inherits.empty() && (different_settings_list.size() > 0)) {
|
||||||
if (iter == m_presets.end() || iter->name != inherits)
|
auto iter = this->find_preset_internal(inherits);
|
||||||
iter = this->find_preset_renamed(inherits);
|
if (iter == m_presets.end() || iter->name != inherits)
|
||||||
if (iter == m_presets.end())
|
iter = this->find_preset_renamed(inherits);
|
||||||
return nullptr;
|
if (iter != m_presets.end()) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "load_external_preset" << boost::format(": change preset %1% inherit %2% 's value to %3% 's values")%original_name %inherits %path;
|
//std::vector<std::string> dirty_options = cfg.diff(iter->config);
|
||||||
return &iter->config;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": change preset %1% inherit %2% 's value to %3% 's values")%original_name %inherits %path;
|
||||||
}
|
cfg.update_non_diff_values_to_base_config(iter->config, keys, different_settings_list, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
||||||
if (found && it->is_system) {
|
}
|
||||||
BOOST_LOG_TRIVIAL(info) << "load_external_preset" << boost::format(": change preset %1% 's value to %2% 's values")%original_name %path;
|
}
|
||||||
return &it->config;
|
else if (found && it->is_system && (different_settings_list.size() > 0)) {
|
||||||
}
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": change preset %1% 's value to %2% 's values")%original_name %path;
|
||||||
return nullptr;
|
cfg.update_non_diff_values_to_base_config(it->config, keys, different_settings_list, extruder_id_name, extruder_variant_name, *key_set1, *key_set2);
|
||||||
}, &keys);
|
}
|
||||||
std::string &inherits = Preset::inherits(cfg);
|
|
||||||
|
|
||||||
//BBS: add config related logs
|
//BBS: add config related logs
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, type %1% , path %2%, name %3%, original_name %4%, inherits %5%")%Preset::get_type_string(m_type) %path %name %original_name %inherits;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, type %1% , path %2%, name %3%, original_name %4%, inherits %5%")%Preset::get_type_string(m_type) %path %name %original_name %inherits;
|
||||||
@@ -2889,18 +2827,6 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
|
|||||||
return std::make_pair(&preset, false);
|
return std::make_pair(&preset, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Preset& PresetCollection::append_preset(std::string &&path, const std::string &name, DynamicPrintConfig &&config)
|
|
||||||
{
|
|
||||||
lock();
|
|
||||||
Preset &preset = m_presets.emplace_back(m_type, name, false);
|
|
||||||
preset.file = std::move(path);
|
|
||||||
preset.config = std::move(config);
|
|
||||||
preset.loaded = true;
|
|
||||||
preset.is_dirty = false;
|
|
||||||
unlock();
|
|
||||||
return preset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select, Semver file_version)
|
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select, Semver file_version)
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
@@ -3920,53 +3846,28 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<std::string>> PresetCollection::merge_presets(const std::vector<PresetCollection*> &others, const VendorMap &new_vendors)
|
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||||
|
std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const VendorMap &new_vendors)
|
||||||
{
|
{
|
||||||
auto less = [this](const Preset &a, const Preset &b) {
|
std::vector<std::string> duplicates;
|
||||||
return m_type == Preset::TYPE_FILAMENT ? filament_preset_less(a, b) : a < b;
|
for (Preset &preset : other.m_presets) {
|
||||||
};
|
if (preset.is_default || preset.is_external)
|
||||||
struct Incoming { Preset *preset; size_t source; };
|
continue;
|
||||||
auto incoming_less = [&less](const Incoming &a, const Incoming &b) { return less(*a.preset, *b.preset); };
|
Preset key(m_type, preset.name);
|
||||||
// Each of `others` is sorted, so its presets form one sorted run.
|
auto it = (m_type == Preset::TYPE_FILAMENT)
|
||||||
std::vector<Incoming> incoming;
|
? std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key, filament_preset_less)
|
||||||
std::vector<size_t> run_ends { 0 };
|
: std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
|
||||||
for (size_t source = 0; source < others.size(); ++ source) {
|
if (it == m_presets.end() || it->name != preset.name) {
|
||||||
for (Preset &preset : others[source]->m_presets)
|
|
||||||
if (! preset.is_default && ! preset.is_external)
|
|
||||||
incoming.push_back({ &preset, source });
|
|
||||||
assert(std::is_sorted(incoming.begin() + run_ends.back(), incoming.end(), incoming_less));
|
|
||||||
run_ends.push_back(incoming.size());
|
|
||||||
}
|
|
||||||
// Merged pairwise and stably, so equal names stay in the order of `others`.
|
|
||||||
const size_t runs = others.size();
|
|
||||||
for (size_t width = 1; width < runs; width *= 2)
|
|
||||||
for (size_t i = 0; i + width < runs; i += 2 * width)
|
|
||||||
std::inplace_merge(incoming.begin() + run_ends[i], incoming.begin() + run_ends[i + width],
|
|
||||||
incoming.begin() + run_ends[std::min(i + 2 * width, runs)], incoming_less);
|
|
||||||
|
|
||||||
std::vector<std::vector<std::string>> duplicates(others.size());
|
|
||||||
std::deque<Preset> merged;
|
|
||||||
auto own = m_presets.begin() + m_num_default_presets;
|
|
||||||
std::move(m_presets.begin(), own, std::back_inserter(merged));
|
|
||||||
// On equal names this collection's preset is kept, else the earliest of `others`,
|
|
||||||
// and each repeat is listed under the collection it came from.
|
|
||||||
for (auto next = incoming.begin(); own != m_presets.end() || next != incoming.end();) {
|
|
||||||
if (next == incoming.end() || (own != m_presets.end() && ! less(*next->preset, *own)))
|
|
||||||
merged.emplace_back(std::move(*own ++));
|
|
||||||
else {
|
|
||||||
Preset &preset = *(next ++)->preset;
|
|
||||||
if (preset.vendor != nullptr) {
|
if (preset.vendor != nullptr) {
|
||||||
// Re-assign a pointer to the vendor structure in the new PresetBundle.
|
// Re-assign a pointer to the vendor structure in the new PresetBundle.
|
||||||
auto it = new_vendors.find(preset.vendor->id);
|
auto it = new_vendors.find(preset.vendor->id);
|
||||||
assert(it != new_vendors.end());
|
assert(it != new_vendors.end());
|
||||||
preset.vendor = &it->second;
|
preset.vendor = &it->second;
|
||||||
}
|
}
|
||||||
merged.emplace_back(std::move(preset));
|
m_presets.emplace(it, std::move(preset));
|
||||||
}
|
} else
|
||||||
for (; next != incoming.end() && next->preset->name == merged.back().name; ++ next)
|
duplicates.emplace_back(std::move(preset.name));
|
||||||
duplicates[next->source].emplace_back(next->preset->name);
|
|
||||||
}
|
}
|
||||||
m_presets = std::move(merged);
|
|
||||||
return duplicates;
|
return duplicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -403,15 +403,6 @@ public:
|
|||||||
std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type
|
std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type
|
||||||
|
|
||||||
static void get_extruder_names_and_keysets(Type type, std::string& extruder_id_name, std::string& extruder_variant_name, std::set<std::string>** p_key_set1, std::set<std::string>** p_key_set2);
|
static void get_extruder_names_and_keysets(Type type, std::string& extruder_id_name, std::string& extruder_variant_name, std::set<std::string>** p_key_set1, std::set<std::string>** p_key_set2);
|
||||||
// Config of a preset loaded from a project or config file: the project's values over the type's
|
|
||||||
// default preset config, without the print-host keys. When different_settings_list is not empty,
|
|
||||||
// every key not listed in it is then refreshed from the base system preset, which find_base returns
|
|
||||||
// for the project's "inherits" (nullptr when there is none), with the listed per-variant values
|
|
||||||
// mapped onto the base's extruder variants. keys, if given, receives the keys taken from the project.
|
|
||||||
static DynamicPrintConfig load_external_config(Type type, const DynamicPrintConfig &default_config, const DynamicPrintConfig &project_config,
|
|
||||||
const std::set<std::string> &different_settings_list,
|
|
||||||
const std::function<DynamicPrintConfig *(const std::string &inherits)> &find_base,
|
|
||||||
t_config_option_keys *keys = nullptr);
|
|
||||||
std::string get_printer_id() const { return vendor ? vendor->id : ""; }
|
std::string get_printer_id() const { return vendor ? vendor->id : ""; }
|
||||||
|
|
||||||
bool has_lidar(PresetBundle *preset_bundle);
|
bool has_lidar(PresetBundle *preset_bundle);
|
||||||
@@ -454,10 +445,7 @@ public:
|
|||||||
static std::string remove_suffix_modified(const std::string& name);
|
static std::string remove_suffix_modified(const std::string& name);
|
||||||
static void normalize(DynamicPrintConfig &config);
|
static void normalize(DynamicPrintConfig &config);
|
||||||
// Report configuration fields, which are misplaced into a wrong group, remove them from the config.
|
// Report configuration fields, which are misplaced into a wrong group, remove them from the config.
|
||||||
// `added`, when given, is the diff applied over a copy of default_config, and only
|
static std::string remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config);
|
||||||
// its keys are checked, since no other key can be missing from default_config.
|
|
||||||
static std::string remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config,
|
|
||||||
const DynamicPrintConfig *added = nullptr);
|
|
||||||
|
|
||||||
// BBS: move constructor to public
|
// BBS: move constructor to public
|
||||||
Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
|
Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
|
||||||
@@ -813,8 +801,6 @@ public:
|
|||||||
// Return number of presets including the "- default -" preset.
|
// Return number of presets including the "- default -" preset.
|
||||||
size_t size() const { return m_presets.size(); }
|
size_t size() const { return m_presets.size(); }
|
||||||
bool has_defaults_only() const { return m_presets.size() <= m_num_default_presets; }
|
bool has_defaults_only() const { return m_presets.size() <= m_num_default_presets; }
|
||||||
// How many presets this collection refused or repaired while loading.
|
|
||||||
int error_count() const { return m_errors; }
|
|
||||||
|
|
||||||
// For Print / Filament presets, disable those, which are not compatible with the printer.
|
// For Print / Filament presets, disable those, which are not compatible with the printer.
|
||||||
template<typename PreferedCondition>
|
template<typename PreferedCondition>
|
||||||
@@ -888,10 +874,8 @@ protected:
|
|||||||
// This is a temporary state, which shall be fixed immediately by the following step.
|
// This is a temporary state, which shall be fixed immediately by the following step.
|
||||||
bool select_preset_by_name_strict(const std::string &name);
|
bool select_preset_by_name_strict(const std::string &name);
|
||||||
|
|
||||||
// Move the presets of `others` into this collection in one pass. A name this
|
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||||
// collection or an earlier one of `others` already has is left out, and reported
|
std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
|
||||||
// in the list of the collection that repeats it.
|
|
||||||
std::vector<std::vector<std::string>> merge_presets(const std::vector<PresetCollection*> &others, const VendorMap &new_vendors);
|
|
||||||
|
|
||||||
// Update m_map_alias_to_profile_name from loaded system profiles.
|
// Update m_map_alias_to_profile_name from loaded system profiles.
|
||||||
void update_map_alias_to_profile_name();
|
void update_map_alias_to_profile_name();
|
||||||
@@ -906,43 +890,6 @@ protected:
|
|||||||
void set_custom_preset_alias(Preset &preset);
|
void set_custom_preset_alias(Preset &preset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// One preset file read and flattened against the presets already in this
|
|
||||||
// collection, before anything the collection shares has been touched.
|
|
||||||
struct UserPresetLoad
|
|
||||||
{
|
|
||||||
Preset preset;
|
|
||||||
// Joins the collection. A file that threw partway still joins it, without
|
|
||||||
// the steps that did not run.
|
|
||||||
bool install { false };
|
|
||||||
// The whole of the load ran, so the preset is ready to be aliased.
|
|
||||||
bool complete { false };
|
|
||||||
// A filament preset that named no compatible printer and was given one from
|
|
||||||
// its name, which commit writes back to its file.
|
|
||||||
bool save_compatible_printers { false };
|
|
||||||
// Unreadable, so commit removes it and its .info file.
|
|
||||||
bool discard_file { false };
|
|
||||||
// The .info file read beside the preset, which commit logs.
|
|
||||||
std::string info_file;
|
|
||||||
// Counted and logged by commit, in the order the directory listed the files.
|
|
||||||
std::vector<std::string> errors;
|
|
||||||
PresetsConfigSubstitutions substitutions;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read and flatten one preset file. It reads only, and resolves against the presets
|
|
||||||
// loaded before this pass, never another file of the same pass, so the files of a
|
|
||||||
// pass are independent of each other.
|
|
||||||
UserPresetLoad resolve_user_preset(const boost::filesystem::path &file, const std::string &canonical_name,
|
|
||||||
const PresetOrigin &load_origin, ForwardCompatibilitySubstitutionRule substitution_rule,
|
|
||||||
const std::string &extruder_id_name, const std::string &extruder_variant_name,
|
|
||||||
std::set<std::string> *key_set1, std::set<std::string> *key_set2) const;
|
|
||||||
|
|
||||||
// Install one resolved preset. The collection, its alias maps, the error count
|
|
||||||
// and the preset files on disk are touched here and only here.
|
|
||||||
void commit_user_preset(UserPresetLoad &&loaded, std::deque<Preset> &presets_loaded,
|
|
||||||
PresetsConfigSubstitutions &substitutions,
|
|
||||||
const std::function<void(Preset&)> &preset_loaded_fn,
|
|
||||||
bool read_only);
|
|
||||||
|
|
||||||
std::string canonical_preset_name(const std::string &name, const PresetOrigin &load_origin = PresetOrigin()) const;
|
std::string canonical_preset_name(const std::string &name, const PresetOrigin &load_origin = PresetOrigin()) const;
|
||||||
|
|
||||||
// Comparator that sorts "Generic " prefixed presets before others, then alphabetically within each group.
|
// Comparator that sorts "Generic " prefixed presets before others, then alphabetically within each group.
|
||||||
@@ -954,10 +901,6 @@ private:
|
|||||||
return a.name < b.name;
|
return a.name < b.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a preset without keeping the collection sorted, for a caller installing
|
|
||||||
// many at once; find_preset() is unusable until sort_presets() runs.
|
|
||||||
Preset& append_preset(std::string &&path, const std::string &name, DynamicPrintConfig &&config);
|
|
||||||
|
|
||||||
// Sort presets: filament presets use generic-first ordering, others sort alphabetically.
|
// Sort presets: filament presets use generic-first ordering, others sort alphabetically.
|
||||||
void sort_presets() {
|
void sort_presets() {
|
||||||
if (m_type == Preset::TYPE_FILAMENT)
|
if (m_type == Preset::TYPE_FILAMENT)
|
||||||
|
|||||||
+431
-616
File diff suppressed because it is too large
Load Diff
+29
-182
@@ -15,7 +15,6 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
@@ -235,9 +234,6 @@ public:
|
|||||||
|
|
||||||
// ORCA: utility function to find the vendor for a given preset name
|
// ORCA: utility function to find the vendor for a given preset name
|
||||||
static std::string find_preset_vendor(const std::string& preset_name, Preset::Type type);
|
static std::string find_preset_vendor(const std::string& preset_name, Preset::Type type);
|
||||||
// Keys a project keeps when its presets are loaded: those listed in its escaped
|
|
||||||
// "different_settings_to_system" entry for the preset, plus the preset bookkeeping keys.
|
|
||||||
static std::set<std::string> project_different_keys(const std::string &different_settings);
|
|
||||||
|
|
||||||
PresetBundle();
|
PresetBundle();
|
||||||
PresetBundle(const PresetBundle &rhs);
|
PresetBundle(const PresetBundle &rhs);
|
||||||
@@ -277,10 +273,6 @@ public:
|
|||||||
const std::string &source_file,
|
const std::string &source_file,
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
||||||
std::string &error, bool allow_source_manifest = true);
|
std::string &error, bool allow_source_manifest = true);
|
||||||
// Resolve a system preset by name. The vendor tree is read from data_dir()/system when installed
|
|
||||||
// there, as the GUI reads it, and from the bundled profiles otherwise.
|
|
||||||
bool resolve_system_preset(DynamicPrintConfig &config, Preset::Type type, const std::string &name,
|
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule, std::string &error);
|
|
||||||
|
|
||||||
// Load selections (current print, current filaments, current printer) from config.ini
|
// Load selections (current print, current filaments, current printer) from config.ini
|
||||||
// This is done just once on application start up.
|
// This is done just once on application start up.
|
||||||
@@ -630,180 +622,36 @@ public:
|
|||||||
// default_filament_profile must resolve to a system filament.
|
// default_filament_profile must resolve to a system filament.
|
||||||
bool check_printer_default_materials() const;
|
bool check_printer_default_materials() const;
|
||||||
|
|
||||||
// One vendor to load, and the directory it is installed in.
|
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||||
struct VendorSource
|
// Public so per-vendor-cache consumers (e.g. the setup wizard) can assemble a
|
||||||
{
|
// bundle out of several per-vendor caches loaded into separate PresetBundle instances.
|
||||||
std::string name;
|
std::vector<std::string> merge_presets(PresetBundle &&other);
|
||||||
boost::filesystem::path dir;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load `vendors` into this bundle, the Orca filament library directly and every
|
|
||||||
// other vendor in parallel into a bundle of its own that inherits from it, merged
|
|
||||||
// in the order given. A vendor that cannot be loaded has its error added to the
|
|
||||||
// returned text, or thrown in validation mode, and its name to `failed`; it is
|
|
||||||
// left out, except for the library, which keeps what it installed before the
|
|
||||||
// failure. Once `cancel` is set, no further vendor starts loading.
|
|
||||||
std::pair<PresetsConfigSubstitutions, std::string> load_vendors(const std::vector<VendorSource>& vendors,
|
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule, bool allow_cache,
|
|
||||||
const std::atomic<bool>* cancel = nullptr, std::vector<std::string>* failed = nullptr);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Move the presets and vendor profiles of `others` into this bundle, in one pass
|
// Load one vendor from the preset cache installed in `dir`, judged against
|
||||||
// over each collection. A preset whose name this bundle or an earlier one of
|
// the vendor profile there. False, with this bundle left clean, when there
|
||||||
// `others` already has is left out and listed under the bundle that repeats it.
|
// is no usable cache and the vendor has to be parsed. This is how
|
||||||
std::vector<std::vector<std::string>> merge_presets(const std::vector<PresetBundle*> &others);
|
// load_vendor_configs_from_json reads a cache.
|
||||||
|
bool load_vendor_cache(const boost::filesystem::path& dir, const std::string& vendor_name, const PresetBundle* base_bundle);
|
||||||
|
|
||||||
// What parsing one entry's JSON sub-file reported. Its errors and warnings are
|
// Load one source-form preset entry into this bundle: resolve `inherits`
|
||||||
// logged when the entry installs, so they come out in listing order with the
|
// and `include`, flatten, validate and register the preset. Returns the
|
||||||
// entry's install errors, as parsing and installing one entry at a time leaves them.
|
// reason loading failed, empty on success. See the definition for the
|
||||||
struct EntryParse
|
// sharing contract between the JSON parse and the cache load.
|
||||||
{
|
// retain_configs / retain_includes, when non-null, name the only presets
|
||||||
ConfigSubstitutions substitutions;
|
// registered into config_maps / include_maps (a config copy each). The
|
||||||
// Counted in the bundle's error count.
|
// cache load passes the names its entries inherit / include — the only
|
||||||
std::vector<std::string> errors;
|
// ones ever looked up again; the JSON parse retains all, not knowing what
|
||||||
std::vector<std::string> warnings;
|
// later subfiles name.
|
||||||
};
|
std::string load_vendor_preset(const CachedPreset& entry,
|
||||||
|
const std::string& path, const std::string& vendor_name,
|
||||||
// An EntryParse for each entry of the VendorCacheData list of the same name.
|
const PresetBundle* base_bundle,
|
||||||
struct VendorParse
|
LoadConfigBundleAttributes flags,
|
||||||
{
|
ConfigSubstitutionContext& substitution_context, PresetsConfigSubstitutions& substitutions,
|
||||||
std::vector<EntryParse> process_entries;
|
std::map<std::string, DynamicPrintConfig>& config_maps, std::map<std::string, DynamicPrintConfig>& include_maps,
|
||||||
std::vector<EntryParse> filament_entries;
|
std::map<std::string, std::string>& filament_id_maps,
|
||||||
std::vector<EntryParse> machine_entries;
|
PresetCollection* presets_collection, size_t& count, bool is_from_lib,
|
||||||
};
|
const std::set<std::string>* retain_configs = nullptr, const std::set<std::string>* retain_includes = nullptr);
|
||||||
|
|
||||||
// One vendor read from its cache or its JSONs by read_vendor, for
|
|
||||||
// install_vendor_read to install.
|
|
||||||
struct VendorRead
|
|
||||||
{
|
|
||||||
std::string dir;
|
|
||||||
std::string vendor_name;
|
|
||||||
LoadConfigBundleAttributes flags;
|
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule;
|
|
||||||
// The errors this bundle had counted before the read, which the cache stamp leaves out.
|
|
||||||
int errors_at_entry { 0 };
|
|
||||||
// A whole-vendor load, which can read a cache and write one.
|
|
||||||
bool cacheable { false };
|
|
||||||
// Read from the cache at cache_path, which install can still reject.
|
|
||||||
bool from_cache { false };
|
|
||||||
std::string cache_path;
|
|
||||||
// Only the vendor profile was asked for.
|
|
||||||
bool vendor_only { false };
|
|
||||||
VendorCacheData data;
|
|
||||||
// What each entry's JSON parse reported, and whether and with which version
|
|
||||||
// the cache is written once the entries install.
|
|
||||||
VendorParse parsed;
|
|
||||||
bool will_cache { false };
|
|
||||||
std::string version;
|
|
||||||
// The sub-file the parse stopped at, its kind, why, and the errors it reported.
|
|
||||||
std::string reason;
|
|
||||||
std::string failed_subfile;
|
|
||||||
const char* failed_kind { nullptr };
|
|
||||||
std::vector<std::string> failed_errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read a vendor into this bundle's vendor profiles and `data`, from its cache
|
|
||||||
// when one covers it, else from its JSONs; nothing is installed. Throws
|
|
||||||
// ConfigurationError when the vendor's own JSON cannot be parsed.
|
|
||||||
VendorRead read_vendor(const std::string& dir, const std::string& vendor_name, LoadConfigBundleAttributes flags,
|
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule, bool allow_cache);
|
|
||||||
|
|
||||||
// Parse the vendor's JSONs into `read`, up to the first sub-file that fails.
|
|
||||||
void parse_vendor_json(VendorRead& read);
|
|
||||||
|
|
||||||
// Install what read_vendor read, against base_bundle's filament library. A cache
|
|
||||||
// that cannot be installed is replaced by a parse of the JSONs. Throws
|
|
||||||
// ConfigurationError at the first entry that cannot be installed, or after
|
|
||||||
// installing the entries before a sub-file that could not be parsed.
|
|
||||||
std::pair<PresetsConfigSubstitutions, size_t> install_vendor_read(VendorRead&& read, const PresetBundle* base_bundle);
|
|
||||||
|
|
||||||
// Install a cache's entries. False, with this bundle left clean, when one of them
|
|
||||||
// cannot be installed.
|
|
||||||
bool install_vendor_cache(const std::string& cache_path, const std::string& vendor_name, VendorCacheData&& data,
|
|
||||||
const PresetBundle* base_bundle);
|
|
||||||
|
|
||||||
// Log and count errors reported by a resolve or a parse.
|
|
||||||
void log_errors(const std::vector<std::string>& errors);
|
|
||||||
|
|
||||||
// The state of installing one collection of one vendor, which
|
|
||||||
// resolve_vendor_preset reads through a const reference and only
|
|
||||||
// commit_vendor_preset writes.
|
|
||||||
struct VendorInstall
|
|
||||||
{
|
|
||||||
// The directory holding <vendor_name>/, whose sub-paths the entries name.
|
|
||||||
std::string path;
|
|
||||||
std::string vendor_name;
|
|
||||||
const VendorProfile* vendor_profile;
|
|
||||||
const PresetBundle* base_bundle;
|
|
||||||
LoadConfigBundleAttributes flags;
|
|
||||||
PresetCollection* presets;
|
|
||||||
// The Orca filament library, which keeps every config for other vendors to
|
|
||||||
// resolve against.
|
|
||||||
bool is_from_lib;
|
|
||||||
PresetsConfigSubstitutions* substitutions;
|
|
||||||
// The names some entry inherits / includes, the only ones whose configs /
|
|
||||||
// include diffs are looked up again.
|
|
||||||
std::set<std::string> inherited;
|
|
||||||
std::set<std::string> included;
|
|
||||||
std::map<std::string, DynamicPrintConfig> config_maps;
|
|
||||||
std::map<std::string, DynamicPrintConfig> include_maps;
|
|
||||||
std::map<std::string, std::string> filament_id_maps;
|
|
||||||
std::unordered_set<std::string> installed_names;
|
|
||||||
size_t count { 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Install a vendor's source-form entries, parsed from its JSON or read from its
|
|
||||||
// cache: processes, then filaments, then printers. Both loads go through here,
|
|
||||||
// so a cache-loaded bundle cannot come out different from a JSON-loaded one.
|
|
||||||
// `parsed` is given for entries parsed just now. `complete` says the entries
|
|
||||||
// are the vendor's whole lists; only then are the filament library's configs
|
|
||||||
// and filament ids left in m_config_maps and m_filament_id_maps. Returns the
|
|
||||||
// number of presets installed, and throws ConfigurationError at the first
|
|
||||||
// entry that cannot be installed.
|
|
||||||
size_t install_vendor(const std::string& path, const std::string& vendor_name, const PresetBundle* base_bundle,
|
|
||||||
LoadConfigBundleAttributes flags, const VendorCacheData& entries,
|
|
||||||
VendorParse* parsed, bool complete, PresetsConfigSubstitutions& substitutions);
|
|
||||||
|
|
||||||
// Install one collection's entries in the order they are listed.
|
|
||||||
void install_vendor_entries(VendorInstall& install, const std::vector<CachedPreset>& entries,
|
|
||||||
std::vector<EntryParse>* parsed);
|
|
||||||
|
|
||||||
// One entry flattened against the preset it inherits, before anything this
|
|
||||||
// bundle shares has been touched.
|
|
||||||
struct PresetInstall
|
|
||||||
{
|
|
||||||
DynamicPrintConfig config;
|
|
||||||
std::string file_path;
|
|
||||||
// Empty when the preset is its own alias.
|
|
||||||
std::string alias;
|
|
||||||
std::string filament_id;
|
|
||||||
std::vector<std::string> renamed_from;
|
|
||||||
// Reported by commit, so resolving entries together leaves the log and
|
|
||||||
// the error count as one entry at a time produces them.
|
|
||||||
std::vector<std::string> errors;
|
|
||||||
// What a base states for the presets that include it, when it is retained.
|
|
||||||
std::optional<DynamicPrintConfig> included;
|
|
||||||
// The config kept for the entries that inherit this one, or for other
|
|
||||||
// vendors when this is the filament library.
|
|
||||||
std::optional<DynamicPrintConfig> retained;
|
|
||||||
// Not instantiated, so it contributes a config and no preset.
|
|
||||||
bool config_only { false };
|
|
||||||
// Non-empty when the entry is rejected, and says why.
|
|
||||||
std::string reason;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Flatten one entry against the config it inherits, from this collection's
|
|
||||||
// config_maps or base_bundle's filament library, with the include diffs it
|
|
||||||
// names layered in. It looks up nothing but the names the entry inherits and
|
|
||||||
// includes, and writes nothing.
|
|
||||||
PresetInstall resolve_vendor_preset(const CachedPreset& entry, const VendorInstall& install) const;
|
|
||||||
|
|
||||||
// Install a resolved entry. The collections, the maps in `install` and the
|
|
||||||
// error count are touched here and only here, one entry at a time.
|
|
||||||
// Presets are appended, so a repeated name is caught with `installed_names`,
|
|
||||||
// and the collection is sorted once every entry is in.
|
|
||||||
std::string commit_vendor_preset(const CachedPreset& entry, PresetInstall&& resolved,
|
|
||||||
ConfigSubstitutions&& substitutions, VendorInstall& install);
|
|
||||||
|
|
||||||
// Clear every collection's m_printer_hold_alias, which reset() leaves alone.
|
// Clear every collection's m_printer_hold_alias, which reset() leaves alone.
|
||||||
void clear_printer_hold_aliases();
|
void clear_printer_hold_aliases();
|
||||||
@@ -815,14 +663,13 @@ private:
|
|||||||
// Vendor trees loaded by resolve_preset_config's manifest path, so every preset
|
// Vendor trees loaded by resolve_preset_config's manifest path, so every preset
|
||||||
// resolved through this bundle shares one load per source root and vendor. The
|
// resolved through this bundle shares one load per source root and vendor. The
|
||||||
// filament library is one such tree, shared by every vendor under its root.
|
// filament library is one such tree, shared by every vendor under its root.
|
||||||
// A tree read from its preset cache is kept apart: its presets carry no source file.
|
std::map<std::tuple<std::string, std::string, ForwardCompatibilitySubstitutionRule>, std::unique_ptr<PresetBundle>>
|
||||||
std::map<std::tuple<std::string, std::string, ForwardCompatibilitySubstitutionRule, bool>, std::unique_ptr<PresetBundle>>
|
|
||||||
m_source_vendor_bundles;
|
m_source_vendor_bundles;
|
||||||
|
|
||||||
const PresetBundle *load_source_vendor(const boost::filesystem::path &root_dir,
|
const PresetBundle *load_source_vendor(const boost::filesystem::path &root_dir,
|
||||||
const std::string &vendor_id,
|
const std::string &vendor_id,
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
||||||
std::string &error, bool allow_cache = false);
|
std::string &error);
|
||||||
|
|
||||||
// Orca: validation only - flag any printer with two or more compatible
|
// Orca: validation only - flag any printer with two or more compatible
|
||||||
// filament presets sharing one filament_id (ambiguous AMS subtype match).
|
// filament presets sharing one filament_id (ambiguous AMS subtype match).
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ void skip_config(cereal::BinaryInputArchive& ar, const CacheDictionary& dict);
|
|||||||
|
|
||||||
// One preset as its JSON subfile states it: the config diff, the names of the
|
// One preset as its JSON subfile states it: the config diff, the names of the
|
||||||
// preset it inherits and the presets it includes, and the parse metadata —
|
// preset it inherits and the presets it includes, and the parse metadata —
|
||||||
// everything PresetBundle::parse_vendor_json extracts and
|
// everything the parse phase of load_vendor_configs_from_json extracts and
|
||||||
// nothing it derives. Inheritance and includes are resolved when the entry is
|
// nothing it derives. Inheritance and includes are resolved when the entry is
|
||||||
// installed, against whatever filament library is loaded then, so a cache
|
// installed, against whatever filament library is loaded then, so a cache
|
||||||
// carries no other vendor's values and no other vendor's update can make it
|
// carries no other vendor's values and no other vendor's update can make it
|
||||||
|
|||||||
+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
|
||||||
@@ -12595,7 +12565,6 @@ OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef()
|
|||||||
|
|
||||||
new_def("initial_no_support_extruder", coInt, "Initial no support extruder", "Zero-based index of the first extruder used for printing without support. Same as initial_no_support_tool.");
|
new_def("initial_no_support_extruder", coInt, "Initial no support extruder", "Zero-based index of the first extruder used for printing without support. Same as initial_no_support_tool.");
|
||||||
new_def("in_head_wrap_detect_zone", coBool, "In head wrap detect zone", "Indicates if the first layer overlaps with the head wrap zone.");
|
new_def("in_head_wrap_detect_zone", coBool, "In head wrap detect zone", "Indicates if the first layer overlaps with the head wrap zone.");
|
||||||
new_def("curr_bed_type", coString, "Current bed type", "Name of the currently selected bed plate type (e.g. 'Textured PEI Plate', 'Smooth High Temp Plate').");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintStatisticsConfigDef::PrintStatisticsConfigDef()
|
PrintStatisticsConfigDef::PrintStatisticsConfigDef()
|
||||||
|
|||||||
@@ -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)
|
||||||
find_package(X11 REQUIRED)
|
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)
|
||||||
|
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.
|
||||||
|
|||||||
@@ -389,6 +389,7 @@ public:
|
|||||||
|
|
||||||
std::vector<GUI::GLModel> model_per_colors;
|
std::vector<GUI::GLModel> model_per_colors;
|
||||||
bool IsTransparent();
|
bool IsTransparent();
|
||||||
|
const std::vector<ColorRGBA>& colors() const { return m_colors; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ColorRGBA> m_colors;
|
std::vector<ColorRGBA> m_colors;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1698,6 +1705,10 @@ void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
|||||||
{
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
render_shells(canvas_width, canvas_height);
|
render_shells(canvas_width, canvas_height);
|
||||||
|
// 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);
|
||||||
|
|
||||||
if (m_viewer.get_extrusion_roles_count() == 0)
|
if (m_viewer.get_extrusion_roles_count() == 0)
|
||||||
return;
|
return;
|
||||||
@@ -2020,6 +2031,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 +2405,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 +2518,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 +2757,139 @@ 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;
|
||||||
|
|
||||||
|
// The range's end layers are drawn as toolpaths, so the solid runs from the top of the bottom
|
||||||
|
// one to the bottom of the top one. A face of the model in either layer's plane would otherwise
|
||||||
|
// fight the toolpaths for the same pixels and flicker while dragging.
|
||||||
|
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
|
||||||
|
const float z_top = (layers[1] > 0) ? m_viewer.get_layer_z(layers[1] - 1) - m_z_offset - 0.001f : -FLT_MAX;
|
||||||
|
const float z_bottom = m_viewer.get_layer_z(layers[0]) - m_z_offset + 0.001f;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// The cut faces, one body at a time. A pixel looks into a body where, with the depth test off,
|
||||||
|
// its back faces outnumber its front faces: zero for a closed body, one where the cut has
|
||||||
|
// opened it. There a quad in the cut plane, in the body's own colour darkened, stands in for
|
||||||
|
// the material, so that a sparse layer on the cut shows a solid inside between its lines
|
||||||
|
// rather than whatever lies further down.
|
||||||
|
const bool bottom_cut = layers[0] > 0;
|
||||||
|
// the count needs every face of a body the range does not cut, its bottom included
|
||||||
|
m_shells.volumes.set_z_range(bottom_cut ? z_bottom : -FLT_MAX, z_top);
|
||||||
|
glsafe(::glEnable(GL_STENCIL_TEST));
|
||||||
|
for (const GLVolume* volume : m_shells.volumes.volumes) {
|
||||||
|
glsafe(::glStencilMask(0xFF));
|
||||||
|
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
||||||
|
glsafe(::glStencilFunc(GL_ALWAYS, 0, 0xFF));
|
||||||
|
glsafe(::glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP));
|
||||||
|
glsafe(::glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP));
|
||||||
|
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||||
|
glsafe(::glDepthMask(GL_FALSE));
|
||||||
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||||
|
shader->start_using();
|
||||||
|
m_shells.volumes.render(GLVolumeCollection::ERenderType::Opaque, true, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height},
|
||||||
|
[volume](const GLVolume& v) { return &v == volume; });
|
||||||
|
shader->stop_using();
|
||||||
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
glsafe(::glDepthMask(GL_TRUE));
|
||||||
|
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
|
||||||
|
glsafe(::glStencilFunc(GL_NOTEQUAL, 0, 0xFF));
|
||||||
|
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
||||||
|
glsafe(::glStencilMask(0x00));
|
||||||
|
// the tower draws itself in its own colours and leaves the volume's at the default
|
||||||
|
ColorRGBA inside = volume->color;
|
||||||
|
if (const auto* tower = dynamic_cast<const GLWipeTowerVolume*>(volume); tower != nullptr && !tower->colors().empty())
|
||||||
|
inside = tower->colors().front();
|
||||||
|
inside.r(0.55f * inside.r());
|
||||||
|
inside.g(0.55f * inside.g());
|
||||||
|
inside.b(0.55f * inside.b());
|
||||||
|
inside.a(1.0f);
|
||||||
|
render_solid_model_caps(z_bottom, z_top, bottom_cut, inside);
|
||||||
|
}
|
||||||
|
glsafe(::glStencilMask(0xFF));
|
||||||
|
glsafe(::glDisable(GL_STENCIL_TEST));
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A quad over the print in each cut plane, drawn where the stencil says the cut opened a model
|
||||||
|
void GCodeViewer::render_solid_model_caps(float z_bottom, float z_top, bool bottom_cut, const ColorRGBA& inside)
|
||||||
|
{
|
||||||
|
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||||
|
if (shader == nullptr)
|
||||||
|
return;
|
||||||
|
const BoundingBoxf3& box = m_paths_bounding_box;
|
||||||
|
const float margin = 1.0f;
|
||||||
|
const Vec2f min(static_cast<float>(box.min.x()) - margin, static_cast<float>(box.min.y()) - margin);
|
||||||
|
const Vec2f max(static_cast<float>(box.max.x()) + margin, static_cast<float>(box.max.y()) + margin);
|
||||||
|
auto build = [&](float z) {
|
||||||
|
GLModel::Geometry data;
|
||||||
|
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
||||||
|
data.reserve_vertices(4);
|
||||||
|
data.reserve_indices(6);
|
||||||
|
data.add_vertex(Vec3f(min.x(), min.y(), z));
|
||||||
|
data.add_vertex(Vec3f(max.x(), min.y(), z));
|
||||||
|
data.add_vertex(Vec3f(max.x(), max.y(), z));
|
||||||
|
data.add_vertex(Vec3f(min.x(), max.y(), z));
|
||||||
|
data.add_triangle(0, 1, 2);
|
||||||
|
data.add_triangle(0, 2, 3);
|
||||||
|
GLModel model;
|
||||||
|
model.init_from(std::move(data));
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
|
shader->start_using();
|
||||||
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
|
||||||
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
|
glsafe(::glDisable(GL_CULL_FACE));
|
||||||
|
if (z_top > -FLT_MAX) {
|
||||||
|
GLModel top = build(z_top + m_z_offset);
|
||||||
|
top.set_color(inside);
|
||||||
|
top.render();
|
||||||
|
}
|
||||||
|
if (bottom_cut) {
|
||||||
|
GLModel bottom = build(z_bottom + m_z_offset);
|
||||||
|
bottom.set_color(inside);
|
||||||
|
bottom.render();
|
||||||
|
}
|
||||||
|
glsafe(::glEnable(GL_CULL_FACE));
|
||||||
|
shader->stop_using();
|
||||||
|
}
|
||||||
|
|
||||||
//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,18 @@ 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);
|
||||||
|
void render_solid_model_caps(float z_bottom, float z_top, bool bottom_cut, const ColorRGBA& inside);
|
||||||
|
// 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 +305,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 +377,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();
|
||||||
@@ -6546,11 +6581,8 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||||||
// glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
|
// glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: finished");
|
BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: finished");
|
||||||
|
|
||||||
// Puts the canvas viewport back in place of the thumbnail one set above. The CLI renders
|
// Puts the canvas viewport back in place of the thumbnail one set above.
|
||||||
// thumbnails with no application and no plater, so there is no canvas viewport to restore.
|
wxGetApp().plater()->get_camera().apply_viewport();
|
||||||
if (wxTheApp != nullptr)
|
|
||||||
if (Plater *plater = wxGetApp().plater(); plater != nullptr)
|
|
||||||
plater->get_camera().apply_viewport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
|
void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
|
||||||
@@ -7782,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
|
||||||
@@ -8779,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;
|
||||||
|
boost::filesystem::create_directories(dest_path.parent_path());
|
||||||
std::string dest_zip_file = encode_path(dest_path.string().c_str());
|
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 {
|
try {
|
||||||
boost::filesystem::create_directories(dest_path.parent_path());
|
if (fs::exists(dest_path)) {
|
||||||
// symlink_status so that an existing symlink, dangling or not, is replaced rather than written through.
|
|
||||||
if (fs::exists(fs::symlink_status(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 };
|
||||||
|
|
||||||
|
|||||||
+216
-150
@@ -1997,126 +1997,6 @@ bool MainFrame::can_reslice() const
|
|||||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Orca: stable key persisted by the "remember last print action" preference. Reordering PrintSelectType
|
|
||||||
// must not silently remap a saved preference, so the key never derives from the enum value.
|
|
||||||
const char* print_select_type_key(MainFrame::PrintSelectType type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case MainFrame::ePrintAll: return "print_all";
|
|
||||||
case MainFrame::ePrintPlate: return "print_plate";
|
|
||||||
case MainFrame::eExportSlicedFile: return "export_sliced_file";
|
|
||||||
case MainFrame::eExportAllSlicedFile: return "export_all_sliced_file";
|
|
||||||
case MainFrame::eExportGcode: return "export_gcode";
|
|
||||||
case MainFrame::eSendGcode: return "send_gcode";
|
|
||||||
case MainFrame::eSendToPrinter: return "send_to_printer";
|
|
||||||
case MainFrame::eSendToPrinterAll: return "send_to_printer_all";
|
|
||||||
case MainFrame::ePrintMultiMachine: return "print_multi_machine";
|
|
||||||
case MainFrame::eUploadGcode: break; // Orca: no dropdown entry, never selectable
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Orca: single source for the print button and print dropdown labels
|
|
||||||
wxString print_select_type_label(MainFrame::PrintSelectType type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case MainFrame::ePrintAll: return _L("Print all");
|
|
||||||
case MainFrame::ePrintPlate: return _L("Print plate");
|
|
||||||
case MainFrame::eExportSlicedFile: return _L("Export plate sliced file");
|
|
||||||
case MainFrame::eExportAllSlicedFile: return _L("Export all sliced file");
|
|
||||||
case MainFrame::eExportGcode: return _L("Export G-code file");
|
|
||||||
case MainFrame::eSendGcode: return _L_CONTEXT("Print", "Verb");
|
|
||||||
case MainFrame::eSendToPrinter: return _L("Send");
|
|
||||||
case MainFrame::eSendToPrinterAll: return _L("Send all");
|
|
||||||
case MainFrame::ePrintMultiMachine: return _L("Send to Multi-device");
|
|
||||||
case MainFrame::eUploadGcode: break; // Orca: no dropdown entry, never selectable
|
|
||||||
}
|
|
||||||
return _L("Print plate");
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::vector<MainFrame::PrintSelectType> MainFrame::available_print_actions() const
|
|
||||||
{
|
|
||||||
std::vector<PrintSelectType> actions;
|
|
||||||
const auto preset_bundle = wxGetApp().preset_bundle;
|
|
||||||
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
|
|
||||||
|
|
||||||
if (preset_bundle && !preset_bundle->is_bbl_vendor() && !use_printer_agents) {
|
|
||||||
// ThirdParty actions
|
|
||||||
actions.push_back(eSendGcode);
|
|
||||||
// Orca: when the printer accepts a .gcode.3mf (the "Support 3MF as gcode" option),
|
|
||||||
// also offer exporting the sliced .gcode.3mf bundle
|
|
||||||
const auto* use_3mf_opt = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionBool>("use_3mf");
|
|
||||||
if (use_3mf_opt != nullptr && use_3mf_opt->value)
|
|
||||||
actions.push_back(eExportSlicedFile);
|
|
||||||
actions.push_back(eExportGcode);
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Orca Slicer actions
|
|
||||||
bool support_send = true;
|
|
||||||
bool support_print_all = true;
|
|
||||||
if (preset_bundle && !preset_bundle->use_bbl_network() && !use_printer_agents) {
|
|
||||||
support_send = false; // All 3rd print hosts do not have the send options
|
|
||||||
|
|
||||||
const auto& cfg = preset_bundle->printers.get_edited_preset().config;
|
|
||||||
const auto* host_type_opt = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type");
|
|
||||||
// Only simply print support uploading all plates
|
|
||||||
support_print_all = host_type_opt != nullptr && host_type_opt->value == PrintHostType::htSimplyPrint;
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.push_back(ePrintPlate);
|
|
||||||
if (support_print_all)
|
|
||||||
actions.push_back(ePrintAll);
|
|
||||||
if (support_send) {
|
|
||||||
actions.push_back(eSendToPrinter);
|
|
||||||
actions.push_back(eSendToPrinterAll);
|
|
||||||
}
|
|
||||||
if (enable_multi_machine)
|
|
||||||
actions.push_back(ePrintMultiMachine);
|
|
||||||
actions.push_back(eExportSlicedFile);
|
|
||||||
actions.push_back(eExportAllSlicedFile);
|
|
||||||
actions.push_back(eExportGcode);
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::select_print_action(PrintSelectType select_type)
|
|
||||||
{
|
|
||||||
m_print_btn->SetLabel(print_select_type_label(select_type));
|
|
||||||
m_print_select = select_type;
|
|
||||||
remember_print_select(select_type);
|
|
||||||
m_print_enable = get_enable_print_status();
|
|
||||||
m_print_btn->Enable(m_print_enable);
|
|
||||||
this->Layout();
|
|
||||||
fit_tab_labels(); // ORCA on label change
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::remember_print_select(PrintSelectType select_type)
|
|
||||||
{
|
|
||||||
if (!wxGetApp().app_config->get_bool("remember_print_action"))
|
|
||||||
return;
|
|
||||||
// AppConfig is marked dirty here and flushed by the regular autosave
|
|
||||||
wxGetApp().app_config->set("last_print_action", print_select_type_key(select_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainFrame::get_remembered_print_select(PrintSelectType& out) const
|
|
||||||
{
|
|
||||||
if (!wxGetApp().app_config->get_bool("remember_print_action"))
|
|
||||||
return false;
|
|
||||||
const std::string saved = wxGetApp().app_config->get("last_print_action");
|
|
||||||
if (saved.empty())
|
|
||||||
return false;
|
|
||||||
// Orca: only restore an action the current printer actually offers in the dropdown
|
|
||||||
for (PrintSelectType type : available_print_actions()) {
|
|
||||||
if (saved == print_select_type_key(type)) {
|
|
||||||
out = type;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxBoxSizer* MainFrame::create_side_tools()
|
wxBoxSizer* MainFrame::create_side_tools()
|
||||||
{
|
{
|
||||||
enable_multi_machine = wxGetApp().is_enable_multi_machine();
|
enable_multi_machine = wxGetApp().is_enable_multi_machine();
|
||||||
@@ -2136,14 +2016,6 @@ wxBoxSizer* MainFrame::create_side_tools()
|
|||||||
m_print_btn = new SideButton(print_panel, _L("Print plate"), "");
|
m_print_btn = new SideButton(print_panel, _L("Print plate"), "");
|
||||||
m_print_option_btn = new SideButton(print_panel, "", "sidebutton_dropdown", 0, 14);
|
m_print_option_btn = new SideButton(print_panel, "", "sidebutton_dropdown", 0, 14);
|
||||||
|
|
||||||
// Orca: restore the last used print/export action if the user opted to remember it
|
|
||||||
PrintSelectType remembered_print_select;
|
|
||||||
if (get_remembered_print_select(remembered_print_select)) {
|
|
||||||
m_print_select = remembered_print_select;
|
|
||||||
m_print_btn->SetLabel(print_select_type_label(remembered_print_select));
|
|
||||||
fit_tab_labels(); // ORCA on label change
|
|
||||||
}
|
|
||||||
|
|
||||||
auto slice_sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto slice_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
slice_sizer->Add(m_slice_option_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
|
slice_sizer->Add(m_slice_option_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
|
||||||
slice_sizer->Add(m_slice_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
|
slice_sizer->Add(m_slice_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
|
||||||
@@ -2306,15 +2178,193 @@ wxBoxSizer* MainFrame::create_side_tools()
|
|||||||
m_print_option_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
|
m_print_option_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
SidePopup* p = new SidePopup(this);
|
SidePopup* p = new SidePopup(this);
|
||||||
for (PrintSelectType type : available_print_actions()) {
|
|
||||||
SideButton* btn = new SideButton(p, print_select_type_label(type), "");
|
if (wxGetApp().preset_bundle
|
||||||
btn->SetCornerRadius(0);
|
&& !wxGetApp().preset_bundle->is_bbl_vendor()
|
||||||
btn->Bind(wxEVT_BUTTON, [this, p, type](wxCommandEvent&) {
|
&& !wxGetApp().app_config->get_bool("use_printer_agents")) {
|
||||||
select_print_action(type);
|
// ThirdParty Buttons
|
||||||
|
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
|
||||||
|
export_gcode_btn->SetCornerRadius(0);
|
||||||
|
export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Export G-code file"));
|
||||||
|
m_print_select = eExportGcode;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
p->Dismiss();
|
p->Dismiss();
|
||||||
});
|
});
|
||||||
p->append_button(btn);
|
|
||||||
|
// upload and print
|
||||||
|
SideButton* send_gcode_btn = new SideButton(p, _L_CONTEXT("Print", "Verb"), "");
|
||||||
|
send_gcode_btn->SetCornerRadius(0);
|
||||||
|
send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L_CONTEXT("Print", "Verb"));
|
||||||
|
m_print_select = eSendGcode;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
p->append_button(send_gcode_btn);
|
||||||
|
|
||||||
|
// Orca: when the printer accepts a .gcode.3mf (the "Support 3MF as gcode" option),
|
||||||
|
// also offer exporting the sliced .gcode.3mf bundle
|
||||||
|
const auto& printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
const auto* use_3mf_opt = printer_config.option<ConfigOptionBool>("use_3mf");
|
||||||
|
if (use_3mf_opt != nullptr && use_3mf_opt->value) {
|
||||||
|
SideButton* export_sliced_file_btn = new SideButton(p, _L("Export plate sliced file"), "");
|
||||||
|
export_sliced_file_btn->SetCornerRadius(0);
|
||||||
|
export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Export plate sliced file"));
|
||||||
|
m_print_select = eExportSlicedFile;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
p->append_button(export_sliced_file_btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->append_button(export_gcode_btn);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
//Orca Slicer Buttons
|
||||||
|
SideButton* print_plate_btn = new SideButton(p, _L("Print plate"), "");
|
||||||
|
print_plate_btn->SetCornerRadius(0);
|
||||||
|
|
||||||
|
SideButton* send_to_printer_btn = new SideButton(p, _L("Send"), "");
|
||||||
|
send_to_printer_btn->SetCornerRadius(0);
|
||||||
|
|
||||||
|
SideButton* export_sliced_file_btn = new SideButton(p, _L("Export plate sliced file"), "");
|
||||||
|
export_sliced_file_btn->SetCornerRadius(0);
|
||||||
|
|
||||||
|
SideButton* export_all_sliced_file_btn = new SideButton(p, _L("Export all sliced file"), "");
|
||||||
|
export_all_sliced_file_btn->SetCornerRadius(0);
|
||||||
|
|
||||||
|
print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Print plate"));
|
||||||
|
m_print_select = ePrintPlate;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
SideButton* print_all_btn = new SideButton(p, _L("Print all"), "");
|
||||||
|
print_all_btn->SetCornerRadius(0);
|
||||||
|
print_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Print all"));
|
||||||
|
m_print_select = ePrintAll;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
send_to_printer_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Send"));
|
||||||
|
m_print_select = eSendToPrinter;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
SideButton* send_to_printer_all_btn = new SideButton(p, _L("Send all"), "");
|
||||||
|
send_to_printer_all_btn->SetCornerRadius(0);
|
||||||
|
send_to_printer_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Send all"));
|
||||||
|
m_print_select = eSendToPrinterAll;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Export plate sliced file"));
|
||||||
|
m_print_select = eExportSlicedFile;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
export_all_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Export all sliced file"));
|
||||||
|
m_print_select = eExportAllSlicedFile;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
bool support_send = true;
|
||||||
|
bool support_print_all = true;
|
||||||
|
|
||||||
|
const auto preset_bundle = wxGetApp().preset_bundle;
|
||||||
|
if (preset_bundle) {
|
||||||
|
if (preset_bundle->use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents")) {
|
||||||
|
// BBL network support everything
|
||||||
|
} else {
|
||||||
|
support_send = false; // All 3rd print hosts do not have the send options
|
||||||
|
|
||||||
|
auto cfg = preset_bundle->printers.get_edited_preset().config;
|
||||||
|
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||||
|
|
||||||
|
// Only simply print support uploading all plates
|
||||||
|
support_print_all = host_type == PrintHostType::htSimplyPrint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p->append_button(print_plate_btn);
|
||||||
|
if (support_print_all) {
|
||||||
|
p->append_button(print_all_btn);
|
||||||
|
}
|
||||||
|
if (support_send) {
|
||||||
|
p->append_button(send_to_printer_btn);
|
||||||
|
p->append_button(send_to_printer_all_btn);
|
||||||
|
}
|
||||||
|
if (enable_multi_machine) {
|
||||||
|
SideButton* print_multi_machine_btn = new SideButton(p, _L("Send to Multi-device"), "");
|
||||||
|
print_multi_machine_btn->SetCornerRadius(0);
|
||||||
|
print_multi_machine_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Send to Multi-device"));
|
||||||
|
m_print_select = ePrintMultiMachine;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
p->append_button(print_multi_machine_btn);
|
||||||
|
}
|
||||||
|
p->append_button(export_sliced_file_btn);
|
||||||
|
p->append_button(export_all_sliced_file_btn);
|
||||||
|
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
|
||||||
|
export_gcode_btn->SetCornerRadius(0);
|
||||||
|
export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
|
||||||
|
m_print_btn->SetLabel(_L("Export G-code file"));
|
||||||
|
m_print_select = eExportGcode;
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
fit_tab_labels(); // ORCA on label change
|
||||||
|
p->Dismiss();
|
||||||
|
});
|
||||||
|
p->append_button(export_gcode_btn);
|
||||||
|
}
|
||||||
|
|
||||||
p->Popup(m_print_btn);
|
p->Popup(m_print_btn);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -4174,22 +4224,38 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const
|
|||||||
|
|
||||||
void MainFrame::set_print_button_to_default(PrintSelectType select_type)
|
void MainFrame::set_print_button_to_default(PrintSelectType select_type)
|
||||||
{
|
{
|
||||||
// Orca: keep the user's remembered print/export action instead of resetting it to the computed
|
if (select_type == PrintSelectType::ePrintPlate) {
|
||||||
// default. get_remembered_print_select() already rejects anything this printer does not offer.
|
m_print_btn->SetLabel(_L("Print plate"));
|
||||||
PrintSelectType remembered;
|
m_print_select = ePrintPlate;
|
||||||
if (get_remembered_print_select(remembered))
|
if (m_print_enable)
|
||||||
select_type = remembered;
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
if (select_type == eUploadGcode)
|
this->Layout();
|
||||||
return; // unsupported: no dropdown entry exists for this action
|
} else if (select_type == PrintSelectType::eSendGcode) {
|
||||||
|
m_print_btn->SetLabel(_L_CONTEXT("Print", "Verb"));
|
||||||
m_print_btn->SetLabel(print_select_type_label(select_type));
|
m_print_select = eSendGcode;
|
||||||
m_print_select = select_type;
|
if (m_print_enable)
|
||||||
// get_enable_print_status() already applies can_send_gcode() to the actions that need it
|
m_print_enable = get_enable_print_status() && can_send_gcode();
|
||||||
if (m_print_enable)
|
m_print_btn->Enable(m_print_enable);
|
||||||
m_print_enable = get_enable_print_status();
|
this->Layout();
|
||||||
m_print_btn->Enable(m_print_enable);
|
} else if (select_type == PrintSelectType::eExportGcode) {
|
||||||
this->Layout();
|
m_print_btn->SetLabel(_L("Export G-code file"));
|
||||||
|
m_print_select = eExportGcode;
|
||||||
|
if (m_print_enable)
|
||||||
|
m_print_enable = get_enable_print_status() && can_send_gcode();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
} else if (select_type == PrintSelectType::eExportSlicedFile) {
|
||||||
|
m_print_btn->SetLabel(_L("Export plate sliced file"));
|
||||||
|
m_print_select = eExportSlicedFile;
|
||||||
|
if (m_print_enable)
|
||||||
|
m_print_enable = get_enable_print_status();
|
||||||
|
m_print_btn->Enable(m_print_enable);
|
||||||
|
this->Layout();
|
||||||
|
} else {
|
||||||
|
// unsupport
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::add_to_recent_projects(const wxString& filename)
|
void MainFrame::add_to_recent_projects(const wxString& filename)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "GUI_Utils.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
#include "Event.hpp"
|
#include "Event.hpp"
|
||||||
@@ -431,14 +430,6 @@ public:
|
|||||||
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
|
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
|
||||||
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
||||||
void set_print_button_to_default(PrintSelectType select_type);
|
void set_print_button_to_default(PrintSelectType select_type);
|
||||||
// Orca: the print/export actions the current printer offers, in the order the dropdown lists them.
|
|
||||||
// The dropdown is built from this, and a remembered action is only restored if it appears here.
|
|
||||||
std::vector<PrintSelectType> available_print_actions() const;
|
|
||||||
// Orca: apply an action picked from the print dropdown to the print button
|
|
||||||
void select_print_action(PrintSelectType select_type);
|
|
||||||
// Orca: remember the user's preferred print/export action across sessions (see "remember_print_action" preference)
|
|
||||||
void remember_print_select(PrintSelectType select_type);
|
|
||||||
bool get_remembered_print_select(PrintSelectType& out) const;
|
|
||||||
|
|
||||||
bool can_save() const;
|
bool can_save() const;
|
||||||
bool can_save_as() const;
|
bool can_save_as() const;
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1831,9 +1832,6 @@ void PreferencesDialog::create_items()
|
|||||||
auto item_mix_print_high_low_temperature = create_item_checkbox(_L("Remove mixed temperature restriction"), _L("With this option enabled, you can print materials with a large temperature difference together."), "enable_high_low_temp_mixed_printing");
|
auto item_mix_print_high_low_temperature = create_item_checkbox(_L("Remove mixed temperature restriction"), _L("With this option enabled, you can print materials with a large temperature difference together."), "enable_high_low_temp_mixed_printing");
|
||||||
g_sizer->Add(item_mix_print_high_low_temperature);
|
g_sizer->Add(item_mix_print_high_low_temperature);
|
||||||
|
|
||||||
auto item_remember_print_action = create_item_checkbox(_L("Remember last print action"), _L("If enabled, OrcaSlicer will remember the last selected option in the print button's dropdown (e.g. Print, Export plate sliced file, Export G-code file) and use it as the default on next startup."), "remember_print_action");
|
|
||||||
g_sizer->Add(item_remember_print_action);
|
|
||||||
|
|
||||||
//// CONTROL > Camera
|
//// CONTROL > Camera
|
||||||
g_sizer->Add(create_item_title(_L("Camera")), 1, wxEXPAND);
|
g_sizer->Add(create_item_title(_L("Camera")), 1, wxEXPAND);
|
||||||
|
|
||||||
@@ -2047,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;
|
||||||
|
json j_pre = json::parse(payload);
|
||||||
|
if (j_pre.empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//bind/unbind
|
//bind/unbind
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json j_pre = json::parse(payload);
|
|
||||||
if (j_pre.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -1405,26 +1405,23 @@ bool GuideFrame::BuildProfileDataFromVendors()
|
|||||||
// is served from the shipped profiles. Each is stamped by name and
|
// is served from the shipped profiles. Each is stamped by name and
|
||||||
// version alone: a profile change requires a version bump, so those two
|
// version alone: a profile change requires a version bump, so those two
|
||||||
// determine content wherever the vendor's copy sits.
|
// determine content wherever the vendor's copy sits.
|
||||||
std::vector<PresetBundle::VendorSource> ordered;
|
struct VendorSource { std::string name; boost::filesystem::path dir; std::string version; };
|
||||||
json stamps = json::array();
|
std::vector<VendorSource> ordered;
|
||||||
auto add_vendor = [&ordered, &stamps](const std::string& name, const boost::filesystem::path& dir) {
|
auto add_vendor = [&ordered](const std::string& name, const boost::filesystem::path& dir) {
|
||||||
// The version a load from `dir` would serve: the profile's where one
|
// The version a load from `dir` would serve: the profile's where one
|
||||||
// exists (a cache is only served while it covers the profile beside
|
// exists (a cache is only served while it covers the profile beside
|
||||||
// it), the cache's own stamp where the cache is the whole vendor.
|
// it), the cache's own stamp where the cache is the whole vendor.
|
||||||
// A profile without a version (blacklist.json) carries no presets
|
// A profile without a version (blacklist.json) carries no presets
|
||||||
// and is passed over.
|
// and is passed over.
|
||||||
const boost::filesystem::path profile = dir / (name + ".json");
|
const boost::filesystem::path profile = dir / (name + ".json");
|
||||||
std::string version;
|
|
||||||
if (boost::filesystem::exists(profile)) {
|
if (boost::filesystem::exists(profile)) {
|
||||||
const Semver v = get_version_from_json(profile.string());
|
const Semver v = get_version_from_json(profile.string());
|
||||||
if (! v.valid())
|
if (v.valid())
|
||||||
return;
|
ordered.push_back({name, dir, v.to_string()});
|
||||||
version = v.to_string();
|
|
||||||
} else {
|
} else {
|
||||||
version = VendorCacheFile::peek_version((dir / (name + ".opc")).string(), name);
|
ordered.push_back({name, dir,
|
||||||
|
VendorCacheFile::peek_version((dir / (name + ".opc")).string(), name)});
|
||||||
}
|
}
|
||||||
ordered.push_back({name, dir});
|
|
||||||
stamps.push_back({name, version});
|
|
||||||
};
|
};
|
||||||
const std::string filament_library(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
const std::string filament_library(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
||||||
if (auto it = vendor_sources.find(filament_library); it != vendor_sources.end())
|
if (auto it = vendor_sources.find(filament_library); it != vendor_sources.end())
|
||||||
@@ -1434,6 +1431,9 @@ bool GuideFrame::BuildProfileDataFromVendors()
|
|||||||
add_vendor(name, dir);
|
add_vendor(name, dir);
|
||||||
if (ordered.empty())
|
if (ordered.empty())
|
||||||
return false;
|
return false;
|
||||||
|
json stamps = json::array();
|
||||||
|
for (const VendorSource& v : ordered)
|
||||||
|
stamps.push_back({v.name, v.version});
|
||||||
|
|
||||||
// What this function derives is a pure function of that stamped set, so
|
// What this function derives is a pure function of that stamped set, so
|
||||||
// the derived JSON is cached whole: a fresh cache makes an open one
|
// the derived JSON is cached whole: a fresh cache makes an open one
|
||||||
@@ -1461,18 +1461,26 @@ bool GuideFrame::BuildProfileDataFromVendors()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Each vendor comes from its preset cache where one covers it, which is
|
// Each vendor comes from its preset cache where one covers it, which is
|
||||||
// what makes this worth doing instead of the scan below.
|
// what makes this worth doing instead of the scan below; loading into a
|
||||||
PresetBundle bundle;
|
// bundle per vendor keeps the install order the startup path has.
|
||||||
std::vector<std::string> failed;
|
PresetBundle bundle;
|
||||||
const std::string errors = bundle.load_vendors(ordered, ForwardCompatibilitySubstitutionRule::EnableSilent,
|
auto load_vendor = [](PresetBundle& into, const std::string& vendor,
|
||||||
/*allow_cache=*/true, m_cancel_token.get(), &failed).second;
|
const boost::filesystem::path& dir, const PresetBundle* base) {
|
||||||
if (*m_cancel_token || bundle.vendors.empty())
|
into.load_vendor_configs_from_json(dir.string(), vendor, PresetBundle::LoadSystem,
|
||||||
return false;
|
ForwardCompatibilitySubstitutionRule::EnableSilent, base);
|
||||||
if (! errors.empty())
|
};
|
||||||
BOOST_LOG_TRIVIAL(warning) << "GuideFrame: loading the vendors reported: " << errors;
|
for (const VendorSource& v : ordered) {
|
||||||
// A vendor that failed to load sends this open to the scan below, which lists
|
if (*m_cancel_token)
|
||||||
// what it can read of every vendor.
|
return false; // as in the scan below: a vendor without a cache is parsed, and that takes time
|
||||||
if (! failed.empty())
|
if (v.name == filament_library) {
|
||||||
|
load_vendor(bundle, v.name, v.dir, nullptr);
|
||||||
|
} else {
|
||||||
|
PresetBundle tmp;
|
||||||
|
load_vendor(tmp, v.name, v.dir, &bundle);
|
||||||
|
bundle.merge_presets(std::move(tmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bundle.vendors.empty())
|
||||||
return false;
|
return false;
|
||||||
if (! BuildProfileJson(bundle, /*require_all_resource_vendors=*/false))
|
if (! BuildProfileJson(bundle, /*require_all_resource_vendors=*/false))
|
||||||
return false;
|
return false;
|
||||||
@@ -1724,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
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user