mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 08:11:14 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7515fb480f | ||
|
|
acc1d6fd21 | ||
|
|
6ca0fc5ef1 | ||
|
|
bcf7d078cd | ||
|
|
15fc96fca7 | ||
|
|
1fffda0533 | ||
|
|
33e26a127e | ||
|
|
6dd0cb9eba | ||
|
|
9bf0fc2a8e | ||
|
|
5bf6db95b3 | ||
|
|
0feb87b417 |
+3
-36
@@ -8,10 +8,9 @@ SCRIPT_PATH=$(dirname "$(readlink -f "${0}")")
|
||||
pushd "${SCRIPT_PATH}" > /dev/null
|
||||
|
||||
function usage() {
|
||||
echo "Usage: ./${SCRIPT_NAME} [-1][-b][-c][-d][-D][-e][-F][-g][-h][-i][-j N][-J N][-p][-r][-s][-t][-u][-l][-L]"
|
||||
echo "Usage: ./${SCRIPT_NAME} [-1][-b][-c][-d][-D][-e][-F][-g][-h][-i][-j N][-p][-r][-s][-t][-u][-l][-L]"
|
||||
echo " -1: limit builds to one core (where possible)"
|
||||
echo " -j N: limit builds to N cores (where possible)"
|
||||
echo " -J N: build up to N dependencies at a time, each still using -j jobs (default: 1)"
|
||||
echo " -b: build in Debug mode"
|
||||
echo " -c: force a clean build"
|
||||
echo " -C: enable ANSI-colored compile output (GNU/Clang only)"
|
||||
@@ -37,13 +36,12 @@ function usage() {
|
||||
}
|
||||
|
||||
SLIC3R_PRECOMPILED_HEADERS="ON"
|
||||
DEPS_PARALLEL=""
|
||||
|
||||
unset name
|
||||
BUILD_DIR=build
|
||||
BUILD_CONFIG=Release
|
||||
FORWARDED_ARGS=()
|
||||
while getopts ":1j:J:bcCdDeFghiprstulL" opt ; do
|
||||
while getopts ":1j:bcCdDeFghiprstulL" opt ; do
|
||||
case ${opt} in
|
||||
1 )
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=1
|
||||
@@ -53,10 +51,6 @@ while getopts ":1j:J:bcCdDeFghiprstulL" opt ; do
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=$OPTARG
|
||||
FORWARDED_ARGS+=("-j" "$OPTARG")
|
||||
;;
|
||||
J )
|
||||
DEPS_PARALLEL=$OPTARG
|
||||
FORWARDED_ARGS+=("-J" "$OPTARG")
|
||||
;;
|
||||
b )
|
||||
BUILD_DIR=build-dbg
|
||||
BUILD_CONFIG=Debug
|
||||
@@ -141,11 +135,6 @@ if [[ -n "${CLEAN_DOCKER_IMAGE}" ]] && [[ -z "${USE_DOCKER}" ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] && ! [[ "${DEPS_PARALLEL}" =~ ^[1-9][0-9]*$ ]] ; then
|
||||
echo "Error: -J expects a positive integer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function check_available_memory_and_disk() {
|
||||
FREE_MEM_GB=$(free --gibi --total | grep 'Mem' | rev | cut --delimiter=" " --fields=1 | rev)
|
||||
MIN_MEM_GB=10
|
||||
@@ -548,29 +537,7 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
|
||||
fi
|
||||
|
||||
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" "${CMAKE_CCACHE_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
||||
# The top-level build runs one dependency at a time by default, which keeps the console
|
||||
# output readable and lets that dependency's own build use all of CMAKE_BUILD_PARALLEL_LEVEL.
|
||||
# -J raises the top level instead, and -j still applies in full to every dependency, so the
|
||||
# worst case is -J times -j compile jobs at once. Ninja has no job server to share a pool
|
||||
# across the nested builds, so that ceiling is not enforced anywhere: pick -J to suit the RAM.
|
||||
DEPS_JOBS=1
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] ; then
|
||||
DEPS_JOBS=${DEPS_PARALLEL}
|
||||
SAVED_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL-}
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-$(nproc)}
|
||||
echo "Building up to ${DEPS_JOBS} dependencies at a time, ${CMAKE_BUILD_PARALLEL_LEVEL} jobs each: up to $(( DEPS_JOBS * CMAKE_BUILD_PARALLEL_LEVEL )) compile jobs at once."
|
||||
fi
|
||||
|
||||
print_and_run cmake --build deps/$BUILD_DIR -j"${DEPS_JOBS}"
|
||||
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] ; then
|
||||
# Give the whole -j back to the OrcaSlicer build below.
|
||||
if [[ -n "${SAVED_PARALLEL_LEVEL}" ]] ; then
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=${SAVED_PARALLEL_LEVEL}
|
||||
else
|
||||
unset CMAKE_BUILD_PARALLEL_LEVEL
|
||||
fi
|
||||
fi
|
||||
print_and_run cmake --build deps/$BUILD_DIR -j1
|
||||
fi
|
||||
|
||||
if [[ -n "${BUILD_ORCA}" ]] || [[ -n "${BUILD_TESTS}" ]] ; then
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
# Multi-extruder and multi-AMS device control
|
||||
|
||||
`DeviceManager`/`MachineObject` model the toolheads, AMS units, and filament slots of a connected
|
||||
printer and drive the device-control UI from that model. The model represents the counts as
|
||||
device-reported runtime values rather than compile-time caps, so a printer with an arbitrary number
|
||||
of toolheads, several AMS units, and a non-uniform number of slots per unit is handled by the same
|
||||
code paths. The printer agent is responsible for translating its printer's state into the shared
|
||||
JSON dialect this model consumes; the agent boundary is described in
|
||||
[printer-agent.md](printer-agent.md), and this document describes the dialect and the model.
|
||||
|
||||
## Model layout
|
||||
|
||||
`MachineObject` owns the device subsystems that this document covers:
|
||||
|
||||
- `DevExtderSystem` — the toolheads (extruders) and their per-toolhead filament/temperature state.
|
||||
- `DevFilaSystem` — the AMS units (`DevAms`) and their trays (`DevAmsTray`).
|
||||
- `DevNozzleSystem` — the nozzles and, on rack printers, `DevNozzleRack`.
|
||||
- `DevFilaSwitch` — a filament switch that can feed several AMS units to more than one extruder.
|
||||
|
||||
Each subsystem exposes its own count — `DevExtderSystem::GetTotalExtderCount()`,
|
||||
`DevFilaSystem::GetAmsCount()`, `DevAms::GetSlotCount()` — and the rest of the application derives
|
||||
its layout from those values rather than from fixed assumptions.
|
||||
|
||||
## Wire dialect
|
||||
|
||||
The dialect is the Bambu-shaped JSON that `DeviceManager` and `MachineObject` already consume. An
|
||||
agent whose printer speaks something else is expected to translate into it; the agent is not free
|
||||
to invent a new shape. Two representations coexist for extruders, and one for AMS/slots.
|
||||
|
||||
### Extruders
|
||||
|
||||
The generic representation is `print.device.extruder`, parsed by
|
||||
`ExtderSystemParser::ParseV2_0` (`DevExtruderSystem.cpp`). It has a packed `state` integer and an
|
||||
`info` array with one object per toolhead:
|
||||
|
||||
```text
|
||||
device.extruder.state packed integer
|
||||
bits 0..3 total extruder count
|
||||
bits 4..7 current extruder id
|
||||
bits 8..11 target extruder id
|
||||
bits 12..14 switch state
|
||||
bits 15..18 currently loading extruder id
|
||||
bit 19 busy for loading
|
||||
|
||||
device.extruder.info[] one entry per toolhead
|
||||
id extruder id
|
||||
filam_bak array of backup-group bits
|
||||
info bit 1 has filament, bit 2 buffer has filament, bit 3 nozzle present
|
||||
temp bits 0..15 current temperature, bits 16..31 target temperature
|
||||
spre slot_id bits 0..7, ams_id bits 8..15 (previous)
|
||||
snow same packing (current)
|
||||
star same packing (target)
|
||||
stat bit 0..15 AMS status, bits 16..31 RFID status
|
||||
hnow current nozzle id
|
||||
```
|
||||
|
||||
The legacy single/dual representation is the pair of scalar fields `nozzle_temper` /
|
||||
`nozzle_target_temper`, plus the `print.ams.tray_tar` / `tray_now` tray pointer, parsed by
|
||||
`ExtderSystemParser::ParseV1_0`. That parser is a fallback and only fills the main extruder when the
|
||||
reported toolhead count is one.
|
||||
|
||||
Physical extruder ids are fixed (`MAIN_EXTRUDER_ID = 0`, `DEPUTY_EXTRUDER_ID = 1`). Bambu printers
|
||||
present the two toolheads as a left/right pair and invert the physical-to-logical mapping; generic
|
||||
printers use the toolhead id itself as the logical id. `DevNozzle::GetLogicExtruderId()` /
|
||||
`GetExtruderId()` centralise that distinction.
|
||||
|
||||
### AMS units and slots
|
||||
|
||||
AMS units arrive as `print.ams.ams[]` and are parsed by `DevFilaSystemParser::ParseV1_0`. Each unit
|
||||
carries an `id` and an `info` bitfield:
|
||||
|
||||
```text
|
||||
info bits 0..3 type: 0 dummy, 1 AMS, 2 AMS-Lite, 3 N3F, 4 N3S, 5 AMS-Lite-mixed (N9)
|
||||
info bits 8..11 bound extruder id (0xE marks a switch-bound unit)
|
||||
info bits 24..27 switch inlet when the unit is switch-bound
|
||||
```
|
||||
|
||||
`DevAmsType` (`DevDefs.h`) enumerates the resulting unit kinds: `EXT_SPOOL`, `AMS`, `AMS_LITE`,
|
||||
`N3F`, `N3S`, and the `AMS_LITE_MIXED` N9 variant. `AMS_LITE_MIXED` reports itself as `AMS_LITE` to
|
||||
generic callers; code that needs the distinction uses `IsAmsLiteMixed()`.
|
||||
|
||||
A unit whose bound-extruder nibble is `0xE` is switch-bound: it is kept only when a `DevFilaSwitch`
|
||||
is installed, in which case its extruder id is pinned to the main extruder and its binding set
|
||||
becomes `{0, 1}`; otherwise the unit is dropped from the AMS list.
|
||||
|
||||
Each unit owns a map of `DevAmsTray` keyed by the tray id from the payload. The slot count is a
|
||||
property of the unit kind and the reported trays:
|
||||
|
||||
- `AMS`, `AMS_LITE`, and `N3F` report four slots on Bambu printers, matching the firmware contract;
|
||||
- `N3S` reports one slot;
|
||||
- `EXT_SPOOL` and any non-Bambu unit report the number of trays actually present.
|
||||
|
||||
The tray ids are the payload's slot indices; they are expected to be unique, 0-based, and contiguous
|
||||
within a unit, because the generic tray-index mapping below is cumulative.
|
||||
|
||||
Bulk/extended spools are carried by `print.vir_slot`, which `DeviceManager` parses into
|
||||
`MachineObject::vt_slot`. Two ids are fixed: `VIRTUAL_TRAY_MAIN_ID = 255` (extruder 0) and
|
||||
`VIRTUAL_TRAY_DEPUTY_ID = 254` (extruder 1); the id convention is `vt_id = 255 - extruder_id`, so
|
||||
further extruders use 253, 252, … . The `vir_slot` parser recognises the 255/254 pair; the agent
|
||||
filament descriptor path `DevFilaSystemParser::ParseAgentFilament` is the entry that stores a fuller
|
||||
descending set in `vt_slot`.
|
||||
|
||||
## Mapping
|
||||
|
||||
Several subsystems relate AMS units, trays, and extruders, and more than one place derives a tray
|
||||
index. The Bambu mapping path (`DevMappingUtil`) computes `ams_id * 4 + slot_id` and knows only the
|
||||
Bambu unit kinds; `DevFilaSystem` provides the device model's own mapping below. The two agree for
|
||||
Bambu units and diverge for a non-Bambu unit whose slot count is not four.
|
||||
|
||||
### AMS id to extruder id
|
||||
|
||||
`DevFilaSystem::GetExtruderIdByAmsId()` resolves an AMS id to the extruder it feeds:
|
||||
|
||||
- an AMS unit returns its bound extruder (`DevAms::GetExtruderId()`);
|
||||
- a virtual tray returns `VIRTUAL_TRAY_MAIN_ID - vt_id`;
|
||||
- Bambu's virtual AMS ids map to the main/deputy extruder.
|
||||
|
||||
A unit may be bound to more than one extruder when a `DevFilaSwitch` feeds it; the binding set
|
||||
(`DevAms::m_binded_extruder_set`) and switch inlet are read by switch-aware code while the
|
||||
single-extruder id stays pinned for legacy callers.
|
||||
|
||||
### Tray index
|
||||
|
||||
`DevFilaSystem::GetTrayIndexMap()` maps a global tray index to an `(ams_id, slot_id)` pair and is
|
||||
used for backup-slot translation, calibration addressing, and the mapping popups.
|
||||
|
||||
- Bambu AMS: `ams_id * 4 + slot_id`; N3S: the AMS id; AMS-Lite-mixed (N9): `24 + slot_id`.
|
||||
- Generic (non-Bambu): a running lane index — the count of trays in preceding units plus the slot
|
||||
id. This depends on each unit's tray ids being 0-based and contiguous.
|
||||
|
||||
### Virtual-slot recognition
|
||||
|
||||
`devPrinterUtil::IsVirtualSlot()` decides whether an id denotes a virtual tray. On Bambu printers it
|
||||
is the fixed pair `{255, 254}`. On generic printers it is the descending range
|
||||
`[255 - count + 1, 255]`, where `count` is the selected printer preset's extruder count. The preset
|
||||
is expected to match the connected device; when the two disagree the virtual range is wrong, which
|
||||
is why a mismatch is reported rather than silently accepted.
|
||||
|
||||
## Device-control UI
|
||||
|
||||
The device-control widgets follow the same device-reported counts.
|
||||
|
||||
- `AMSModel` (`AMSItem.hpp`) mirrors `DevAmsType` numerically: `EXT_AMS`, `GENERIC_AMS`, `AMS_LITE`,
|
||||
`N3F_AMS`, `N3S_AMS`. A plain `AMS` maps to `GENERIC_AMS`.
|
||||
- On non-Bambu printers `use_generic_ams_layout()` routes `GENERIC_AMS` units through a variable-lane
|
||||
rendering that draws one lane per reported tray; Bambu keeps its fixed four-slot rendering.
|
||||
- `AMSControl` picks its layout by vendor: Bambu keeps the left/right presentation
|
||||
(`CreateAmsSingleNozzle` for one toolhead, `CreateAmsDoubleNozzle` for two), while non-Bambu
|
||||
printers use `CreateAmsMultiNozzle`, which builds one preview and one AMS simplebook per toolhead
|
||||
for any count.
|
||||
- The status panel's `ExtruderImage` renders one per-toolhead state per nozzle and is resized to
|
||||
`GetTotalExtderCount()`. Non-Bambu printers choose the active toolhead through
|
||||
`m_generic_nozzle_selector` (shown when more than one toolhead exists); Bambu keeps its left/right
|
||||
`m_nozzle_btn_panel`. Nozzle temperature controls are created on demand per toolhead.
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Single dialect.** The agent translates its protocol into this JSON; the model does not learn
|
||||
vendor protocols. Extending the model with a new unit kind or field means extending the dialect.
|
||||
- **Agent-produced counts.** Toolhead, AMS, and slot counts come from the device payload. Nothing
|
||||
caps them at two toolheads or four slots except the Bambu contract described above.
|
||||
- **Extruder dialect invariants.** `device.extruder.state`'s count field must equal the length of
|
||||
`info[]`, and `info[]` must be ordered by extruder id with `id` equal to the array index: the
|
||||
parser places toolheads by array position while `GetExtderById()` indexes by id.
|
||||
- **Preset/device agreement.** The generic layout and virtual-slot range derive their count from the
|
||||
selected printer preset. A printer preset must therefore declare the same number of
|
||||
`nozzle_diameter` entries as the device has toolheads; a mismatch is surfaced because the sidebar
|
||||
and extruder logic would otherwise disagree.
|
||||
- **Bambu compatibility.** Bambu-specific presentation (left/right pair, fixed 4-slot units, fixed
|
||||
virtual ids, physical/logical inversion) is preserved behind the vendor check. Generic paths do
|
||||
not alter it.
|
||||
- **Tray-index invariant.** The generic tray-index map is cumulative, so payload tray ids must be
|
||||
unique, 0-based, and contiguous per unit. A malformed id is a producer error.
|
||||
- **Protocol markers.** The dialect has no neutral equivalent for some Bambu state, so agents that
|
||||
lack it send empty `print.cfg`, `print.fun`, `print.aux`, and `print.stat`. Their presence selects
|
||||
the new-protocol parse path; they are compatibility markers, not feature switches.
|
||||
|
||||
## Main implementation locations
|
||||
|
||||
- [`DevExtruderSystem`](../../src/slic3r/GUI/DeviceCore/DevExtruderSystem.h) — toolhead model and the
|
||||
`device.extruder` parser
|
||||
- [`DevFilaSystem`](../../src/slic3r/GUI/DeviceCore/DevFilaSystem.h) — AMS/tray model, the AMS parser,
|
||||
and the mapping helpers
|
||||
- [`DevDefs.h`](../../src/slic3r/GUI/DeviceCore/DevDefs.h) — `DevAmsType`, extruder ids, and virtual
|
||||
tray ids
|
||||
- [`DeviceManager.cpp`](../../src/slic3r/GUI/DeviceManager.cpp) — message dispatch, `vir_slot`
|
||||
parsing, and device state assembly
|
||||
- [`AMSControl.cpp`](../../src/slic3r/GUI/Widgets/AMSControl.cpp) — per-toolhead AMS panes
|
||||
(`CreateAmsMultiNozzle`) and the generic layout
|
||||
- [`AMSItem.hpp`](../../src/slic3r/GUI/Widgets/AMSItem.hpp) — `AMSModel` and the variable-lane AMS
|
||||
rendering
|
||||
- [`StatusPanel.cpp`](../../src/slic3r/GUI/StatusPanel.cpp) — `ExtruderImage`, the toolhead selector,
|
||||
and per-toolhead temperature controls
|
||||
- [`MoonrakerPrinterAgent.cpp`](../../src/slic3r/Utils/MoonrakerPrinterAgent.cpp) — producer example
|
||||
(AMS payload and the `device.extruder` encoding)
|
||||
@@ -4094,8 +4094,6 @@ int CLI::run(int argc, char **argv)
|
||||
flush_and_exit(CLI_MIXED_FILAMENT_INVALID);
|
||||
}
|
||||
}
|
||||
if (filament_count > 0)
|
||||
resize_mixed_filament_metadata(m_print_config, size_t(filament_count), size_t(filament_count));
|
||||
|
||||
m_print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value = printer_technology;
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <gp_XY.hxx>
|
||||
#include <initializer_list>
|
||||
#include <math.h>
|
||||
|
||||
@@ -261,6 +261,8 @@ set(lisbslic3r_sources
|
||||
GCode/PreciseSeam.cpp
|
||||
GCode/PreciseSeam.hpp
|
||||
GCode/PreciseSeamInternal.hpp
|
||||
#GCodeSender.cpp
|
||||
#GCodeSender.hpp
|
||||
GCode/SmallAreaInfillFlowCompensator.cpp
|
||||
GCode/SmallAreaInfillFlowCompensator.hpp
|
||||
GCode/SpiralVase.cpp
|
||||
@@ -445,6 +447,7 @@ set(lisbslic3r_sources
|
||||
SLA/SupportTreeBuildsteps.hpp
|
||||
SLA/SupportTree.cpp
|
||||
SLA/SupportTree.hpp
|
||||
#SLA/SupportTreeIGL.cpp
|
||||
SLA/SupportTreeMesher.cpp
|
||||
SLA/SupportTreeMesher.hpp
|
||||
SlicesToTriangleMesh.cpp
|
||||
|
||||
@@ -2589,6 +2589,7 @@ void priv::create_face_types(FaceTypeMap &map,
|
||||
}
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/clip.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
|
||||
{
|
||||
CutMesh& tm = cut.mesh;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "Emboss.hpp"
|
||||
#include <optional>
|
||||
#include <stdio.h>
|
||||
#include <numeric>
|
||||
#include <cstdlib>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "TDataStd_Name.hxx"
|
||||
#include "BRepBuilderAPI_Transform.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
#include "BRepTools.hxx"
|
||||
#include <IMeshTools_Parameters.hxx>
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "Print.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "Time.hpp"
|
||||
|
||||
@@ -148,6 +148,8 @@ struct ConflictComputeResult
|
||||
|
||||
using ConflictComputeOpt = std::optional<ConflictComputeResult>;
|
||||
|
||||
using ConflictObjName = std::optional<std::pair<std::string, std::string>>;
|
||||
|
||||
struct ConflictChecker
|
||||
{
|
||||
static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional<const FakeWipeTower *> wtdptr);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <string>
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
namespace Slic3r {
|
||||
namespace PreciseSeam {
|
||||
|
||||
// Import EnforcedBlockedSeamPoint from SeamPlacerImpl namespace for convenience
|
||||
using SeamPlacerImpl::EnforcedBlockedSeamPoint;
|
||||
|
||||
// Geometry and its exterior bounds are prepared together, then treated as read-only.
|
||||
struct ModifierRegion {
|
||||
ExPolygon polygon;
|
||||
@@ -134,7 +137,7 @@ SegmentExtraction extract_perimeter_segments(const PreparedPerimeter &prepared,
|
||||
|
||||
// Result of weak modifier segment processing
|
||||
struct WeakModifierSegment {
|
||||
SeamPlacerImpl::EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
|
||||
EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
|
||||
Point left_point; // Coordinates of left (first) point of segment
|
||||
PerimeterPosition left_position; // Position on the source perimeter before insertion/refinement.
|
||||
Point right_point; // Coordinates of right (last) point of segment
|
||||
|
||||
@@ -40,7 +40,8 @@ std::string get_error_string(const ThumbnailErrors& errors);
|
||||
|
||||
|
||||
typedef std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> GCodeThumbnailDefinitionsList;
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG");
|
||||
using namespace std::literals;
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv);
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const ConfigBase &config);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
#include "GCodeSender.hpp"
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include "/usr/include/asm-generic/ioctls.h"
|
||||
|
||||
/* The following definitions are kindly borrowed from:
|
||||
/usr/include/asm-generic/termbits.h
|
||||
Unfortunately we cannot just include that one because
|
||||
it would redefine the "struct termios" already defined
|
||||
the <termios.h> already included by Boost.ASIO. */
|
||||
#define K_NCCS 19
|
||||
struct termios2 {
|
||||
tcflag_t c_iflag;
|
||||
tcflag_t c_oflag;
|
||||
tcflag_t c_cflag;
|
||||
tcflag_t c_lflag;
|
||||
cc_t c_line;
|
||||
cc_t c_cc[K_NCCS];
|
||||
speed_t c_ispeed;
|
||||
speed_t c_ospeed;
|
||||
};
|
||||
#define BOTHER CBAUDEX
|
||||
|
||||
#endif
|
||||
|
||||
//#define DEBUG_SERIAL
|
||||
#ifdef DEBUG_SERIAL
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
std::fstream fs;
|
||||
#endif
|
||||
|
||||
#define KEEP_SENT 20
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
GCodeSender::GCodeSender()
|
||||
: io(), serial(io), can_send(false), sent(0), open(false), error(false),
|
||||
connected(false), queue_paused(false)
|
||||
{
|
||||
#ifdef DEBUG_SERIAL
|
||||
std::srand(std::time(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
GCodeSender::~GCodeSender()
|
||||
{
|
||||
this->disconnect();
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
||||
{
|
||||
this->disconnect();
|
||||
|
||||
this->set_error_status(false);
|
||||
try {
|
||||
this->serial.open(devname);
|
||||
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
|
||||
this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
|
||||
this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
this->set_baud_rate(baud_rate);
|
||||
|
||||
this->serial.close();
|
||||
this->serial.open(devname);
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
|
||||
// set baud rate again because set_option overwrote it
|
||||
this->set_baud_rate(baud_rate);
|
||||
this->open = true;
|
||||
this->reset();
|
||||
} catch (boost::system::system_error &) {
|
||||
this->set_error_status(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// a reset firmware expect line numbers to start again from 1
|
||||
this->sent = 0;
|
||||
this->last_sent.clear();
|
||||
|
||||
/* Initialize debugger */
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs.open("serial.txt", std::fstream::out | std::fstream::trunc);
|
||||
#endif
|
||||
|
||||
// this gives some work to the io_service before it is started
|
||||
// (post() runs the supplied function in its thread)
|
||||
this->io.post(boost::bind(&GCodeSender::do_read, this));
|
||||
|
||||
// start reading in the background thread
|
||||
boost::thread t(boost::bind(&boost::asio::io_service::run, &this->io));
|
||||
this->background_thread.swap(t);
|
||||
|
||||
// always send a M105 to check for connection because firmware might be silent on connect
|
||||
//FIXME Vojtech: This is being sent too early, leading to line number synchronization issues,
|
||||
// from which the GCodeSender never recovers.
|
||||
// this->send("M105", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_baud_rate(unsigned int baud_rate)
|
||||
{
|
||||
try {
|
||||
// This does not support speeds > 115200
|
||||
this->serial.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
|
||||
} catch (boost::system::system_error &) {
|
||||
boost::asio::serial_port::native_handle_type handle = this->serial.native_handle();
|
||||
|
||||
#if __APPLE__
|
||||
termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
::cfsetspeed(&ios, baud_rate);
|
||||
speed_t newSpeed = baud_rate;
|
||||
ioctl(handle, IOSSIOSPEED, &newSpeed);
|
||||
::tcsetattr(handle, TCSANOW, &ios);
|
||||
#elif __linux__
|
||||
termios2 ios;
|
||||
if (ioctl(handle, TCGETS2, &ios))
|
||||
printf("Error in TCGETS2: %s\n", strerror(errno));
|
||||
ios.c_ispeed = ios.c_ospeed = baud_rate;
|
||||
ios.c_cflag &= ~CBAUD;
|
||||
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
|
||||
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
|
||||
ios.c_cc[VTIME] = 1;
|
||||
if (ioctl(handle, TCSETS2, &ios))
|
||||
printf("Error in TCSETS2: %s\n", strerror(errno));
|
||||
|
||||
#elif __OpenBSD__
|
||||
struct termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
::cfsetspeed(&ios, baud_rate);
|
||||
if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
|
||||
printf("Failed to set baud rate: %s\n", strerror(errno));
|
||||
#else
|
||||
//throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::disconnect()
|
||||
{
|
||||
if (!this->open) return;
|
||||
this->open = false;
|
||||
this->connected = false;
|
||||
this->io.post(boost::bind(&GCodeSender::do_close, this));
|
||||
this->background_thread.join();
|
||||
this->io.reset();
|
||||
/*
|
||||
if (this->error_status()) {
|
||||
throw(boost::system::system_error(boost::system::error_code(),
|
||||
"Error while closing the device"));
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "DISCONNECTED" << std::endl << std::flush;
|
||||
fs.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::is_connected() const
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::wait_connected(unsigned int timeout) const
|
||||
{
|
||||
using namespace boost::posix_time;
|
||||
ptime t0 = second_clock::local_time() + seconds(timeout);
|
||||
while (!this->connected) {
|
||||
if (second_clock::local_time() > t0) return false;
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
GCodeSender::queue_size() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
return this->queue.size();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::pause_queue()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->queue_paused = true;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::resume_queue()
|
||||
{
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->queue_paused = false;
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::purge_queue(bool priority)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
if (priority) {
|
||||
// clear priority queue
|
||||
std::list<std::string> empty;
|
||||
std::swap(this->priqueue, empty);
|
||||
} else {
|
||||
// clear queue
|
||||
std::queue<std::string> empty;
|
||||
std::swap(this->queue, empty);
|
||||
this->queue_paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
// purge log and return its contents
|
||||
std::vector<std::string>
|
||||
GCodeSender::purge_log()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
std::vector<std::string> retval;
|
||||
retval.reserve(this->log.size());
|
||||
while (!this->log.empty()) {
|
||||
retval.push_back(this->log.front());
|
||||
this->log.pop();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCodeSender::getT() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
return this->T;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCodeSender::getB() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
return this->B;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_close()
|
||||
{
|
||||
this->set_error_status(false);
|
||||
boost::system::error_code ec;
|
||||
this->serial.cancel(ec);
|
||||
if (ec) this->set_error_status(true);
|
||||
this->serial.close(ec);
|
||||
if (ec) this->set_error_status(true);
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_error_status(bool e)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||
this->error = e;
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::error_status() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||
return this->error;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_read()
|
||||
{
|
||||
// read one line
|
||||
boost::asio::async_read_until(
|
||||
this->serial,
|
||||
this->read_buffer,
|
||||
'\n',
|
||||
boost::bind(
|
||||
&GCodeSender::on_read,
|
||||
this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::on_read(const boost::system::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
#ifdef __APPLE__
|
||||
if (error.value() == 45) {
|
||||
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
|
||||
this->do_read();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
||||
// error can be true even because the serial port was closed.
|
||||
// In this case it is not a real error, so ignore.
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::istream is(&this->read_buffer);
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
if (!line.empty()) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "<< " << line << std::endl << std::flush;
|
||||
#endif
|
||||
|
||||
// note that line might contain \r at its end
|
||||
// parse incoming line
|
||||
if (!this->connected
|
||||
&& (boost::starts_with(line, "start")
|
||||
|| boost::starts_with(line, "Grbl ")
|
||||
|| boost::starts_with(line, "ok")
|
||||
|| boost::contains(line, "T:"))) {
|
||||
this->connected = true;
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else if (boost::starts_with(line, "ok")) {
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else if (boost::istarts_with(line, "resend") // Marlin uses "Resend: "
|
||||
|| boost::istarts_with(line, "rs")) {
|
||||
// extract the first number from line
|
||||
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
||||
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl;
|
||||
#endif
|
||||
|
||||
if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) {
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
|
||||
const auto lines_to_resend = this->sent - toresend + 1;
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "!! resending " << lines_to_resend << " lines" << std::endl;
|
||||
#endif
|
||||
// move the unsent lines to priqueue
|
||||
this->priqueue.insert(
|
||||
this->priqueue.begin(), // insert at the beginning
|
||||
this->last_sent.begin() + this->last_sent.size() - lines_to_resend,
|
||||
this->last_sent.end()
|
||||
);
|
||||
|
||||
// we can empty last_sent because it's not useful anymore
|
||||
this->last_sent.clear();
|
||||
|
||||
// start resending with the requested line number
|
||||
this->sent = toresend - 1;
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else {
|
||||
printf("Cannot resend %zu (oldest we have is %zu)\n", toresend, this->sent - this->last_sent.size());
|
||||
}
|
||||
} else if (boost::starts_with(line, "wait")) {
|
||||
// ignore
|
||||
} else {
|
||||
// push any other line into the log
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
this->log.push(line);
|
||||
}
|
||||
|
||||
// parse temperature info
|
||||
{
|
||||
size_t pos = line.find("T:");
|
||||
if (pos != std::string::npos && line.size() > pos + 2) {
|
||||
// we got temperature info
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
this->T = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
|
||||
|
||||
pos = line.find("B:");
|
||||
if (pos != std::string::npos && line.size() > pos + 2) {
|
||||
// we got bed temperature info
|
||||
this->B = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->do_read();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send(const std::vector<std::string> &lines, bool priority)
|
||||
{
|
||||
// append lines to queue
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
for (std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||
if (priority) {
|
||||
this->priqueue.push_back(*line);
|
||||
} else {
|
||||
this->queue.push(*line);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send(const std::string &line, bool priority)
|
||||
{
|
||||
// append line to queue
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
if (priority) {
|
||||
this->priqueue.push_back(line);
|
||||
} else {
|
||||
this->queue.push(line);
|
||||
}
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send()
|
||||
{
|
||||
this->io.post(boost::bind(&GCodeSender::do_send, this));
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_send()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
|
||||
// printer is not connected or we're still waiting for the previous ack
|
||||
if (!this->can_send) return;
|
||||
|
||||
std::string line;
|
||||
while (!this->priqueue.empty() || (!this->queue.empty() && !this->queue_paused)) {
|
||||
if (!this->priqueue.empty()) {
|
||||
line = this->priqueue.front();
|
||||
this->priqueue.pop_front();
|
||||
} else {
|
||||
line = this->queue.front();
|
||||
this->queue.pop();
|
||||
}
|
||||
|
||||
// strip comments
|
||||
size_t comment_pos = line.find_first_of(';');
|
||||
if (comment_pos != std::string::npos)
|
||||
line.erase(comment_pos, std::string::npos);
|
||||
boost::algorithm::trim(line);
|
||||
|
||||
// if line is not empty, send it
|
||||
if (!line.empty()) break;
|
||||
// if line is empty, process next item in queue
|
||||
}
|
||||
if (line.empty()) return;
|
||||
|
||||
// compute full line
|
||||
++ this->sent;
|
||||
#ifndef DEBUG_SERIAL
|
||||
const auto line_num = this->sent;
|
||||
#else
|
||||
// In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time
|
||||
const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent;
|
||||
#endif
|
||||
std::string full_line = "N" + boost::lexical_cast<std::string>(line_num) + " " + line;
|
||||
|
||||
// calculate checksum
|
||||
int cs = 0;
|
||||
for (std::string::const_iterator it = full_line.begin(); it != full_line.end(); ++it)
|
||||
cs = cs ^ *it;
|
||||
|
||||
// write line to device
|
||||
full_line += "*";
|
||||
full_line += boost::lexical_cast<std::string>(cs);
|
||||
full_line += "\n";
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << ">> " << full_line << std::flush;
|
||||
#endif
|
||||
|
||||
this->last_sent.push_back(line);
|
||||
this->can_send = false;
|
||||
|
||||
while (this->last_sent.size() > KEEP_SENT) {
|
||||
this->last_sent.pop_front();
|
||||
}
|
||||
|
||||
// we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the
|
||||
// stack and the buffer would lose its underlying storage causing memory corruption
|
||||
std::ostream os(&this->write_buffer);
|
||||
os << full_line;
|
||||
boost::asio::async_write(this->serial, this->write_buffer, boost::bind(&GCodeSender::on_write, this, boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::on_write(const boost::system::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this->do_send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_DTR(bool on)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__SYMBIAN32__)
|
||||
boost::asio::serial_port_service::native_handle_type handle = this->serial.native_handle();
|
||||
if (on)
|
||||
EscapeCommFunction(handle, SETDTR);
|
||||
else
|
||||
EscapeCommFunction(handle, CLRDTR);
|
||||
#else
|
||||
int fd = this->serial.native_handle();
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
if (on)
|
||||
status |= TIOCM_DTR;
|
||||
else
|
||||
status &= ~TIOCM_DTR;
|
||||
ioctl(fd, TIOCMSET, &status);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::reset()
|
||||
{
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef slic3r_GCodeSender_hpp_
|
||||
#define slic3r_GCodeSender_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include <queue>
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <cstddef>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
class GCodeSender : private boost::noncopyable {
|
||||
public:
|
||||
GCodeSender();
|
||||
~GCodeSender();
|
||||
bool connect(std::string devname, unsigned int baud_rate);
|
||||
void send(const std::vector<std::string> &lines, bool priority = false);
|
||||
void send(const std::string &s, bool priority = false);
|
||||
void disconnect();
|
||||
bool error_status() const;
|
||||
bool is_connected() const;
|
||||
bool wait_connected(unsigned int timeout = 3) const;
|
||||
size_t queue_size() const;
|
||||
void pause_queue();
|
||||
void resume_queue();
|
||||
void purge_queue(bool priority = false);
|
||||
std::vector<std::string> purge_log();
|
||||
std::string getT() const;
|
||||
std::string getB() const;
|
||||
void set_DTR(bool on);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
asio::io_service io;
|
||||
asio::serial_port serial;
|
||||
boost::thread background_thread;
|
||||
boost::asio::streambuf read_buffer, write_buffer;
|
||||
bool open; // whether the serial socket is connected
|
||||
bool connected; // whether the printer is online
|
||||
bool error;
|
||||
mutable boost::mutex error_mutex;
|
||||
|
||||
// this mutex guards queue, priqueue, can_send, queue_paused, sent, last_sent
|
||||
mutable boost::mutex queue_mutex;
|
||||
std::queue<std::string> queue;
|
||||
std::list<std::string> priqueue;
|
||||
bool can_send;
|
||||
bool queue_paused;
|
||||
size_t sent;
|
||||
std::deque<std::string> last_sent;
|
||||
|
||||
// this mutex guards log, T, B
|
||||
mutable boost::mutex log_mutex;
|
||||
std::queue<std::string> log;
|
||||
std::string T, B;
|
||||
|
||||
void set_baud_rate(unsigned int baud_rate);
|
||||
void set_error_status(bool e);
|
||||
void do_send();
|
||||
void on_write(const boost::system::error_code& error, size_t bytes_transferred);
|
||||
void do_close();
|
||||
void do_read();
|
||||
void on_read(const boost::system::error_code& error, size_t bytes_transferred);
|
||||
void send();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GCodeSender_hpp_ */
|
||||
@@ -94,6 +94,8 @@ struct Circle {
|
||||
|
||||
using Circlef = Circle<Vec2f>;
|
||||
using Circled = Circle<Vec2d>;
|
||||
using CircleSqf = CircleSq<Vec2f>;
|
||||
using CircleSqd = CircleSq<Vec2d>;
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "MedialAxis.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
@@ -154,12 +154,13 @@ template<class ExecutionPolicy, class Enable = void> struct _Loop
|
||||
};
|
||||
|
||||
// Add Specialization for using ExecutionTBB for parallel loops.
|
||||
template<> struct _Loop<Slic3r::ExecutionTBB>
|
||||
using namespace Slic3r;
|
||||
template<> struct _Loop<ExecutionTBB>
|
||||
{
|
||||
template<class It, class Fn> static void for_each_idx(It from, It to, Fn&& fn)
|
||||
{
|
||||
Slic3r::execution::for_each(
|
||||
Slic3r::ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, Slic3r::execution::max_concurrency(Slic3r::ex_tbb));
|
||||
execution::for_each(
|
||||
ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, execution::max_concurrency(ex_tbb));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Format/AssimpImport.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Arrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "calib.hpp"
|
||||
#include "enum_bitmask.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
#include "EmbossShape.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
|
||||
@@ -3593,6 +3593,24 @@ void PresetBundle::export_selections(AppConfig &config)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": printer %1%, print %2%, filaments[0] %3% ")%printers.get_selected_preset_name() % prints.get_selected_preset_name() %filament_presets[0];
|
||||
}
|
||||
|
||||
// Preserve metadata only for existing colour slots; new slots get false/empty defaults.
|
||||
static void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
|
||||
{
|
||||
auto resize = [old_slot_count, new_slot_count](auto *opt) {
|
||||
if (opt) {
|
||||
opt->values.resize(std::min(old_slot_count, opt->values.size()));
|
||||
opt->values.resize(new_slot_count);
|
||||
}
|
||||
};
|
||||
resize(config.option<ConfigOptionBools>("filament_is_mixed"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_components"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios"));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve"));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part"));
|
||||
}
|
||||
|
||||
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
|
||||
{
|
||||
unsigned old_filament_count = this->filament_presets.size();
|
||||
|
||||
@@ -10845,21 +10845,6 @@ void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<cons
|
||||
}
|
||||
}
|
||||
|
||||
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
|
||||
{
|
||||
auto resize = [old_slot_count, new_slot_count](auto *opt) {
|
||||
opt->values.resize(std::min(old_slot_count, opt->values.size()));
|
||||
opt->values.resize(new_slot_count);
|
||||
};
|
||||
resize(config.option<ConfigOptionBools>("filament_is_mixed", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_components", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios", true));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve", true));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part", true));
|
||||
}
|
||||
|
||||
|
||||
//used for object/region config
|
||||
//use the smallest of multiple to single
|
||||
|
||||
@@ -933,10 +933,6 @@ extern std::set<std::string> filament_dev_options;
|
||||
// filament_configs, one config per filament in slot order, as the filaments' values one after another.
|
||||
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs);
|
||||
|
||||
// Orca: sizes the per-slot mixed-colour metadata options to new_slot_count, keeping the first
|
||||
// old_slot_count values; an option the config lacks is created.
|
||||
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count);
|
||||
|
||||
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 compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
|
||||
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_map_indices);
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
using TColor = typename PixelRenderer::color_type;
|
||||
using TValue = typename TColor::value_type;
|
||||
using TPixel = typename PixelRenderer::pixel_type;
|
||||
using TRawBuffer = agg::rendering_buffer;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "TDataStd_Name.hxx"
|
||||
#include "BRepBuilderAPI_Transform.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
#include "Font_BRepFont.hxx"
|
||||
#include "Font_BRepTextBuilder.hxx"
|
||||
|
||||
@@ -17,6 +17,9 @@ typedef std::vector<Color> ColorList;
|
||||
typedef std::array<double, 3> ColorDouble;
|
||||
typedef std::array<std::size_t, 3> RGB;
|
||||
|
||||
// Function pointer type that points to a specific color-difference function based on the chosen method.
|
||||
using DistanceFunction = double (*)(const Color&, const Color&);
|
||||
|
||||
// Color space used for computing color differences.
|
||||
enum struct ColorDifferenceMethod : std::size_t {
|
||||
RGB = 0, // Simplest and fastest
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
namespace Slic3r { namespace tex2color {
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
// Default upper bound on the number of half-edges in any single boundary cycle
|
||||
// that CloseBoundariesAndRepairManifoldness will attempt to triangulate. The
|
||||
// cost of triangulate_hole grows non-linearly with cycle length, so this caps
|
||||
@@ -59,7 +61,6 @@ struct BoundaryEdgeStats
|
||||
// any pre-processing (e.g. stitch_borders) needed for the count to be meaningful.
|
||||
inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cgal_mesh)
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using CGALMesh = cgalutils::CGALMesh;
|
||||
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
|
||||
|
||||
@@ -86,7 +87,6 @@ inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cga
|
||||
// boundary statistics; entering this function always triggers triangulation.
|
||||
inline void CloseBoundariesAndRepairManifoldness(cgalutils::CGALMesh& cgal_mesh)
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using CGALMesh = cgalutils::CGALMesh;
|
||||
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
|
||||
using FaceDescriptor = boost::graph_traits<CGALMesh>::face_descriptor;
|
||||
@@ -112,7 +112,6 @@ inline bool RepairMesh(const TriMesh& mesh,
|
||||
AlgoCancelCallback cancel_callback = nullptr,
|
||||
const RepairSetting& setting = RepairSetting{})
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
auto elapsed_ms = [](Clock::time_point t0) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - t0).count();
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
#include <regex>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@@ -432,6 +432,8 @@ inline IntegerOnly<I, I> fast_round_up(double a)
|
||||
return a == 0.49999999999999994 ? I(0) : I(floor(a + 0.5));
|
||||
}
|
||||
|
||||
template<class T> using SamePair = std::pair<T, T>;
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
@@ -19,17 +19,20 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
@@ -35,12 +36,14 @@
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/hyperlink.h>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/hyperlink.h>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <wx/string.h>
|
||||
#include <wx/toplevel.h>
|
||||
#include <wx/timer.h>
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "CapsuleButton.hpp"
|
||||
|
||||
namespace Slic3r::GUI { struct IntEvent; }
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// #include "libslic3r/GCodeSender.hpp"
|
||||
#include "ConfigManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
@@ -163,8 +163,8 @@ public:
|
||||
~devPrinterUtil() = delete;
|
||||
|
||||
public:
|
||||
static bool IsVirtualSlot(int ams_id) { return (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID);}
|
||||
static bool IsVirtualSlot(const std::string& ams_id) { return (ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR); }
|
||||
static bool IsVirtualSlot(int ams_id);
|
||||
static bool IsVirtualSlot(const std::string& ams_id);
|
||||
};
|
||||
|
||||
namespace GUI
|
||||
@@ -198,3 +198,6 @@ template<> struct std::hash<NozzleDef>
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||
};
|
||||
};
|
||||
|
||||
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
|
||||
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
@@ -33,6 +34,25 @@
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool devPrinterUtil::IsVirtualSlot(int ams_id)
|
||||
{
|
||||
if (wxTheApp == nullptr || GUI::wxGetApp().preset_bundle == nullptr || GUI::wxGetApp().preset_bundle->is_bbl_vendor())
|
||||
return ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID;
|
||||
|
||||
const int extruder_count = GUI::wxGetApp().preset_bundle->get_printer_extruder_count();
|
||||
return ams_id >= VIRTUAL_TRAY_MAIN_ID - extruder_count + 1 && ams_id <= VIRTUAL_TRAY_MAIN_ID;
|
||||
}
|
||||
|
||||
bool devPrinterUtil::IsVirtualSlot(const std::string& ams_id)
|
||||
{
|
||||
try {
|
||||
return IsVirtualSlot(std::stoi(ams_id));
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int _hex_digit_to_int(const char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1; }
|
||||
|
||||
wxColour DevAmsTray::decode_color(const std::string &color)
|
||||
@@ -200,18 +220,21 @@ wxString DevAms::GetDisplayName() const
|
||||
|
||||
int DevAms::GetSlotCount() const
|
||||
{
|
||||
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
|
||||
auto ams_type = GetAmsType();
|
||||
if (ams_type == AMS || ams_type == AMS_LITE || ams_type == N3F)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if (ams_type == N3S)
|
||||
{
|
||||
return 1;
|
||||
if (wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
|
||||
GUI::wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
|
||||
auto ams_type = GetAmsType();
|
||||
if (ams_type == AMS || ams_type == AMS_LITE || ams_type == N3F)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if (ams_type == N3S)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return static_cast<int>(m_trays.size());
|
||||
}
|
||||
|
||||
DevAmsTray* DevAms::GetTray(const std::string& tray_id) const
|
||||
@@ -302,13 +325,21 @@ int DevFilaSystem::GetExtruderIdByAmsId(const std::string& ams_id) const
|
||||
{
|
||||
return it->second->GetExtruderId();
|
||||
}
|
||||
else if (stoi(ams_id) == VIRTUAL_TRAY_MAIN_ID)
|
||||
{
|
||||
return MAIN_EXTRUDER_ID;
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (!is_bbl_vendor && GetOwner()) {
|
||||
for (const auto& tray : GetOwner()->vt_slot) {
|
||||
if (tray.id == ams_id)
|
||||
return VIRTUAL_TRAY_MAIN_ID - std::stoi(ams_id);
|
||||
}
|
||||
}
|
||||
else if (stoi(ams_id) == VIRTUAL_TRAY_DEPUTY_ID)
|
||||
{
|
||||
|
||||
if (is_bbl_vendor && ams_id == VIRTUAL_AMS_MAIN_ID_STR)
|
||||
return MAIN_EXTRUDER_ID;
|
||||
if (is_bbl_vendor && ams_id == VIRTUAL_AMS_DEPUTY_ID_STR)
|
||||
return DEPUTY_EXTRUDER_ID;
|
||||
if (!is_bbl_vendor && devPrinterUtil::IsVirtualSlot(ams_id)) {
|
||||
return VIRTUAL_TRAY_MAIN_ID - std::stoi(ams_id);
|
||||
}
|
||||
|
||||
assert(false && __FUNCTION__);
|
||||
@@ -325,9 +356,22 @@ std::string DevFilaSystem::GetNozzleFlowStringByAmsId(const std::string& ams_id)
|
||||
std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
|
||||
{
|
||||
std::map<int, DevAmsSlotId> tray_id_map;
|
||||
tray_id_map[VIRTUAL_TRAY_MAIN_ID] = DevAmsSlotId{VIRTUAL_TRAY_MAIN_ID, 0};
|
||||
tray_id_map[VIRTUAL_TRAY_DEPUTY_ID] = DevAmsSlotId{VIRTUAL_TRAY_DEPUTY_ID, 0};
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (is_bbl_vendor) {
|
||||
tray_id_map[VIRTUAL_TRAY_MAIN_ID] = DevAmsSlotId{VIRTUAL_TRAY_MAIN_ID, 0};
|
||||
tray_id_map[VIRTUAL_TRAY_DEPUTY_ID] = DevAmsSlotId{VIRTUAL_TRAY_DEPUTY_ID, 0};
|
||||
} else if (GetOwner()) {
|
||||
for (const auto& tray : GetOwner()->vt_slot) {
|
||||
try {
|
||||
const int tray_id = std::stoi(tray.id);
|
||||
tray_id_map[tray_id] = DevAmsSlotId{tray_id, 0};
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " invalid virtual tray id: " << tray.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int generic_tray_index = 0;
|
||||
for (auto& [ams_id, ams_item] : GetAmsList()) {
|
||||
for (auto &[slot_id, slot_item] : ams_item->GetTrays()) {
|
||||
if (ams_item && slot_item) {
|
||||
@@ -335,9 +379,11 @@ std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
|
||||
int ams_id_int = stoi(ams_id);
|
||||
int slot_id_int = stoi(slot_id);
|
||||
int tray_index = -1;
|
||||
if (ams_item->GetAmsType() == DevAms::N3S) {
|
||||
if (!is_bbl_vendor) {
|
||||
tray_index = generic_tray_index + slot_id_int;
|
||||
} else if (ams_item->GetAmsType() == DevAms::N3S) {
|
||||
tray_index = ams_id_int;
|
||||
} else if(ams_item->GetAmsType() == DevAms::AMS_LITE && ams_item->IsAmsLiteMixed()) {
|
||||
} else if (ams_item->GetAmsType() == DevAms::AMS_LITE && ams_item->IsAmsLiteMixed()) {
|
||||
tray_index = 24 + slot_id_int;
|
||||
} else {
|
||||
tray_index = (ams_id_int * 4 + slot_id_int);
|
||||
@@ -348,6 +394,8 @@ std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_bbl_vendor)
|
||||
generic_tray_index += static_cast<int>(ams_item->GetTrays().size());
|
||||
}
|
||||
|
||||
return tray_id_map;
|
||||
@@ -913,6 +961,8 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
|
||||
}
|
||||
|
||||
ams->m_exist = true;
|
||||
ams->m_binded_extruder_set = {ext_id};
|
||||
ams->m_binded_switcher_pos.reset();
|
||||
ams->m_current_temperature = u.value("temperature", (float) INVALID_AMS_TEMPERATURE);
|
||||
ams->m_humidity_percent = u.value("humidity_percent", -1);
|
||||
ams->m_left_dry_time = u.value("dry_time_min", 0);
|
||||
@@ -995,16 +1045,22 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
|
||||
}
|
||||
|
||||
// --- external / direct spools -> obj->vt_slot ---
|
||||
// extruder 0 -> main virtual slot, extruder >0 -> deputy.
|
||||
// Keep one virtual slot per extruder. The legacy two-extruder mapping uses
|
||||
// 255 for extruder 0 and 254 for extruder 1; continue that sequence for
|
||||
// additional extruders (253, 252, ...).
|
||||
if (obj && data.contains("external") && data["external"].is_array())
|
||||
{
|
||||
std::map<int, DevAmsTray> external_slots;
|
||||
obj->vt_slot.clear();
|
||||
for (const auto& e : data["external"])
|
||||
{
|
||||
if (!e.is_object())
|
||||
continue;
|
||||
const int ext = e.value("extruder", MAIN_EXTRUDER_ID);
|
||||
const int vt_id = (ext == MAIN_EXTRUDER_ID) ? VIRTUAL_TRAY_MAIN_ID : VIRTUAL_TRAY_DEPUTY_ID;
|
||||
if (ext < MAIN_EXTRUDER_ID || ext > VIRTUAL_TRAY_MAIN_ID)
|
||||
continue;
|
||||
|
||||
const int vt_id = VIRTUAL_TRAY_MAIN_ID - ext;
|
||||
DevAmsTray tray(std::to_string(vt_id));
|
||||
tray.is_exists = e.value("loaded", false);
|
||||
tray.m_fila_type = e.value("material", std::string());
|
||||
@@ -1013,8 +1069,11 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
|
||||
tray.nozzle_temp_min = std::to_string(e.value("nozzle_temp_min", 0));
|
||||
tray.nozzle_temp_max = std::to_string(e.value("nozzle_temp_max", 0));
|
||||
tray.remain = e.value("remain_percent", -1);
|
||||
obj->vt_slot.push_back(tray);
|
||||
external_slots.insert_or_assign(ext, std::move(tray));
|
||||
}
|
||||
|
||||
for (auto& entry : external_slots)
|
||||
obj->vt_slot.push_back(std::move(entry.second));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
#include <wx/colour.h>
|
||||
|
||||
@@ -348,7 +349,7 @@ public:
|
||||
DevAmsTray* GetAmsTray(const std::string& ams_id, const std::string& tray_id) const;
|
||||
void CollectAmsColors(std::vector<wxColour>& ams_colors) const;
|
||||
|
||||
// Map a linear tray index -> {ams_id, slot_id}. Includes the two virtual (external-spool) trays,
|
||||
// Map a linear tray index -> {ams_id, slot_id}. Includes virtual external-spool trays,
|
||||
// N3S single-slot units, and the A2L/N9 AMS-Lite-mixed layout (trays 24-27).
|
||||
std::map<int, DevAmsSlotId> GetTrayIndexMap();
|
||||
|
||||
@@ -433,4 +434,4 @@ struct DevFilamentDryingPreset
|
||||
float filament_dev_ams_drying_heat_distortion_temperature = 0.0f;
|
||||
};
|
||||
|
||||
}// namespace Slic3r
|
||||
}// namespace Slic3r
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "slic3r/GUI/DeviceCore/DevDefs.h"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/string.h>
|
||||
#include <string>
|
||||
#include "libslic3r/CommonDefs.hpp"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <cmath>
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -176,34 +178,52 @@ DevFirmwareVersionInfo DevNozzle::GetFirmwareInfo() const
|
||||
int DevNozzle::GetLogicExtruderId() const
|
||||
{
|
||||
int total_ext_count = GetTotalExtruderCount();
|
||||
if (total_ext_count == 1) {
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtLeftExtruder()) {
|
||||
return LOGIC_L_EXTRUDER_ID;
|
||||
} else if (AtRightExtruder()) {
|
||||
return LOGIC_R_EXTRUDER_ID;
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
|
||||
GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (is_bbl_vendor) {
|
||||
if (total_ext_count == 1) {
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtLeftExtruder()) {
|
||||
return LOGIC_L_EXTRUDER_ID;
|
||||
} else if (AtRightExtruder()) {
|
||||
return LOGIC_R_EXTRUDER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
// For some reason, BBL's logical extruder ID is inverted:
|
||||
// physical extruder id = 0 (MAIN_EXTRUDER_ID) vs logical extruder id = 1 (LOGIC_R_EXTRUDER_ID)
|
||||
// Likely because logical id reads from left to right (left = 0, right = 1)
|
||||
// For generic N extruders, this inversion does not apply.
|
||||
if (IsOnRack()) return INVALID_EXTRUDER_ID;
|
||||
return m_nozzle_id;
|
||||
}
|
||||
|
||||
int DevNozzle::GetExtruderId() const
|
||||
{
|
||||
int total_ext_count = GetTotalExtruderCount();
|
||||
if (total_ext_count == 1) {
|
||||
return MAIN_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtRightExtruder()) {
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
|
||||
GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (is_bbl_vendor) {
|
||||
int total_ext_count = GetTotalExtruderCount();
|
||||
if (total_ext_count == 1) {
|
||||
return MAIN_EXTRUDER_ID;
|
||||
} else if (AtLeftExtruder()) {
|
||||
return DEPUTY_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtRightExtruder()) {
|
||||
return MAIN_EXTRUDER_ID;
|
||||
} else if (AtLeftExtruder()) {
|
||||
return DEPUTY_EXTRUDER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
return MAIN_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
return MAIN_EXTRUDER_ID;
|
||||
return m_nozzle_id;
|
||||
}
|
||||
|
||||
bool DevNozzle::AtLeftExtruder() const
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "ReleaseNote.hpp"
|
||||
#include <string>
|
||||
#include <boost/log/trivial.hpp>
|
||||
@@ -64,6 +65,7 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
#include <tuple>
|
||||
#include <wx/dir.h>
|
||||
@@ -1273,19 +1275,24 @@ int MachineObject::get_bed_temperature_limit()
|
||||
|
||||
bool MachineObject::is_filament_installed()
|
||||
{
|
||||
if (m_extder_system->GetTotalExtderCount() > 0) {
|
||||
// right//or single
|
||||
auto ext = m_extder_system->m_extders[MAIN_EXTRUDER_ID];
|
||||
if (ext.m_ext_has_filament) {
|
||||
// if (m_extder_system->GetTotalExtderCount() > 0) {
|
||||
// // right//or single
|
||||
// auto ext = m_extder_system->m_extders[MAIN_EXTRUDER_ID];
|
||||
// if (ext.m_ext_has_filament) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// /*left*/
|
||||
// if (m_extder_system->GetTotalExtderCount() > 1) {
|
||||
// auto ext = m_extder_system->m_extders[DEPUTY_EXTRUDER_ID];
|
||||
// if (ext.m_ext_has_filament) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
for (auto& ext : m_extder_system->m_extders) {
|
||||
if (ext.m_ext_has_filament)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*left*/
|
||||
if (m_extder_system->GetTotalExtderCount() > 1) {
|
||||
auto ext = m_extder_system->m_extders[DEPUTY_EXTRUDER_ID];
|
||||
if (ext.m_ext_has_filament) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -5262,16 +5269,17 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
|
||||
|
||||
bool MachineObject::contains_tray(const std::string &ams_id, const std::string &tray_id) const
|
||||
{
|
||||
if (ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
|
||||
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (is_bbl_vendor && ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR)
|
||||
return m_fila_system->GetAmsTray(ams_id, tray_id) != nullptr;
|
||||
} else {
|
||||
|
||||
if (!is_bbl_vendor || ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR) {
|
||||
for (const auto& tray : vt_slot) {
|
||||
if (tray.id == ams_id) { return true; }
|
||||
if (tray.id == ams_id) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return !is_bbl_vendor && m_fila_system->GetAmsTray(ams_id, tray_id) != nullptr;
|
||||
}
|
||||
|
||||
DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string &tray_id) const
|
||||
@@ -5281,13 +5289,19 @@ DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string
|
||||
return DevAmsTray(tray_id);
|
||||
}
|
||||
|
||||
if (ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
|
||||
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (is_bbl_vendor && ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
|
||||
auto tray = m_fila_system->GetAmsTray(ams_id, tray_id);
|
||||
if (tray) { return *tray;};
|
||||
if (tray) return *tray;
|
||||
}
|
||||
else {
|
||||
else if (!is_bbl_vendor || ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR) {
|
||||
for (const auto &tray : vt_slot) {
|
||||
if (tray.id == ams_id) { return tray; }
|
||||
if (tray.id == ams_id) return tray;
|
||||
}
|
||||
|
||||
if (!is_bbl_vendor) {
|
||||
auto tray = m_fila_system->GetAmsTray(ams_id, tray_id);
|
||||
if (tray) return *tray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2427,15 +2427,11 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (show_bed) {
|
||||
bool show_axes = m_show_world_axes;
|
||||
#ifdef SLIC3R_CAD
|
||||
// Design tab: while its reference planes are up they draw their own axes from the modeling
|
||||
// origin, where the bed's triad would otherwise sit on top of them.
|
||||
show_axes = show_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes());
|
||||
#endif
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
|
||||
}
|
||||
// Design tab: while its reference planes are up they draw their own axes from the modeling
|
||||
// origin, where the bed's triad would otherwise sit on top of them.
|
||||
if (show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(),
|
||||
m_show_world_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes()));
|
||||
m_frame_profiler.mark("bed");
|
||||
if (show_bed) //BBS: add outline logic
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
|
||||
@@ -183,10 +183,13 @@ wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_NAME_CHANGE, SimpleEvent);
|
||||
//BBS: declare EVT_GLCANVAS_PLATE_SELECT
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_SELECT, SimpleEvent);
|
||||
|
||||
using Vec2dEvent = Event<Vec2d>;
|
||||
// _bool_ value is used as a indicator of selection in the 3DScene
|
||||
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
|
||||
using RBtnPlateEvent = Event<std::pair<Vec2d, int>>;
|
||||
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
||||
|
||||
using Vec3dEvent = Event<Vec3d>;
|
||||
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
||||
|
||||
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/dcmemory.h>
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "AboutDialog.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include "WebUserLoginDialog.hpp"
|
||||
|
||||
|
||||
@@ -172,10 +172,12 @@
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/InstanceLock.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "slic3r/plugin/host/PluginHostUi.hpp"
|
||||
#include "slic3r/plugin/PythonInterpreter.hpp"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include <nanosvg/nanosvg.h>
|
||||
#include <nanosvg/nanosvgrast.h>
|
||||
#include "OpenGLManager.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Notebook.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
#include "MarkdownTip.hpp"
|
||||
#include "NetworkTestDialog.hpp"
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "wx/evtloop.h"
|
||||
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include "MediaPlayCtrl.h"
|
||||
#include "MediaFilePanel.h"
|
||||
#include "Plater.hpp"
|
||||
#include "BindDialog.hpp"
|
||||
|
||||
#include "DeviceCore/DevManager.h"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
@@ -30,12 +31,14 @@
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r_version.h"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
@@ -54,6 +54,7 @@ struct Option {
|
||||
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
|
||||
opt(_opt), opt_id(id) {}
|
||||
};
|
||||
using t_option = std::unique_ptr<Option>; //!
|
||||
|
||||
/// Represents option lines
|
||||
class Line : public UndoValueUIManager
|
||||
|
||||
@@ -108,6 +108,7 @@ class PlaterPresetComboBox;
|
||||
class PartPlateList;
|
||||
class SyncNozzleAndAmsDialog;
|
||||
class FinishSyncAmsDialog;
|
||||
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
||||
|
||||
class Plater;
|
||||
enum class ActionButtonType : int;
|
||||
@@ -324,6 +325,7 @@ private:
|
||||
class Plater: public wxPanel
|
||||
{
|
||||
public:
|
||||
using fs_path = boost::filesystem::path;
|
||||
|
||||
Plater(wxWindow *parent, MainFrame *main_frame);
|
||||
Plater(Plater &&) = delete;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include <miniz.h>
|
||||
#include <OrcaCloudServiceAgent.hpp>
|
||||
#include <wx/event.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
#include "libslic3r_version.h"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -31,8 +32,10 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/richtext/richtextctrl.h>
|
||||
|
||||
#include "AmsMappingPopup.hpp"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -37,6 +38,7 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -32,6 +33,7 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
+746
-255
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
#ifndef slic3r_StatusPanel_hpp_
|
||||
#define slic3r_StatusPanel_hpp_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "MonitorPage.hpp"
|
||||
@@ -66,6 +68,7 @@
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
class StepIndicator;
|
||||
class wxChoice;
|
||||
|
||||
#define COMMAND_TIMEOUT 5
|
||||
|
||||
@@ -140,28 +143,35 @@ class ExtruderImage : public wxWindow
|
||||
ScalableBitmap *m_left_extruder_active_empty;
|
||||
ScalableBitmap *m_left_extruder_unactive_filled;
|
||||
ScalableBitmap *m_left_extruder_unactive_empty;
|
||||
|
||||
ScalableBitmap *m_right_extruder_active_filled;
|
||||
ScalableBitmap *m_right_extruder_active_empty;
|
||||
ScalableBitmap *m_right_extruder_unactive_filled;
|
||||
ScalableBitmap *m_right_extruder_unactive_empty;
|
||||
|
||||
|
||||
ScalableBitmap *m_extruder_single_nozzle_empty_load;
|
||||
ScalableBitmap *m_extruder_single_nozzle_empty_unload;
|
||||
ScalableBitmap *m_extruder_single_nozzle_filled_load;
|
||||
ScalableBitmap *m_extruder_single_nozzle_filled_unload;
|
||||
|
||||
|
||||
ExtruderState m_left_ext_state = {ExtruderState::EMPTY_LOAD};
|
||||
ExtruderState m_right_ext_state = {ExtruderState::EMPTY_LOAD};
|
||||
ExtruderState m_single_ext_state = {ExtruderState::EMPTY_LOAD};
|
||||
std::vector<ExtruderState> m_multi_extruder_states;
|
||||
bool m_generic_nozzle_display{false};
|
||||
|
||||
public:
|
||||
void update(int nozzle_num, int nozzle_id);
|
||||
void update(ExtruderState single_state);
|
||||
void update(ExtruderState right_state, ExtruderState left_state);
|
||||
|
||||
void update(ExtruderState state, int idx);
|
||||
|
||||
void msw_rescale();
|
||||
void setExtruderCount(int nozzle_num);
|
||||
void setGenericNozzleDisplay(bool enabled);
|
||||
void setExtruderUsed(std::string loc);
|
||||
void setExtruderUsed(int nozzle_idx);
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
|
||||
void render(wxDC &dc);
|
||||
@@ -495,6 +505,8 @@ protected:
|
||||
std::vector<ExtruderImage *> m_extruderImage;
|
||||
|
||||
SwitchBoard * m_nozzle_btn_panel;
|
||||
wxChoice* m_generic_nozzle_selector{nullptr};
|
||||
int m_generic_nozzle_selector_count{0};
|
||||
|
||||
wxStaticText * m_text_tasklist_caption;
|
||||
|
||||
@@ -507,10 +519,23 @@ protected:
|
||||
wxBoxSizer * m_misc_ctrl_sizer;
|
||||
StaticBox* m_fan_panel;
|
||||
StaticLine * m_line_nozzle;
|
||||
TempInput* m_tempCtrl_nozzle;
|
||||
wxWindowID m_nozzle_temp_control_id{wxID_ANY};
|
||||
wxWindow* m_temp_nozzle_parent{nullptr};
|
||||
wxBoxSizer* m_temp_nozzle_sizer{nullptr};
|
||||
size_t m_temp_nozzle_active_count{0};
|
||||
TempInput* m_tempCtrl_nozzle{nullptr};
|
||||
int m_temp_nozzle_timeout{ 0 };
|
||||
TempInput* m_tempCtrl_nozzle_deputy;
|
||||
TempInput* m_tempCtrl_nozzle_deputy{nullptr};
|
||||
int m_temp_nozzle_deputy_timeout{ 0 };
|
||||
|
||||
std::vector<TempInput*> m_tempCtrl_nozzles;
|
||||
std::vector<int> m_temp_nozzle_timeouts;
|
||||
|
||||
// Last (dev_id, device toolhead count, preset nozzle count) we warned about, to avoid log spam.
|
||||
std::string m_last_mismatch_dev_id;
|
||||
int m_last_mismatch_device_count{-1};
|
||||
int m_last_mismatch_preset_count{-1};
|
||||
|
||||
TempInput * m_tempCtrl_bed;
|
||||
int m_temp_bed_timeout {0};
|
||||
TempInput * m_tempCtrl_chamber;
|
||||
@@ -550,6 +575,7 @@ protected:
|
||||
|
||||
/* AMS control box <-> live nozzle-rack panel toggle (rack printers only) */
|
||||
SwitchBoard* m_ams_rack_switch{ nullptr };
|
||||
MultiSwitchButton* m_ams_nozzle_switch{ nullptr };
|
||||
|
||||
AMSControl* m_ams_control;
|
||||
StaticBox* m_ams_control_box;
|
||||
@@ -624,6 +650,9 @@ public:
|
||||
|
||||
wxBoxSizer *create_temp_axis_group(wxWindow *parent);
|
||||
wxBoxSizer *create_temp_control(wxWindow *parent);
|
||||
TempInput* create_nozzle_temp_control(wxWindow *parent, wxWindowID id);
|
||||
void set_temp_input_colors(TempInput* temp_ctrl);
|
||||
void ensure_nozzle_temp_controls(size_t count);
|
||||
wxBoxSizer *create_misc_control(wxWindow *parent);
|
||||
wxBoxSizer *create_axis_control(wxWindow *parent);
|
||||
wxPanel *create_bed_control(wxWindow *parent);
|
||||
@@ -654,6 +683,7 @@ private:
|
||||
friend class MonitorPanel;
|
||||
void wire_controls();
|
||||
bool load_thumbnail_from_url(const wxString &url, MachineObject *obj);
|
||||
void sync_nozzle_temp_controls(size_t count);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<SliceInfoPopup> m_slice_info_popup;
|
||||
@@ -801,6 +831,7 @@ protected:
|
||||
void update_cloud_subtask(MachineObject *obj);
|
||||
void update_sdcard_subtask(MachineObject *obj);
|
||||
void update_temp_ctrl(MachineObject *obj);
|
||||
void check_extruder_count_mismatch(MachineObject* obj);
|
||||
void update_misc_ctrl(MachineObject *obj);
|
||||
void update_ams(MachineObject* obj);
|
||||
void update_rack(MachineObject* obj);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// #include "libslic3r/GCodeSender.hpp"
|
||||
//#include "slic3r/Utils/Serial.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "PresetHints.hpp"
|
||||
@@ -101,6 +102,8 @@
|
||||
#include "Widgets/MultiNozzleSync.hpp"
|
||||
#include "Widgets/SwitchButton.hpp"
|
||||
#include "Widgets/TabCtrl.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Search.hpp"
|
||||
#include "BedShapeDialog.hpp"
|
||||
#include "libslic3r/GCode/Thumbnails.hpp"
|
||||
#include "WipeTowerDialog.hpp"
|
||||
|
||||
@@ -244,7 +244,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
|
||||
m_sizer_ams_body->Add(m_sizer_ams_area_right, wxALIGN_CENTER, 0);
|
||||
|
||||
m_sizer_body->Add(m_sizer_ams_items, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_body->Add(0, 0, 1, wxEXPAND | wxTOP, FromDIP(10));
|
||||
m_sizer_ams_gap = m_sizer_body->Add(0, 0, 1, wxEXPAND | wxTOP, FromDIP(10));
|
||||
m_sizer_body->Add(m_sizer_ams_body, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_body->Add(m_sizer_down_road, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_body->Add(m_sizer_ams_option, 0, wxEXPAND, 0);
|
||||
@@ -371,7 +371,7 @@ bool AMSControl::IsAmsInRightPanel(std::string ams_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
else if (m_total_ext_count == 1){
|
||||
for (auto id : m_item_ids[MAIN_EXTRUDER_ID]){
|
||||
if (id == ams_id){
|
||||
return true;
|
||||
@@ -379,6 +379,10 @@ bool AMSControl::IsAmsInRightPanel(std::string ams_id) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AMSControl::AmsSelectedSwitch(wxCommandEvent& event) {
|
||||
@@ -519,7 +523,7 @@ void AMSControl::msw_rescale()
|
||||
m_button_ams_setting_press.msw_rescale();
|
||||
m_button_ams_setting->SetBitmap(m_button_ams_setting_normal.bmp());
|
||||
|
||||
m_extruder->msw_rescale();
|
||||
if (m_extruder) m_extruder->msw_rescale();
|
||||
|
||||
if (m_button_extruder_feed) m_button_extruder_feed->Rescale(); // ORCA
|
||||
if (m_button_extruder_back) m_button_extruder_back->Rescale(); // ORCA
|
||||
@@ -603,6 +607,8 @@ void AMSControl::CreateAms()
|
||||
|
||||
|
||||
void AMSControl::ClearAms() {
|
||||
m_sizer_ams_items->Clear(false);
|
||||
|
||||
m_simplebook_ams_right->DeleteAllPages();
|
||||
m_simplebook_ams_left->DeleteAllPages();
|
||||
m_simplebook_ams_right->DestroyChildren();
|
||||
@@ -613,7 +619,7 @@ void AMSControl::ClearAms() {
|
||||
m_simplebook_ams_left->Refresh();
|
||||
|
||||
for (auto it : m_ams_preview_list) {
|
||||
delete it.second;
|
||||
if (it.second) it.second->Destroy();
|
||||
}
|
||||
m_ams_preview_list.clear();
|
||||
m_ext_image_list.clear();
|
||||
@@ -624,12 +630,167 @@ void AMSControl::ClearAms() {
|
||||
m_ams_item_list.clear();
|
||||
m_sizer_prv_right->Clear();
|
||||
m_sizer_prv_left->Clear();
|
||||
m_item_ids = { {}, {} };
|
||||
|
||||
if (m_nozzle_book) {
|
||||
m_nozzle_book->Destroy();
|
||||
m_nozzle_book = nullptr;
|
||||
} else {
|
||||
for (auto &pane : m_nozzle_panes) {
|
||||
if (pane.preview_panel) pane.preview_panel->Destroy();
|
||||
if (pane.ams_book) pane.ams_book->Destroy();
|
||||
}
|
||||
}
|
||||
m_item_ids.assign(std::max(m_total_ext_count, 2), {});
|
||||
m_nozzle_panes.clear();
|
||||
m_active_nozzle_id = 0;
|
||||
m_current_ams.clear();
|
||||
m_current_show_ams_left.clear();
|
||||
m_current_show_ams_right.clear();
|
||||
pair_id.clear();
|
||||
}
|
||||
|
||||
void AMSControl::restore_legacy_ams_layout()
|
||||
{
|
||||
m_sizer_ams_items->Clear(false);
|
||||
m_sizer_ams_items->SetOrientation(wxHORIZONTAL);
|
||||
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxALIGN_CENTER);
|
||||
m_sizer_ams_items->Add(m_panel_prv_left, 0, wxLEFT | wxRIGHT, FromDIP(5));
|
||||
m_sizer_ams_items->Add(m_panel_prv_right, 0, wxLEFT | wxRIGHT, FromDIP(5));
|
||||
|
||||
m_panel_prv_left->Show();
|
||||
m_panel_prv_right->Show();
|
||||
m_simplebook_ams_left->Show();
|
||||
m_simplebook_ams_right->Show();
|
||||
m_panel_down_road->Show();
|
||||
m_sizer_body->Show(m_sizer_ams_body, true);
|
||||
m_sizer_option_mid->Show(m_extruder, true);
|
||||
m_down_road->SetSingleSideLayout(false);
|
||||
m_sizer_ams_gap->SetProportion(1);
|
||||
m_sizer_ams_gap->SetBorder(FromDIP(10));
|
||||
m_down_road->SetNozzleCount(m_total_ext_count);
|
||||
}
|
||||
|
||||
void AMSControl::CreateAmsMultiNozzle(const std::string &series_name, const std::string &printer_type) {
|
||||
const size_t pane_count = static_cast<size_t>(std::max(m_total_ext_count, 0));
|
||||
m_nozzle_panes.resize(pane_count);
|
||||
m_active_nozzle_id = 0;
|
||||
|
||||
m_sizer_ams_items->Clear(false);
|
||||
m_sizer_ams_items->SetOrientation(wxVERTICAL);
|
||||
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxALIGN_CENTER);
|
||||
m_sizer_body->Show(m_sizer_ams_body, false);
|
||||
m_sizer_ams_gap->SetProportion(0);
|
||||
m_sizer_ams_gap->SetBorder(0);
|
||||
|
||||
m_panel_prv_left->Hide();
|
||||
m_panel_prv_right->Hide();
|
||||
m_simplebook_ams_left->Hide();
|
||||
m_simplebook_ams_right->Hide();
|
||||
m_panel_down_road->Show();
|
||||
m_sizer_option_mid->Show(m_extruder, true);
|
||||
m_switcher->Hide();
|
||||
m_down_road->SetNozzleCount(1);
|
||||
m_down_road->SetSingleSideLayout(true, AMSPanelPos::LEFT_PANEL);
|
||||
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_SINGLE);
|
||||
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
m_extruder->updateNozzleNum(1, series_name);
|
||||
|
||||
m_nozzle_book = new wxSimplebook(m_amswin, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_nozzle_book->SetMinSize(wxSize(FromDIP(264), -1));
|
||||
m_nozzle_book->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
|
||||
m_sizer_ams_items->Add(m_nozzle_book, 0, wxALIGN_CENTER);
|
||||
|
||||
for (size_t nozzle_id = 0; nozzle_id < pane_count; ++nozzle_id) {
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
// A generic page is one H2C side. Keep the left-side geometry as the
|
||||
// canonical single-nozzle presentation; the road and AMS item code
|
||||
// still use the same side-specific paths as the legacy layout.
|
||||
pane.panel_pos = AMSPanelPos::LEFT_PANEL;
|
||||
pane.page = new wxPanel(m_nozzle_book, wxID_ANY);
|
||||
pane.page->SetMinSize(wxSize(FromDIP(264), -1));
|
||||
pane.page->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
|
||||
pane.page_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
pane.page->SetSizer(pane.page_sizer);
|
||||
|
||||
pane.preview_panel = new wxScrolledWindow(pane.page, wxID_ANY);
|
||||
pane.preview_panel->SetScrollRate(10, 0);
|
||||
pane.preview_panel->SetSize(AMS_ITEMS_PANEL_SIZE);
|
||||
pane.preview_panel->SetMinSize(AMS_ITEMS_PANEL_SIZE);
|
||||
pane.preview_panel->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
|
||||
|
||||
pane.preview_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
pane.preview_panel->SetSizer(pane.preview_sizer);
|
||||
|
||||
// Generic lane cards grow to their calculated width for 1-8 lanes. Keep the
|
||||
// book at the legacy minimum, but allow the page to expand to that width.
|
||||
pane.ams_book = new wxSimplebook(pane.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
|
||||
pane.ams_book->SetMinSize(wxSize(FromDIP(264), -1));
|
||||
pane.ams_book->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
|
||||
pane.ams_area = new wxBoxSizer(wxHORIZONTAL);
|
||||
pane.ams_area->Add(pane.ams_book, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
pane.page_sizer->Add(pane.preview_panel, 0, wxALIGN_CENTER | wxTOP, FromDIP(5));
|
||||
pane.page_sizer->Add(pane.ams_area, 0, wxALIGN_CENTER | wxTOP, FromDIP(10));
|
||||
|
||||
m_nozzle_book->AddPage(pane.page, wxEmptyString, nozzle_id == 0);
|
||||
}
|
||||
|
||||
auto add_info = [this, series_name, printer_type, pane_count](AMSinfo info) {
|
||||
if (pane_count == 0) return;
|
||||
|
||||
info.nozzle_id = std::clamp(info.nozzle_id, 0, static_cast<int>(pane_count) - 1);
|
||||
AddAmsPreview(info, info.ams_type);
|
||||
if (info.ams_type == AMSModel::EXT_AMS && info.cans.size() == 1)
|
||||
AddAms({info}, series_name, printer_type, m_nozzle_panes[info.nozzle_id].panel_pos);
|
||||
else
|
||||
AddAms(info);
|
||||
};
|
||||
|
||||
for (const auto &info : m_ams_info)
|
||||
add_info(info);
|
||||
|
||||
for (auto &info : m_ext_info) {
|
||||
const int virtual_tray_id = std::atoi(info.ams_id.c_str());
|
||||
if (virtual_tray_id <= VIRTUAL_TRAY_MAIN_ID &&
|
||||
virtual_tray_id >= VIRTUAL_TRAY_MAIN_ID - static_cast<int>(pane_count) + 1)
|
||||
info.nozzle_id = VIRTUAL_TRAY_MAIN_ID - virtual_tray_id;
|
||||
add_info(info);
|
||||
}
|
||||
|
||||
for (auto &pane : m_nozzle_panes) {
|
||||
if (pane.ams_book && pane.ams_book->GetPageCount() > 0)
|
||||
pane.ams_book->SetSelection(0);
|
||||
if (pane.preview_sizer) pane.preview_sizer->Layout();
|
||||
if (pane.preview_panel) pane.preview_panel->FitInside();
|
||||
if (pane.ams_book) pane.ams_book->Layout();
|
||||
if (pane.page) pane.page->Layout();
|
||||
|
||||
if (!pane.current_ams.empty()) {
|
||||
auto preview = m_ams_preview_list.find(pane.current_ams);
|
||||
if (preview != m_ams_preview_list.end() && preview->second)
|
||||
preview->second->OnSelected();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_nozzle_book && pane_count > 0) {
|
||||
m_nozzle_book->SetSelection(0);
|
||||
m_current_ams = m_nozzle_panes[0].current_ams;
|
||||
if (!m_current_ams.empty())
|
||||
SwitchAms(m_current_ams);
|
||||
else {
|
||||
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
}
|
||||
}
|
||||
m_amswin->Layout();
|
||||
m_amswin->Fit();
|
||||
// Generic N Nozzles
|
||||
}
|
||||
|
||||
void AMSControl::CreateAmsDoubleNozzle(const std::string &series_name, const std::string &printer_type)
|
||||
{
|
||||
restore_legacy_ams_layout();
|
||||
|
||||
std::vector<AMSinfo> single_info_left;
|
||||
std::vector<AMSinfo> single_info_right;
|
||||
|
||||
@@ -751,6 +912,8 @@ void AMSControl::CreateAmsDoubleNozzle(const std::string &series_name, const std
|
||||
|
||||
void AMSControl::CreateAmsSingleNozzle(const std::string &series_name, const std::string &printer_type)
|
||||
{
|
||||
restore_legacy_ams_layout();
|
||||
|
||||
std::vector<int>m_item_nums{0,0};
|
||||
std::vector<AMSinfo> single_info;
|
||||
|
||||
@@ -979,6 +1142,17 @@ void AMSControl::UpdateAms(const std::string &series_name,
|
||||
if (m_ams_info[i].routes_to_main_extruder() != ams_info[i].routes_to_main_extruder()) {
|
||||
fresh = true;
|
||||
}
|
||||
if (m_ams_info[i].nozzle_id != ams_info[i].nozzle_id) {
|
||||
fresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(m_ext_info.size()); ++i) {
|
||||
if (m_ext_info[i].ams_id != ext_info[i].ams_id ||
|
||||
m_ext_info[i].nozzle_id != ext_info[i].nozzle_id ||
|
||||
m_ext_info[i].ext_type != ext_info[i].ext_type) {
|
||||
fresh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
@@ -993,11 +1167,13 @@ void AMSControl::UpdateAms(const std::string &series_name,
|
||||
m_dev_id = dev_id;
|
||||
if (fresh){
|
||||
ClearAms();
|
||||
if (m_total_ext_count >= 2){
|
||||
CreateAmsDoubleNozzle(series_name, printer_type);
|
||||
}else{
|
||||
if (m_total_ext_count == 1)
|
||||
CreateAmsSingleNozzle(series_name, printer_type);
|
||||
}
|
||||
else if (m_total_ext_count == 2)
|
||||
CreateAmsDoubleNozzle(series_name, printer_type);
|
||||
else
|
||||
CreateAmsMultiNozzle(series_name, printer_type);
|
||||
|
||||
SetSize(wxSize(FromDIP(578), -1));
|
||||
SetMinSize(wxSize(FromDIP(578), -1));
|
||||
Layout();
|
||||
@@ -1070,7 +1246,7 @@ void AMSControl::UpdateAms(const std::string &series_name,
|
||||
}
|
||||
|
||||
/*update ams extruder*/
|
||||
if (m_extruder->updateNozzleNum(m_total_ext_count, series_name))
|
||||
if (m_total_ext_count <= 2 && m_extruder->updateNozzleNum(m_total_ext_count, series_name))
|
||||
{
|
||||
m_amswin->Layout();
|
||||
}
|
||||
@@ -1106,7 +1282,13 @@ void AMSControl::AddAmsPreview(AMSinfo info, AMSModel type)
|
||||
{
|
||||
AMSPreview *ams_prv = nullptr;
|
||||
|
||||
if (info.routes_to_main_extruder())
|
||||
if (m_total_ext_count > 2) {
|
||||
const int nozzle_id = std::clamp(info.nozzle_id, 0, m_total_ext_count - 1);
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
ams_prv = new AMSPreview(pane.preview_panel, wxID_ANY, info, type);
|
||||
pane.preview_sizer->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
|
||||
}
|
||||
else if (info.routes_to_main_extruder())
|
||||
{
|
||||
ams_prv = new AMSPreview(m_panel_prv_right, wxID_ANY, info, type);
|
||||
m_sizer_prv_right->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
|
||||
@@ -1129,6 +1311,40 @@ void AMSControl::AddAmsPreview(AMSinfo info, AMSModel type)
|
||||
void AMSControl::createAms(wxSimplebook* parent, int& idx, AMSinfo info, AMSPanelPos pos) {
|
||||
auto ams_item = new AmsItem(parent, info, info.ams_type, pos);
|
||||
parent->InsertPage(idx, ams_item, wxEmptyString, true);
|
||||
|
||||
// Generic AMS cards may be wider than the legacy 264 DIP page. Propagate
|
||||
// that calculated width through the page and simplebook so 5-8 lanes are
|
||||
// laid out fully instead of being clipped by the old minimum width.
|
||||
if (info.ams_type == AMSModel::GENERIC_AMS) {
|
||||
const int required_width = ams_item->GetMinSize().x;
|
||||
if (required_width > 0) {
|
||||
const int page_width = std::max(parent->GetMinSize().x, required_width);
|
||||
parent->SetMinSize(wxSize(page_width, -1));
|
||||
if (auto *page = parent->GetParent()) {
|
||||
page->SetMinSize(wxSize(std::max(page->GetMinSize().x, page_width), -1));
|
||||
if (auto *nozzle_book = page->GetParent())
|
||||
nozzle_book->SetMinSize(wxSize(std::max(nozzle_book->GetMinSize().x, page_width), -1));
|
||||
}
|
||||
}
|
||||
// Above eight lanes, AmsItem is a fixed-minimum viewport over a wider
|
||||
// lane strip. Expand the viewport through its page's sizers rather
|
||||
// than propagating the strip's virtual width into the card/book.
|
||||
const bool generic_lane_layout = !wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor();
|
||||
if (generic_lane_layout && info.cans.size() > 8) {
|
||||
if (auto *page = parent->GetParent()) {
|
||||
auto pane = std::find_if(m_nozzle_panes.begin(), m_nozzle_panes.end(),
|
||||
[page](const NozzleAmsPane &candidate) { return candidate.page == page; });
|
||||
if (pane != m_nozzle_panes.end()) {
|
||||
pane->ams_area->GetItem(pane->ams_book)->SetProportion(1);
|
||||
pane->ams_area->GetItem(pane->ams_book)->SetFlag(wxEXPAND);
|
||||
pane->page_sizer->GetItem(pane->ams_area)->SetFlag(wxEXPAND | wxTOP);
|
||||
m_sizer_ams_items->GetItem(m_nozzle_book)->SetFlag(wxEXPAND);
|
||||
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxEXPAND);
|
||||
m_amswin->Layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ams_item->set_selection(idx);
|
||||
idx++;
|
||||
|
||||
@@ -1153,7 +1369,10 @@ AMSRoadShowMode AMSControl::findFirstMode(AMSPanelPos pos) {
|
||||
if (item->second->get_ams_model() == AMSModel::EXT_AMS && item->second->get_ext_type() == AMSModelOriginType::LITE_EXT) return AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
|
||||
return AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
|
||||
}
|
||||
else{
|
||||
else if (item->second->get_can_count() > GENERIC_AMS_SLOT_NUM) {
|
||||
return AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
|
||||
}
|
||||
else {
|
||||
for (auto ids : pair_id){
|
||||
if (ids.first == ams_id || ids.second == ams_id){
|
||||
return AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
|
||||
@@ -1200,6 +1419,11 @@ void AMSControl::createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSi
|
||||
//book_sizer->Add(ams1, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
book_sizer->Add(ams1, 0, wxLEFT, (book_panel->GetSize().x - ams1->GetSize().x) / 2);
|
||||
}
|
||||
else if (pos == AMSPanelPos::SINGLE_PANEL) {
|
||||
book_sizer->AddStretchSpacer(1);
|
||||
book_sizer->Add(ams1, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
book_sizer->AddStretchSpacer(1);
|
||||
}
|
||||
else{
|
||||
auto ext_image = new AMSExtImage(book_panel, pos, m_total_ext_count, false);
|
||||
book_sizer->Add(ams1, 0, wxLEFT, FromDIP(30));
|
||||
@@ -1228,7 +1452,17 @@ void AMSControl::createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSi
|
||||
|
||||
void AMSControl::AddAms(AMSinfo info, AMSPanelPos pos)
|
||||
{
|
||||
if (m_total_ext_count > 1){
|
||||
if (m_total_ext_count > 2) {
|
||||
const int nozzle_id = std::clamp(info.nozzle_id, 0, m_total_ext_count - 1);
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
pane.item_ids.push_back(info.ams_id);
|
||||
m_item_ids[nozzle_id].push_back(info.ams_id);
|
||||
if (pane.current_ams.empty()) pane.current_ams = info.ams_id;
|
||||
createAms(pane.ams_book, pane.page_index, info, AMSPanelPos::SINGLE_PANEL);
|
||||
pane.ams_book->Fit();
|
||||
pane.ams_book->Layout();
|
||||
}
|
||||
else if (m_total_ext_count == 2){
|
||||
if (info.routes_to_main_extruder()){
|
||||
createAms(m_simplebook_ams_right, m_right_page_index, info, AMSPanelPos::RIGHT_PANEL);
|
||||
}
|
||||
@@ -1239,6 +1473,9 @@ void AMSControl::AddAms(AMSinfo info, AMSPanelPos pos)
|
||||
else if (m_total_ext_count == 1){
|
||||
createAms(m_simplebook_ams_left, m_left_page_index, info, AMSPanelPos::LEFT_PANEL);
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
m_simplebook_ams_left->Layout();
|
||||
m_simplebook_ams_right->Layout();
|
||||
m_simplebook_ams_left->Refresh();
|
||||
@@ -1269,7 +1506,19 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
|
||||
if (single_info.size() <= 0){
|
||||
return;
|
||||
}
|
||||
if (m_total_ext_count == 2) {
|
||||
if (m_total_ext_count > 2) {
|
||||
const int nozzle_id = std::clamp(single_info.front().nozzle_id, 0, m_total_ext_count - 1);
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
for (const auto &info : single_info) {
|
||||
pane.item_ids.push_back(info.ams_id);
|
||||
m_item_ids[nozzle_id].push_back(info.ams_id);
|
||||
}
|
||||
if (pane.current_ams.empty()) pane.current_ams = single_info.front().ams_id;
|
||||
createAmsPanel(pane.ams_book, pane.page_index, single_info, series_name, printer_type, AMSPanelPos::SINGLE_PANEL, m_total_ext_count);
|
||||
pane.ams_book->Fit();
|
||||
pane.ams_book->Layout();
|
||||
}
|
||||
else if (m_total_ext_count == 2) {
|
||||
if (single_info[0].routes_to_main_extruder()) {
|
||||
createAmsPanel(m_simplebook_ams_right, m_right_page_index, single_info, series_name, printer_type, AMSPanelPos::RIGHT_PANEL, m_total_ext_count);
|
||||
}
|
||||
@@ -1285,6 +1534,9 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
|
||||
createAmsPanel(m_simplebook_ams_left, m_left_page_index, single_info, series_name, printer_type, AMSPanelPos::LEFT_PANEL, m_total_ext_count);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
|
||||
m_simplebook_ams_left->Layout();
|
||||
m_simplebook_ams_right->Layout();
|
||||
@@ -1322,6 +1574,21 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
|
||||
void AMSControl::AddAmsPreview(std::vector<AMSinfo>single_info, AMSPanelPos pos) {
|
||||
if (single_info.size() <= 0) return;
|
||||
|
||||
if (m_total_ext_count > 2) {
|
||||
const int nozzle_id = std::clamp(single_info.front().nozzle_id, 0, m_total_ext_count - 1);
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
for (const auto &info : single_info) {
|
||||
auto *ams_prv = new AMSPreview(pane.preview_panel, wxID_ANY, info, info.ams_type);
|
||||
pane.preview_sizer->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
|
||||
ams_prv->Bind(wxEVT_LEFT_DOWN, [this, ams_prv](wxMouseEvent &e) {
|
||||
SwitchAms(ams_prv->get_ams_id());
|
||||
e.Skip();
|
||||
});
|
||||
m_ams_preview_list[info.ams_id] = ams_prv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AMSPreview* ams_prv = nullptr;
|
||||
AMSPreview* ams_prv2 = nullptr;
|
||||
if (pos == AMSPanelPos::RIGHT_PANEL){
|
||||
@@ -1360,8 +1627,97 @@ void AMSControl::AddAmsPreview(std::vector<AMSinfo>single_info, AMSPanelPos pos)
|
||||
}
|
||||
}
|
||||
|
||||
void AMSControl::SelectNozzle(int nozzle_id)
|
||||
{
|
||||
if (nozzle_id < 0 || nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
|
||||
return;
|
||||
|
||||
if (m_active_nozzle_id == nozzle_id &&
|
||||
(!m_nozzle_book || m_nozzle_book->GetSelection() == nozzle_id))
|
||||
return;
|
||||
|
||||
m_active_nozzle_id = nozzle_id;
|
||||
if (m_nozzle_book && m_nozzle_book->GetSelection() != nozzle_id)
|
||||
m_nozzle_book->SetSelection(nozzle_id);
|
||||
m_current_ams = m_nozzle_panes[nozzle_id].current_ams;
|
||||
if (!m_current_ams.empty()) {
|
||||
SwitchAms(m_current_ams);
|
||||
return;
|
||||
}
|
||||
if (m_down_road) {
|
||||
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
}
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(false, 0);
|
||||
post_event(SimpleEvent(EVT_AMS_SWITCH));
|
||||
}
|
||||
|
||||
void AMSControl::SwitchAms(std::string ams_id)
|
||||
{
|
||||
if (m_total_ext_count > 2) {
|
||||
NozzleAmsPane *pane = nullptr;
|
||||
int nozzle_id = -1;
|
||||
for (int index = 0; index < static_cast<int>(m_nozzle_panes.size()); ++index) {
|
||||
auto &candidate = m_nozzle_panes[index];
|
||||
if (std::find(candidate.item_ids.begin(), candidate.item_ids.end(), ams_id) != candidate.item_ids.end()) {
|
||||
pane = &candidate;
|
||||
nozzle_id = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pane) return;
|
||||
|
||||
m_active_nozzle_id = nozzle_id;
|
||||
if (m_nozzle_book && m_nozzle_book->GetSelection() != nozzle_id)
|
||||
m_nozzle_book->SetSelection(nozzle_id);
|
||||
|
||||
pane->current_ams = ams_id;
|
||||
m_current_ams = ams_id;
|
||||
m_current_select = ams_id;
|
||||
|
||||
for (const auto &id : pane->item_ids) {
|
||||
auto preview = m_ams_preview_list.find(id);
|
||||
if (preview == m_ams_preview_list.end() || !preview->second) continue;
|
||||
if (id == ams_id) preview->second->OnSelected();
|
||||
else preview->second->UnSelected();
|
||||
}
|
||||
|
||||
auto item = m_ams_item_list.find(ams_id);
|
||||
if (item != m_ams_item_list.end() && item->second) {
|
||||
if (pane->ams_book)
|
||||
pane->ams_book->SetSelection(item->second->get_selection());
|
||||
|
||||
AMSRoadShowMode road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
|
||||
if (item->second->get_can_count() == GENERIC_AMS_SLOT_NUM)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
|
||||
else if (item->second->get_can_count() > GENERIC_AMS_SLOT_NUM)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
|
||||
else if (item->second->get_ams_model() == AMSModel::N3S_AMS)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
|
||||
else if (item->second->get_ams_model() == AMSModel::AMS_LITE ||
|
||||
(item->second->get_ams_model() == AMSModel::EXT_AMS &&
|
||||
item->second->get_ext_type() == AMSModelOriginType::LITE_EXT))
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
|
||||
|
||||
if (m_down_road) {
|
||||
if (pane->panel_pos == AMSPanelPos::LEFT_PANEL) {
|
||||
m_down_road->UpdateLeft(1, road_mode);
|
||||
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
} else {
|
||||
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
m_down_road->UpdateRight(1, road_mode);
|
||||
}
|
||||
m_down_road->UpdatePassRoad(pane->panel_pos, -1, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
|
||||
}
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(false, 0);
|
||||
}
|
||||
|
||||
post_event(SimpleEvent(EVT_AMS_SWITCH));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ams_id == m_current_show_ams_left || ams_id == m_current_show_ams_right){return;}
|
||||
|
||||
bool is_in_right = IsAmsInRightPanel(ams_id);
|
||||
@@ -1432,13 +1788,17 @@ void AMSControl::SwitchAms(std::string ams_id)
|
||||
else {
|
||||
AMSRoadShowMode mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
|
||||
|
||||
if (item->get_ams_model() == AMSModel::N3S_AMS)
|
||||
if (item->get_can_count() > GENERIC_AMS_SLOT_NUM)
|
||||
mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
|
||||
else if (item->get_ams_model() == AMSModel::N3S_AMS)
|
||||
mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
|
||||
|
||||
for (auto it : pair_id) {
|
||||
if (it.first == ams_id || it.second == ams_id) {
|
||||
mode = AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
|
||||
break;
|
||||
if (item->get_can_count() <= GENERIC_AMS_SLOT_NUM) {
|
||||
for (auto it : pair_id) {
|
||||
if (it.first == ams_id || it.second == ams_id) {
|
||||
mode = AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos == AMSPanelPos::LEFT_PANEL ? m_down_road->UpdateLeft(m_total_ext_count, mode)
|
||||
@@ -1500,6 +1860,20 @@ void AMSControl::SetExtruder(bool on_off, int nozzle_id, std::string ams_id, std
|
||||
AmsItem *item = nullptr;
|
||||
if (m_ams_item_list.find(ams_id) != m_ams_item_list.end()) { item = m_ams_item_list[ams_id]; }
|
||||
|
||||
if (m_total_ext_count > 2) {
|
||||
if (nozzle_id < 0 || nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
|
||||
return;
|
||||
|
||||
if (!m_extruder)
|
||||
return;
|
||||
|
||||
if (on_off && item)
|
||||
m_extruder->OnAmsLoading(true, 0, item->GetTagColr(slot_id));
|
||||
else
|
||||
m_extruder->OnAmsLoading(false, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (on_off && item) {
|
||||
auto col = item->GetTagColr(slot_id);
|
||||
m_extruder->OnAmsLoading(true, nozzle_id, col);
|
||||
@@ -1624,6 +1998,69 @@ void AMSControl::SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadTy
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_total_ext_count > 2) {
|
||||
const int nozzle_id = std::clamp(ams->get_nozzle_id(), 0, m_total_ext_count - 1);
|
||||
if (nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
|
||||
return;
|
||||
auto &pane = m_nozzle_panes[nozzle_id];
|
||||
AMSRoadShowMode road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
|
||||
if (ams->get_can_count() == GENERIC_AMS_SLOT_NUM)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
|
||||
else if (ams->get_can_count() > GENERIC_AMS_SLOT_NUM)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
|
||||
else if (ams->get_ams_model() == AMSModel::N3S_AMS)
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
|
||||
else if (ams->get_ams_model() == AMSModel::AMS_LITE ||
|
||||
(ams->get_ams_model() == AMSModel::EXT_AMS && ams->get_ext_type() == AMSModelOriginType::LITE_EXT))
|
||||
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
|
||||
|
||||
if (can_index >= 0 && can_index < static_cast<int>(info.cans.size()) && m_down_road)
|
||||
m_down_road->SetPassRoadColour(pane.panel_pos == AMSPanelPos::LEFT_PANEL,
|
||||
info.cans[can_index].material_colour);
|
||||
if (m_down_road) {
|
||||
if (pane.panel_pos == AMSPanelPos::LEFT_PANEL) {
|
||||
m_down_road->UpdateLeft(1, road_mode);
|
||||
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
} else {
|
||||
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
|
||||
m_down_road->UpdateRight(1, road_mode);
|
||||
}
|
||||
}
|
||||
|
||||
int road_length = 0;
|
||||
|
||||
if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_NONE) {
|
||||
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
|
||||
if (m_down_road)
|
||||
m_down_road->UpdatePassRoad(pane.panel_pos, -1, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(false, 0);
|
||||
}
|
||||
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP1) {
|
||||
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_1);
|
||||
if (m_down_road)
|
||||
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_1);
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(false, 0);
|
||||
}
|
||||
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2) {
|
||||
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_2);
|
||||
if (m_down_road)
|
||||
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_2);
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(true, 0, ams->GetTagColr(canid));
|
||||
}
|
||||
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3) {
|
||||
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_3);
|
||||
if (m_down_road)
|
||||
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_3);
|
||||
if (m_extruder)
|
||||
m_extruder->OnAmsLoading(true, 0, ams->GetTagColr(canid));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (can_index >= 0 && can_index < info.cans.size())
|
||||
{
|
||||
m_down_road->SetPassRoadColour(left, info.cans[can_index].material_colour);
|
||||
|
||||
@@ -47,6 +47,20 @@ public:
|
||||
void on_retry();
|
||||
|
||||
protected:
|
||||
struct NozzleAmsPane
|
||||
{
|
||||
wxPanel* page{nullptr};
|
||||
wxBoxSizer* page_sizer{nullptr};
|
||||
wxScrolledWindow* preview_panel{nullptr};
|
||||
wxBoxSizer* preview_sizer{nullptr};
|
||||
wxSimplebook* ams_book{nullptr};
|
||||
wxBoxSizer* ams_area{nullptr};
|
||||
AMSPanelPos panel_pos{AMSPanelPos::LEFT_PANEL};
|
||||
std::vector<std::string> item_ids;
|
||||
std::string current_ams;
|
||||
int page_index{0};
|
||||
};
|
||||
|
||||
std::string m_current_ams;
|
||||
std::string m_current_slot_left;
|
||||
std::string m_current_slot_right;
|
||||
@@ -63,6 +77,9 @@ protected:
|
||||
|
||||
std::string m_dev_id;
|
||||
std::vector<std::vector<std::string>> m_item_ids{ {}, {} };
|
||||
std::vector<NozzleAmsPane> m_nozzle_panes;
|
||||
wxSimplebook* m_nozzle_book{nullptr};
|
||||
int m_active_nozzle_id{0};
|
||||
std::vector<std::pair<std::string, std::string>> pair_id;
|
||||
|
||||
int m_total_ext_count = 1;
|
||||
@@ -103,6 +120,7 @@ protected:
|
||||
wxSimplebook *m_simplebook_ams_left{nullptr};
|
||||
wxSimplebook *m_simplebook_ams_right{ nullptr };
|
||||
wxSimplebook *m_simplebook_bottom{nullptr};
|
||||
wxSizerItem *m_sizer_ams_gap{nullptr};
|
||||
wxPanel *m_panel_down_road{ nullptr };
|
||||
int m_left_page_index = 0;
|
||||
int m_right_page_index = 0;
|
||||
@@ -156,6 +174,7 @@ public:
|
||||
void createAms(wxSimplebook* parent, int& idx, AMSinfo info, AMSPanelPos pos);
|
||||
void createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSinfo> infos, const std::string &series_name, const std::string &printer_type, AMSPanelPos pos, int total_ext_num);
|
||||
AMSRoadShowMode findFirstMode(AMSPanelPos pos);
|
||||
void restore_legacy_ams_layout();
|
||||
|
||||
AMSModel m_ams_model{AMSModel::EXT_AMS};
|
||||
AMSModel m_ext_model{AMSModel::EXT_AMS};
|
||||
@@ -183,6 +202,7 @@ public:
|
||||
|
||||
void UpdatePassRoad(std::string ams_id, AMSPassRoadType type, AMSPassRoadSTEP step);
|
||||
void CreateAms();
|
||||
void CreateAmsMultiNozzle(const std::string& series_name, const std::string& printer_type);
|
||||
void CreateAmsDoubleNozzle(const std::string &series_name, const std::string& printer_type);
|
||||
void CreateAmsSingleNozzle(const std::string &series_name, const std::string &printer_type);
|
||||
void ClearAms();
|
||||
@@ -210,6 +230,8 @@ public:
|
||||
void SetExtruder(bool on_off, int nozzle_id, std::string ams_id, std::string slot_id);
|
||||
void SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadType type, AMSPassRoadSTEP step);
|
||||
void SwitchAms(std::string ams_id);
|
||||
void SelectNozzle(int nozzle_id);
|
||||
int GetActiveNozzleId() const { return m_active_nozzle_id; }
|
||||
|
||||
void msw_rescale();
|
||||
void on_filament_load(wxCommandEvent &event);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/scrolwin.h>
|
||||
|
||||
#include "CalibUtils.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
@@ -76,6 +77,48 @@ namespace Slic3r { namespace GUI {
|
||||
//#define AMS_SINGLE_CAN_SIZE wxSize(FromDIP(78), 144)
|
||||
#define AMS_CANS_WINDOW_SIZE wxSize(FromDIP(264), FromDIP(174))
|
||||
#define AMS_SINGLE_CAN_SIZE wxSize(FromDIP(78), FromDIP(174))
|
||||
|
||||
int generic_ams_layout_width(wxWindow *window, int lane_count)
|
||||
{
|
||||
const int base_width = window->FromDIP(264);
|
||||
const int lane_width = window->FromDIP(52);
|
||||
const int lane_gap = window->FromDIP(4);
|
||||
if (lane_count <= 4)
|
||||
return base_width;
|
||||
|
||||
// Lane centers are distributed as width * (index + 1) / (count + 1).
|
||||
// Keep at least one card width plus the requested gap between centers.
|
||||
return std::max(base_width, (lane_count + 1) * (lane_width + lane_gap));
|
||||
}
|
||||
|
||||
std::vector<int> generic_ams_lane_centers(int lane_count, int width)
|
||||
{
|
||||
std::vector<int> centers;
|
||||
if (lane_count <= 0)
|
||||
return centers;
|
||||
|
||||
centers.reserve(lane_count);
|
||||
for (int lane = 0; lane < lane_count; ++lane)
|
||||
centers.push_back(width * (lane + 1) / (lane_count + 1));
|
||||
return centers;
|
||||
}
|
||||
|
||||
int generic_ams_preview_width(wxWindow *window, int lane_count)
|
||||
{
|
||||
const int base_width = window->FromDIP(52);
|
||||
const int lane_width = window->FromDIP(9);
|
||||
const int lane_gap = window->FromDIP(4);
|
||||
if (lane_count <= 0)
|
||||
return base_width;
|
||||
|
||||
return std::max(base_width, lane_count * lane_width + (lane_count + 1) * lane_gap);
|
||||
}
|
||||
|
||||
bool use_generic_ams_layout(AMSModel model)
|
||||
{
|
||||
return model == AMSModel::GENERIC_AMS &&
|
||||
(!wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor());
|
||||
}
|
||||
bool AMSinfo::parse_ams_info(MachineObject *obj, DevAms *ams, bool remain_flag, bool humidity_flag)
|
||||
{
|
||||
if (!ams) return false;
|
||||
@@ -170,10 +213,14 @@ void AMSinfo::parse_ext_info(MachineObject* obj, DevAmsTray tray) {
|
||||
info.can_id = std::to_string(0);
|
||||
this->cans.clear();
|
||||
|
||||
if (tray.id == std::to_string(VIRTUAL_TRAY_MAIN_ID))
|
||||
try {
|
||||
const int virtual_tray_id = std::stoi(tray.id);
|
||||
if (virtual_tray_id >= 0 && virtual_tray_id <= VIRTUAL_TRAY_MAIN_ID)
|
||||
this->nozzle_id = VIRTUAL_TRAY_MAIN_ID - virtual_tray_id;
|
||||
}
|
||||
catch (...) {
|
||||
this->nozzle_id = 0;
|
||||
else if (tray.id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID))
|
||||
this->nozzle_id = 1;
|
||||
}
|
||||
|
||||
if (tray.is_tray_info_ready()) {
|
||||
info.ctype = tray.ctype;
|
||||
@@ -856,7 +903,11 @@ AMSextruder::AMSextruder(wxWindow *parent, wxWindowID id, int nozzle_num, const
|
||||
|
||||
void AMSextruder::TurnOff()
|
||||
{
|
||||
m_left_extruder->TurnOff();
|
||||
if (m_left_extruder) m_left_extruder->TurnOff();
|
||||
if (m_right_extruder) m_right_extruder->TurnOff();
|
||||
for (auto *extruder : m_nozzle_extruders) {
|
||||
if (extruder) extruder->TurnOff();
|
||||
}
|
||||
}
|
||||
|
||||
void AMSextruder::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, int nozzle_num)
|
||||
@@ -882,7 +933,7 @@ void AMSextruder::OnAmsLoading(bool load, int nozzle_id, wxColour col /*= AMS_CO
|
||||
m_left_extruder->OnAmsLoading(load, col);
|
||||
if (load) m_current_colur_deputy = col;
|
||||
}
|
||||
else if (m_nozzle_num > 1){
|
||||
else if (m_nozzle_num == 2){
|
||||
if (nozzle_id == MAIN_EXTRUDER_ID) {
|
||||
m_right_extruder->OnAmsLoading(load, col);
|
||||
if (m_current_colur != col){
|
||||
@@ -896,6 +947,15 @@ void AMSextruder::OnAmsLoading(bool load, int nozzle_id, wxColour col /*= AMS_CO
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_nozzle_num > 2) {
|
||||
if (nozzle_id >= 0 && nozzle_id < static_cast<int>(m_nozzle_extruders.size())) {
|
||||
m_nozzle_extruders[nozzle_id]->OnAmsLoading(load, col);
|
||||
if (load) m_current_colur = col;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
}
|
||||
|
||||
/*return true if something is updated*/
|
||||
@@ -905,12 +965,26 @@ bool AMSextruder::updateNozzleNum(int nozzle_num, const std::string& series_name
|
||||
m_series_name = series_name;
|
||||
m_nozzle_num = nozzle_num;
|
||||
this->DestroyChildren();
|
||||
m_left_extruder = nullptr;
|
||||
m_right_extruder = nullptr;
|
||||
m_nozzle_extruders.clear();
|
||||
|
||||
m_right_extruder = new AMSextruderImage(this, wxID_ANY, "right_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
|
||||
m_bitmap_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
if (m_nozzle_num == 1) {
|
||||
if (MachineObject::is_series_n(m_series_name)) {
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
|
||||
} else if (MachineObject::is_series_x(m_series_name) || MachineObject::is_series_p(m_series_name)) {
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
|
||||
} else {
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
|
||||
}
|
||||
|
||||
wxBoxSizer* m_bitmap_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
if (m_nozzle_num >= 2)
|
||||
m_left_extruder->setShowState(true);
|
||||
m_bitmap_sizer->Add(m_left_extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
|
||||
}
|
||||
else if (m_nozzle_num == 2)
|
||||
{
|
||||
m_right_extruder = new AMSextruderImage(this, wxID_ANY, "right_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "left_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
|
||||
m_left_extruder->setShowState(true);
|
||||
m_right_extruder->setShowState(true);
|
||||
@@ -918,25 +992,17 @@ bool AMSextruder::updateNozzleNum(int nozzle_num, const std::string& series_name
|
||||
m_bitmap_sizer->Add(m_right_extruder, 0, wxLEFT | wxALIGN_TOP, FromDIP(2));
|
||||
m_bitmap_sizer->AddSpacer(2);
|
||||
}
|
||||
else if (m_nozzle_num > 2) {
|
||||
for (int nozzle_id = 0; nozzle_id < m_nozzle_num; ++nozzle_id) {
|
||||
auto *extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
|
||||
extruder->setShowState(true);
|
||||
m_nozzle_extruders.push_back(extruder);
|
||||
m_bitmap_sizer->Add(extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MachineObject::is_series_n(m_series_name))
|
||||
{
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
|
||||
}
|
||||
else if(MachineObject::is_series_x(m_series_name) || MachineObject::is_series_p(m_series_name))
|
||||
{
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
|
||||
}
|
||||
|
||||
m_left_extruder->setShowState(true);
|
||||
m_right_extruder->setShowState(false);
|
||||
m_bitmap_sizer->Add(m_left_extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
|
||||
m_bitmap_sizer->Add(m_right_extruder, 0, wxLEFT | wxALIGN_TOP, FromDIP(3));
|
||||
// Generic N Nozzles
|
||||
}
|
||||
|
||||
SetSizer(m_bitmap_sizer);
|
||||
@@ -951,6 +1017,9 @@ void AMSextruder::msw_rescale()
|
||||
//m_amsSextruder->msw_rescale();
|
||||
if (m_left_extruder) m_left_extruder->msw_rescale();
|
||||
if (m_right_extruder) m_right_extruder->msw_rescale();
|
||||
for (auto *extruder : m_nozzle_extruders) {
|
||||
if (extruder) extruder->msw_rescale();
|
||||
}
|
||||
Layout();
|
||||
Update();
|
||||
Refresh();
|
||||
@@ -2123,7 +2192,10 @@ AMSRoadUpPart::AMSRoadUpPart(wxWindow* parent, wxWindowID id, AMSinfo info, AMSM
|
||||
m_ams_model = model;
|
||||
|
||||
if (m_ams_model == AMSModel::GENERIC_AMS){
|
||||
create(parent, id, pos, wxSize(FromDIP(264), FromDIP(34)));
|
||||
const int width = use_generic_ams_layout(m_ams_model)
|
||||
? generic_ams_layout_width(this, static_cast<int>(m_amsinfo.cans.size()))
|
||||
: FromDIP(264);
|
||||
create(parent, id, pos, wxSize(width, FromDIP(34)));
|
||||
}
|
||||
else{
|
||||
create(parent, id, pos, wxSize(FromDIP(78), FromDIP(34)));
|
||||
@@ -2235,6 +2307,38 @@ void AMSRoadUpPart::doRender(wxDC& dc)
|
||||
dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
|
||||
|
||||
if (use_generic_ams_layout(m_ams_model)) {
|
||||
const int lane_count = static_cast<int>(m_amsinfo.cans.size());
|
||||
if (lane_count <= 0)
|
||||
return;
|
||||
|
||||
const auto lane_centers = generic_ams_lane_centers(lane_count, size.x);
|
||||
const int height = FromDIP(21);
|
||||
for (const int lane_center : lane_centers)
|
||||
dc.DrawLine(lane_center, 0, lane_center, height);
|
||||
|
||||
if (lane_centers.size() > 1)
|
||||
dc.DrawLine(lane_centers.front(), height, lane_centers.back(), height);
|
||||
dc.DrawLine(size.x / 2, height, size.x / 2, size.y);
|
||||
|
||||
if (m_load_step != AMSPassRoadSTEP::AMS_ROAD_STEP_NONE &&
|
||||
m_load_slot_index >= 0 && m_load_slot_index < lane_count) {
|
||||
const int lane_center = lane_centers[m_load_slot_index];
|
||||
dc.SetPen(wxPen(_get_diff_clr(this, m_amsinfo.cans[m_load_slot_index].material_colour), 4, wxPENSTYLE_SOLID));
|
||||
dc.DrawLine(lane_center, 0, lane_center, height);
|
||||
dc.DrawLine(std::min(lane_center, size.x / 2), height,
|
||||
std::max(lane_center, size.x / 2), height);
|
||||
if (m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 ||
|
||||
m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3)
|
||||
dc.DrawLine(size.x / 2, height, size.x / 2, size.y);
|
||||
}
|
||||
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxBrush(wxColour(194, 194, 194)));
|
||||
dc.DrawRectangle(size.x / 2 - FromDIP(14), height - FromDIP(5), FromDIP(28), FromDIP(10));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_ams_model == N3S_AMS || m_ams_model == EXT_AMS) && m_amsinfo.cans.size() != 4){
|
||||
dc.DrawLine(((float)size.x / 2), (0), ((float)size.x / 2), (size.y));
|
||||
if (m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 || m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3){
|
||||
@@ -2360,6 +2464,25 @@ void AMSRoadDownPart::UpdateRight(int nozzle_num, AMSRoadShowMode mode)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void AMSRoadDownPart::SetNozzleCount(int nozzle_num)
|
||||
{
|
||||
const int new_nozzle_num = std::max(1, nozzle_num);
|
||||
if (m_nozzle_num == new_nozzle_num) return;
|
||||
|
||||
m_nozzle_num = new_nozzle_num;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void AMSRoadDownPart::SetSingleSideLayout(bool enabled, AMSPanelPos pos)
|
||||
{
|
||||
if (m_single_side_layout == enabled && m_single_side_pos == pos)
|
||||
return;
|
||||
|
||||
m_single_side_layout = enabled;
|
||||
m_single_side_pos = pos;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void AMSRoadDownPart::OnVamsLoading(bool load, wxColour col /*= AMS_CONTROL_GRAY500*/)
|
||||
{
|
||||
/*m_vams_loading = load;
|
||||
@@ -2433,8 +2556,14 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
/*if (m_road_color.Alpha() == 0) { dc.SetPen(wxPen(*wxWHITE, m_passroad_width, wxPENSTYLE_SOLID)); }
|
||||
else { dc.SetPen(wxPen(m_road_color, m_passroad_width, wxPENSTYLE_SOLID)); }*/
|
||||
dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxPENSTYLE_SOLID));
|
||||
|
||||
auto xpos = left_nozzle_pos.x;
|
||||
if (m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE || m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE){
|
||||
if (m_single_side_layout) {
|
||||
dc.DrawLine(size.x / 2, 0, size.x / 2, size.y);
|
||||
}
|
||||
else if (
|
||||
(m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE ||
|
||||
m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE)){
|
||||
auto length = 50;
|
||||
if (m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE || m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE)
|
||||
length = -13;
|
||||
@@ -2465,6 +2594,9 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
case AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE:
|
||||
dc.DrawLine(left_nozzle_pos.x, 0, left_nozzle_pos.x, size.y);
|
||||
break;
|
||||
case AMSRoadShowMode::AMS_ROAD_MODE_GENERIC:
|
||||
// Generic N-lane geometry is intentionally left unimplemented.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2492,23 +2624,41 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
dc.DrawLine(left_nozzle_pos.x, (size.y / 2), left_nozzle_pos.x + FromDIP(145), (size.y / 2));
|
||||
dc.DrawLine(left_nozzle_pos.x + FromDIP(145), 0, left_nozzle_pos.x + FromDIP(145), (size.y / 2));
|
||||
break;
|
||||
case AMSRoadShowMode::AMS_ROAD_MODE_GENERIC:
|
||||
// Generic N-lane geometry is intentionally left unimplemented.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_single_side_layout) {
|
||||
if (m_pass_road_left_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 ||
|
||||
m_pass_road_left_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3) {
|
||||
dc.SetPen(wxPen(_get_diff_clr(this, m_road_color[1]), 4, wxPENSTYLE_SOLID));
|
||||
dc.DrawLine(size.x / 2, 0, size.x / 2, size.y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE){
|
||||
if (m_nozzle_num == 2) {
|
||||
if (m_nozzle_num == 1) {
|
||||
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE && m_left_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE) {
|
||||
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y / 2));
|
||||
}
|
||||
const auto &nozzle_pos = m_single_side_layout && m_single_side_pos == AMSPanelPos::RIGHT_PANEL
|
||||
? right_nozzle_pos
|
||||
: left_nozzle_pos;
|
||||
dc.DrawLine(nozzle_pos.x, (size.y / 2), nozzle_pos.x, (size.y));
|
||||
}
|
||||
else if (m_nozzle_num == 2) {
|
||||
/*dc.DrawLine(FromDIP(left_nozzle_pos.x), FromDIP(size.y / 2), FromDIP(left_nozzle_pos.x), FromDIP(size.y));
|
||||
dc.DrawLine(FromDIP(right_nozzle_pos.x), FromDIP(size.y / 2), FromDIP(right_nozzle_pos.x), FromDIP(size.y));*/
|
||||
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (left_nozzle_pos.x), (size.y));
|
||||
dc.DrawLine((right_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y));
|
||||
}
|
||||
else {
|
||||
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE && m_left_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE) {
|
||||
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y / 2));
|
||||
}
|
||||
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (left_nozzle_pos.x), (size.y));
|
||||
// Generic N Nozzles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2533,10 +2683,16 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
dc.DrawLine(xpos, size.y / 2, xpos, size.y);*/
|
||||
int x = left_nozzle_pos.x;
|
||||
int len = m_right_road_length;
|
||||
if (m_nozzle_num == 2) {
|
||||
if (m_nozzle_num == 1) {
|
||||
// Keep the single-nozzle path anchored at the left nozzle.
|
||||
}
|
||||
else if (m_nozzle_num == 2) {
|
||||
x = right_nozzle_pos.x;
|
||||
len = len - 14;
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
dc.DrawLine(((x)), (size.y / 2), x + FromDIP(len), (size.y / 2));
|
||||
dc.DrawLine(x + FromDIP(len), (0), x + FromDIP(len), (size.y / 2));
|
||||
dc.DrawLine((x), (size.y / 2), (x), (size.y));
|
||||
@@ -2544,10 +2700,16 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
else{
|
||||
int x = left_nozzle_pos.x;
|
||||
int len = m_right_road_length;
|
||||
if (m_nozzle_num == 2) {
|
||||
if (m_nozzle_num == 1) {
|
||||
// Keep the single-nozzle path anchored at the left nozzle.
|
||||
}
|
||||
else if (m_nozzle_num == 2) {
|
||||
x = right_nozzle_pos.x;
|
||||
len = len - 14;
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
dc.DrawLine(((x)), (size.y / 2), x + FromDIP(len), (size.y / 2));
|
||||
dc.DrawLine(x + FromDIP(len), (0), x + FromDIP(len), (size.y / 2));
|
||||
dc.DrawLine((x), (size.y / 2), (x), (size.y));
|
||||
@@ -2571,7 +2733,7 @@ void AMSRoadDownPart::doRender(wxDC& dc)
|
||||
}
|
||||
|
||||
void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP step) {
|
||||
if (m_nozzle_num >= 2){
|
||||
if (m_nozzle_num == 2){
|
||||
if (pos == AMSPanelPos::LEFT_PANEL){
|
||||
if (m_left_road_length == len && m_pass_road_left_step == step){ return; }
|
||||
m_left_road_length = len;;
|
||||
@@ -2583,7 +2745,7 @@ void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP s
|
||||
m_pass_road_right_step = step;
|
||||
}
|
||||
}
|
||||
else{
|
||||
else if (m_nozzle_num == 1){
|
||||
if (pos == AMSPanelPos::LEFT_PANEL) {
|
||||
if (m_left_road_length == len && m_pass_road_left_step == step && m_right_road_length == -1) { return; }
|
||||
m_left_road_length = len;
|
||||
@@ -2597,6 +2759,9 @@ void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP s
|
||||
m_pass_road_right_step = step;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Generic N Nozzles
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -2615,7 +2780,10 @@ AMSPreview::AMSPreview(wxWindow* parent, wxWindowID id, AMSinfo amsinfo, AMSMode
|
||||
{
|
||||
wxWindow::Create(parent, id, pos);
|
||||
if (itemType == AMSModel::GENERIC_AMS || itemType == AMSModel::AMS_LITE || itemType == AMSModel::N3F_AMS) {
|
||||
create(parent, id, pos, AMS_PREV_FOUR_SIZE);
|
||||
if (itemType == AMSModel::GENERIC_AMS && use_generic_ams_layout(itemType))
|
||||
create(parent, id, pos, wxSize(generic_ams_preview_width(this, static_cast<int>(amsinfo.cans.size())), AMS_PREV_FOUR_SIZE.y));
|
||||
else
|
||||
create(parent, id, pos, AMS_PREV_FOUR_SIZE);
|
||||
}
|
||||
else {
|
||||
create(parent, id, pos, AMS_PREV_SINGLE_SIZE);
|
||||
@@ -2650,7 +2818,13 @@ void AMSPreview::UpdateInfo(AMSinfo amsinfo)
|
||||
m_amsinfo = amsinfo;
|
||||
m_ams_item_type = amsinfo.ams_type;
|
||||
|
||||
if (m_ams_item_type == AMSModel::GENERIC_AMS || m_ams_item_type == AMSModel::AMS_LITE || m_ams_item_type == AMSModel::N3F_AMS) {
|
||||
if (m_ams_item_type == AMSModel::GENERIC_AMS && use_generic_ams_layout(m_ams_item_type)) {
|
||||
const wxSize generic_size(generic_ams_preview_width(this, static_cast<int>(m_amsinfo.cans.size())), AMS_PREV_FOUR_SIZE.y);
|
||||
SetSize(generic_size);
|
||||
SetMinSize(generic_size);
|
||||
SetMaxSize(generic_size);
|
||||
}
|
||||
else if (m_ams_item_type == AMSModel::GENERIC_AMS || m_ams_item_type == AMSModel::AMS_LITE || m_ams_item_type == AMSModel::N3F_AMS) {
|
||||
SetMinSize(AMS_PREV_FOUR_SIZE);
|
||||
SetMaxSize(AMS_PREV_FOUR_SIZE);
|
||||
} else {
|
||||
@@ -2758,8 +2932,41 @@ void AMSPreview::doRender(wxDC &dc)
|
||||
dc.DrawRoundedRectangle(0, 0, size.x, size.y, FromDIP(3));
|
||||
|
||||
auto left = 0;
|
||||
//four slot
|
||||
if (m_ams_item_type != AMSModel::EXT_AMS && m_ams_item_type != AMSModel::N3S_AMS){
|
||||
// Generic AMS previews use the same normalized lane centers as the card row and connector.
|
||||
if (m_ams_item_type == AMSModel::GENERIC_AMS && use_generic_ams_layout(m_ams_item_type)) {
|
||||
const int lane_count = static_cast<int>(m_amsinfo.cans.size());
|
||||
const auto lane_centers = generic_ams_lane_centers(lane_count, size.x);
|
||||
const int cube_width = FromDIP(9);
|
||||
const int cube_height = FromDIP(14);
|
||||
|
||||
for (int lane = 0; lane < lane_count; ++lane) {
|
||||
const Caninfo &can = m_amsinfo.cans[lane];
|
||||
const int cube_left = lane_centers[lane] - cube_width / 2;
|
||||
const int cube_top = (size.y - cube_height) / 2;
|
||||
wxRect rect(cube_left, cube_top, cube_width, cube_height);
|
||||
|
||||
if (can.material_colour.Alpha() == 0) {
|
||||
if (wxGetApp().dark_mode())
|
||||
dc.DrawBitmap(m_ts_bitmap_cube_dark.bmp(), cube_left - FromDIP(1), (size.y - m_ts_bitmap_cube_dark.GetBmpHeight()) / 2);
|
||||
else
|
||||
dc.DrawBitmap(m_ts_bitmap_cube.bmp(), cube_left - FromDIP(1), (size.y - m_ts_bitmap_cube.GetBmpHeight()) / 2);
|
||||
}
|
||||
else if (can.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) {
|
||||
dc.SetPen(wxPen(wxColor(0, 0, 0)));
|
||||
dc.DrawLine(rect.GetRight() - FromDIP(1), rect.GetTop() + FromDIP(1),
|
||||
rect.GetLeft() + FromDIP(1), rect.GetBottom() - FromDIP(1));
|
||||
}
|
||||
else {
|
||||
wxColour color = can.material_colour;
|
||||
change_the_opacity(color);
|
||||
dc.SetPen(wxPen(*wxTRANSPARENT_PEN));
|
||||
dc.SetBrush(wxBrush(color));
|
||||
dc.DrawRoundedRectangle(rect, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// four slot
|
||||
else if (m_ams_item_type != AMSModel::EXT_AMS && m_ams_item_type != AMSModel::N3S_AMS){
|
||||
left = FromDIP(8);
|
||||
for (std::vector<Caninfo>::iterator iter = m_amsinfo.cans.begin(); iter != m_amsinfo.cans.end(); iter++) {
|
||||
|
||||
@@ -3158,10 +3365,19 @@ AmsItem::AmsItem(wxWindow *parent,AMSinfo info, AMSModel model, AMSPanelPos pos
|
||||
m_panel_pos = pos;
|
||||
|
||||
if (m_ams_model == AMSModel::GENERIC_AMS){
|
||||
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, AMS_CANS_WINDOW_SIZE);
|
||||
SetSize(AMS_CANS_WINDOW_SIZE);
|
||||
SetMinSize(AMS_CANS_WINDOW_SIZE);
|
||||
SetMaxSize(AMS_CANS_WINDOW_SIZE);
|
||||
const int lane_count = static_cast<int>(m_info.cans.size());
|
||||
const int width = use_generic_ams_layout(m_ams_model) && lane_count <= 8
|
||||
? generic_ams_layout_width(this, lane_count)
|
||||
: FromDIP(264);
|
||||
const wxSize generic_size(width, FromDIP(174));
|
||||
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, generic_size);
|
||||
SetSize(generic_size);
|
||||
SetMinSize(generic_size);
|
||||
// For large lane counts the card is a viewport, not the lane strip. Let
|
||||
// its parent allocate the available width; the scrolled child retains
|
||||
// the wider virtual content size.
|
||||
if (!(use_generic_ams_layout(m_ams_model) && lane_count > 8))
|
||||
SetMaxSize(generic_size);
|
||||
}
|
||||
else{
|
||||
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, AMS_SINGLE_CAN_SIZE);
|
||||
@@ -3187,12 +3403,43 @@ void AmsItem::create(wxWindow *parent)
|
||||
if (m_ams_model != AMSModel::AMS_LITE) {
|
||||
sizer_can = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer_item = new wxBoxSizer(wxVERTICAL);
|
||||
auto it = m_info.cans.begin();
|
||||
for (; it != m_info.cans.end(); it++) {
|
||||
AddCan(*it, m_can_count, m_info.cans.size(), sizer_can);
|
||||
const bool generic_layout = use_generic_ams_layout(m_ams_model);
|
||||
const int lane_count = static_cast<int>(m_info.cans.size());
|
||||
const int content_width = generic_layout ? generic_ams_layout_width(this, lane_count) : 0;
|
||||
const bool scroll_lanes = generic_layout && lane_count > 8;
|
||||
const auto lane_centers = generic_layout
|
||||
? generic_ams_lane_centers(lane_count, content_width)
|
||||
: std::vector<int>();
|
||||
|
||||
m_can_parent = this;
|
||||
if (scroll_lanes) {
|
||||
m_can_scroll = new wxScrolledWindow(this, wxID_ANY);
|
||||
m_can_scroll->SetScrollRate(FromDIP(10), 0);
|
||||
m_can_scroll->SetMinSize(wxSize(FromDIP(264), FromDIP(143)));
|
||||
m_can_scroll->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_LIB_BK_COLOUR));
|
||||
m_can_scroll_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_can_scroll->SetSizer(m_can_scroll_sizer);
|
||||
m_can_parent = m_can_scroll;
|
||||
}
|
||||
|
||||
int previous_right = 0;
|
||||
for (int can_index = 0; can_index < lane_count; ++can_index) {
|
||||
if (generic_layout) {
|
||||
const int left = lane_centers[can_index] - FromDIP(26);
|
||||
sizer_can->AddSpacer(std::max(0, left - previous_right));
|
||||
AddCan(m_info.cans[can_index], m_can_count, m_info.cans.size(), sizer_can);
|
||||
previous_right = left + FromDIP(52);
|
||||
}
|
||||
else {
|
||||
AddCan(m_info.cans[can_index], m_can_count, m_info.cans.size(), sizer_can);
|
||||
}
|
||||
m_can_count++;
|
||||
}
|
||||
it = m_info.cans.begin();
|
||||
if (generic_layout)
|
||||
sizer_can->AddSpacer(std::max(0, content_width - previous_right));
|
||||
if (generic_layout)
|
||||
sizer_can->SetMinSize(wxSize(content_width, -1));
|
||||
|
||||
//auto road_panel = new wxWindow(this, wxID_ANY);
|
||||
//auto road_panel = new wxPanel(this, wxID_ANY);
|
||||
//road_panel->SetSize(AMS_CAN_ROAD_SIZE);
|
||||
@@ -3207,11 +3454,18 @@ void AmsItem::create(wxWindow *parent)
|
||||
sizer_item->Add(m_ext_image, 0, wxALIGN_CENTER, 0);
|
||||
}
|
||||
}
|
||||
m_panel_road = new AMSRoadUpPart(this, wxID_ANY, m_info, m_ams_model);
|
||||
m_panel_road = new AMSRoadUpPart(m_can_parent, wxID_ANY, m_info, m_ams_model);
|
||||
|
||||
sizer_item->Add(sizer_can, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
//sizer_item->Add(m_panel_road, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
sizer_item->Add(m_panel_road, 1, wxEXPAND);
|
||||
if (scroll_lanes) {
|
||||
m_can_scroll_sizer->Add(sizer_can, 0, wxALIGN_LEFT, 0);
|
||||
m_can_scroll_sizer->Add(m_panel_road, 0, wxALIGN_LEFT, 0);
|
||||
sizer_item->Add(m_can_scroll, 1, wxEXPAND);
|
||||
}
|
||||
else {
|
||||
sizer_item->Add(sizer_can, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
//sizer_item->Add(m_panel_road, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
sizer_item->Add(m_panel_road, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
SetSizer(sizer_item);
|
||||
}
|
||||
@@ -3235,13 +3489,17 @@ void AmsItem::create(wxWindow *parent)
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
if (m_can_scroll) {
|
||||
m_can_scroll->Layout();
|
||||
m_can_scroll->FitInside();
|
||||
}
|
||||
Thaw();
|
||||
//Refresh();
|
||||
}
|
||||
|
||||
void AmsItem::AddCan(Caninfo caninfo, int canindex, int maxcan, wxBoxSizer* sizer)
|
||||
{
|
||||
auto amscan = new wxWindow(this, wxID_ANY);
|
||||
auto amscan = new wxWindow(m_can_parent ? m_can_parent : this, wxID_ANY);
|
||||
|
||||
amscan->SetSize(wxSize(FromDIP(52), FromDIP(109)));
|
||||
amscan->SetMinSize(wxSize(FromDIP(52), FromDIP(109)));
|
||||
@@ -3322,7 +3580,8 @@ void AmsItem::AddCan(Caninfo caninfo, int canindex, int maxcan, wxBoxSizer* size
|
||||
amscan->Layout();
|
||||
amscan->Fit();
|
||||
|
||||
sizer->Add(amscan, 0, wxUP | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
const int margin = use_generic_ams_layout(m_ams_model) ? 0 : FromDIP(5);
|
||||
sizer->Add(amscan, 0, wxUP | wxLEFT | wxRIGHT, margin);
|
||||
|
||||
m_can_lib_list[caninfo.can_id] = m_panel_lib;
|
||||
//m_can_road_list[caninfo.can_id] = m_panel_road;
|
||||
@@ -3447,6 +3706,10 @@ void AmsItem::UpdateInfo(AMSinfo info)
|
||||
}
|
||||
|
||||
Layout();
|
||||
if (m_can_scroll) {
|
||||
m_can_scroll->Layout();
|
||||
m_can_scroll->FitInside();
|
||||
}
|
||||
}
|
||||
|
||||
void AmsItem::SetDefSelectCan()
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <wx/string.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/scrolwin.h>
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSwitch.h" // Orca: DevFilaSwitch::SwitchPos for inlet-aware AMS placement
|
||||
|
||||
@@ -96,6 +97,7 @@ enum class AMSRoadShowMode : int {
|
||||
AMS_ROAD_MODE_SINGLE,
|
||||
AMS_ROAD_MODE_SINGLE_N3S,
|
||||
AMS_ROAD_MODE_AMS_LITE,
|
||||
AMS_ROAD_MODE_GENERIC,
|
||||
AMS_ROAD_MODE_NONE
|
||||
};
|
||||
|
||||
@@ -480,6 +482,7 @@ public:
|
||||
//AMSextruderImage *m_amsSextruder{nullptr};
|
||||
AMSextruderImage* m_left_extruder = nullptr;
|
||||
AMSextruderImage* m_right_extruder = nullptr;
|
||||
std::vector<AMSextruderImage*> m_nozzle_extruders;
|
||||
AMSextruder(wxWindow *parent, wxWindowID id, int nozzle_num, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize);
|
||||
~AMSextruder();
|
||||
|
||||
@@ -681,6 +684,8 @@ public:
|
||||
// void Update(AMSRoadDownPartMode nozzle, AMSRoadShowMode left_mode, AMSRoadShowMode right_mode, int left_len, int right_len);
|
||||
void UpdateLeft(int nozzle_num, AMSRoadShowMode mode);
|
||||
void UpdateRight(int nozzle_num, AMSRoadShowMode mode);
|
||||
void SetNozzleCount(int nozzle_num);
|
||||
void SetSingleSideLayout(bool enabled, AMSPanelPos pos = AMSPanelPos::LEFT_PANEL);
|
||||
|
||||
void OnVamsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500);
|
||||
void SetPassRoadColour(bool left, wxColour col);
|
||||
@@ -698,6 +703,8 @@ private:
|
||||
AMSRoadShowMode m_left_rode_mode = {AMSRoadShowMode::AMS_ROAD_MODE_FOUR};
|
||||
AMSRoadShowMode m_right_rode_mode = {AMSRoadShowMode::AMS_ROAD_MODE_FOUR};
|
||||
bool m_selected = {false};
|
||||
bool m_single_side_layout = {false};
|
||||
AMSPanelPos m_single_side_pos = {AMSPanelPos::LEFT_PANEL};
|
||||
|
||||
int m_left_road_length = {-1};
|
||||
int m_right_road_length = {-1};
|
||||
@@ -864,6 +871,9 @@ private:
|
||||
|
||||
std::map<std::string, AMSLib*> m_can_lib_list;
|
||||
//std::map<std::string, AMSRoad*> m_can_road_list;
|
||||
wxScrolledWindow* m_can_scroll = { nullptr };
|
||||
wxBoxSizer* m_can_scroll_sizer = { nullptr };
|
||||
wxWindow* m_can_parent = { nullptr };
|
||||
AMSRoadUpPart* m_panel_road = { nullptr };
|
||||
std::map<std::string, AMSrefresh*> m_can_refresh_list;
|
||||
AMSHumidity* m_humidity = { nullptr };
|
||||
|
||||
@@ -420,6 +420,14 @@ ExtruderBadge::ExtruderBadge(wxWindow* parent) : wxPanel(parent)
|
||||
|
||||
void ExtruderBadge::SetExtruderInfo(int extruder_id, const std::string& diameter, const NozzleVolumeType& volume_type)
|
||||
{
|
||||
// The badge renders exactly two extruders (left/right). A generic non-BBL printer with
|
||||
// N > 2 can report an out-of-range id here (see MultiNozzleStatusTable::UpdateRackInfo);
|
||||
// reject it rather than writing past the two-element lists.
|
||||
if (extruder_id < 0 || extruder_id >= static_cast<int>(m_diameter_list.size())) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": out-of-range extruder_id " << extruder_id;
|
||||
return;
|
||||
}
|
||||
|
||||
m_diameter_list[extruder_id] = diameter;
|
||||
m_volume_type_list[extruder_id] = volume_type;
|
||||
|
||||
@@ -867,14 +875,21 @@ void MultiNozzleStatusTable::UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack)
|
||||
bool has_right = false;
|
||||
for (auto& elem : nozzles_in_extruder) {
|
||||
auto& nozzle = elem.second;
|
||||
int extruder_id = nozzle.AtLeftExtruder() ? 0 : 1;
|
||||
if (nozzle.AtRightExtruder())
|
||||
has_right = true;
|
||||
|
||||
int extruder_id{};
|
||||
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||
extruder_id = nozzle.AtLeftExtruder() ? 0 : 1;
|
||||
if (nozzle.AtRightExtruder())
|
||||
has_right = true;
|
||||
}
|
||||
else
|
||||
extruder_id = nozzle.GetExtruderId();
|
||||
|
||||
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.m_nozzle_flow);
|
||||
|
||||
m_badge->SetExtruderInfo(extruder_id, format_diameter_to_str(nozzle.GetNozzleDiameter()), volume_type);
|
||||
}
|
||||
// TODO: Update for N extruders
|
||||
m_badge->SetExtruderValid(has_right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,9 @@ void TempInput::SetFinish()
|
||||
wxCommandEvent event(wxCUSTOMEVT_SET_TEMP_FINISH);
|
||||
event.SetInt(temp_type);
|
||||
event.SetString(wxString::Format("%d", m_input_type));
|
||||
// N-extruder temp controls all share TEMP_OF_NORMAL_TYPE, so the string payload above can't
|
||||
// tell them apart; carry widget identity so the listener can find which one fired.
|
||||
event.SetEventObject(this);
|
||||
wxPostEvent(this->GetParent(), event);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "../GUI/DeviceCore/DevConfig.h"
|
||||
#include "../GUI/DeviceCore/DevExtruderSystem.h"
|
||||
#include "../GUI/DeviceCore/DevManager.h"
|
||||
#include "../GUI/DeviceCore/DevStorage.h"
|
||||
#include "libslic3r/FlushVolCalc.hpp"
|
||||
#include "../GUI/Plater.hpp"
|
||||
#include <memory>
|
||||
#include "slic3r/GUI/Jobs/Worker.hpp"
|
||||
|
||||
@@ -365,6 +365,7 @@ void StyleManager::init_trunc_names(float max_width) {
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
// for get DPI
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
|
||||
|
||||
@@ -553,6 +554,8 @@ bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile>
|
||||
return true;
|
||||
}
|
||||
|
||||
#include <libslic3r/AppConfig.hpp>
|
||||
#include "WxFontUtils.hpp"
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
// StylesSerializable
|
||||
|
||||
@@ -89,6 +89,7 @@ using fn_ft_job_get_result = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeo
|
||||
using fn_ft_tunnel_start_job = ft_err(FT_CALL *)(FT_TunnelHandle *, FT_JobHandle *);
|
||||
using fn_ft_job_cancel = ft_err(FT_CALL *)(FT_JobHandle *);
|
||||
|
||||
using fn_ft_job_msg_destroy = void(FT_CALL *)(ft_job_msg *);
|
||||
using fn_ft_job_set_msg_cb = ft_err(FT_CALL *)(FT_JobHandle *, void(FT_CALL *)(void *user, ft_job_msg msg), void *user);
|
||||
using fn_ft_job_try_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, ft_job_msg *out_msg);
|
||||
using fn_ft_job_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeout_ms, ft_job_msg *out_msg);
|
||||
|
||||
@@ -1057,6 +1057,25 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::
|
||||
}
|
||||
}
|
||||
|
||||
// Tool selection is printer-specific G-code. Moonraker forwards the
|
||||
// conventional T<index> command to Klipper, where the printer can
|
||||
// implement logical selection or a physical toolhead attach macro.
|
||||
if (cmd == "select_extruder") {
|
||||
if (!json["print"].contains("extruder_index") || !json["print"]["extruder_index"].is_number_integer()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: select_extruder missing integer extruder_index, full json: " << json_str;
|
||||
return BAMBU_NETWORK_ERR_INVALID_RESULT;
|
||||
}
|
||||
|
||||
const int extruder_idx = json["print"]["extruder_index"].get<int>();
|
||||
if (extruder_idx < 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: select_extruder received negative extruder_index: " << extruder_idx;
|
||||
return BAMBU_NETWORK_ERR_INVALID_RESULT;
|
||||
}
|
||||
|
||||
send_gcode_async(dev_id, "T" + std::to_string(extruder_idx));
|
||||
return BAMBU_NETWORK_SUCCESS;
|
||||
}
|
||||
|
||||
if (cmd == "home") {
|
||||
send_gcode_async(dev_id, "G28");
|
||||
return BAMBU_NETWORK_SUCCESS;
|
||||
@@ -1153,7 +1172,7 @@ bool MoonrakerPrinterAgent::query_printer_status(const std::string& base_url,
|
||||
nlohmann::json& status,
|
||||
std::string& error) const
|
||||
{
|
||||
std::string url = join_url(base_url, "/printer/objects/query?print_stats&virtual_sdcard&extruder&heater_bed&fan");
|
||||
std::string url = join_url(base_url, "/printer/objects/query?print_stats&virtual_sdcard&toolhead&extruder&heater_bed&fan");
|
||||
|
||||
std::string response_body;
|
||||
bool success = false;
|
||||
@@ -1566,11 +1585,12 @@ void MoonrakerPrinterAgent::run_status_stream(std::string dev_id, std::string ba
|
||||
}
|
||||
|
||||
for (const auto& name : this->available_objects) {
|
||||
if (name == "extruder" || name.rfind("extruder", 0) == 0) {
|
||||
const bool is_main_extruder = name == "extruder";
|
||||
const bool is_additional_extruder =
|
||||
name.rfind("extruder", 0) == 0 && name.size() > 8 &&
|
||||
std::all_of(name.begin() + 8, name.end(), [](unsigned char c) { return std::isdigit(c) != 0; });
|
||||
if (is_main_extruder || is_additional_extruder) {
|
||||
subscribe_objects.insert(name);
|
||||
if (name == "extruder") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1866,6 +1886,81 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
|
||||
}
|
||||
}
|
||||
|
||||
// MachineObject::parse_new_info() only parses device.extruder when the four
|
||||
// legacy new-protocol fields are present. Moonraker has no equivalents, so
|
||||
// empty values act as compatibility markers without enabling any features.
|
||||
std::map<int, const nlohmann::json*> extruder_status;
|
||||
for (const auto& item : status_cache.items()) {
|
||||
int extruder_id = -1;
|
||||
if (item.key() == "extruder") {
|
||||
extruder_id = 0;
|
||||
} else if (item.key().rfind("extruder", 0) == 0 && item.key().size() > 8 &&
|
||||
std::all_of(item.key().begin() + 8, item.key().end(), [](unsigned char c) { return std::isdigit(c) != 0; })) {
|
||||
try {
|
||||
extruder_id = std::stoi(item.key().substr(8));
|
||||
} catch (...) {
|
||||
extruder_id = -1;
|
||||
}
|
||||
}
|
||||
if (extruder_id >= 0 && item.value().is_object()) {
|
||||
extruder_status.emplace(extruder_id, &item.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (!extruder_status.empty()) {
|
||||
auto& device_extruder = payload["print"]["device"]["extruder"];
|
||||
|
||||
int current_extruder_id = 0;
|
||||
if (status_cache.contains("toolhead") && status_cache["toolhead"].is_object() &&
|
||||
status_cache["toolhead"].contains("extruder") && status_cache["toolhead"]["extruder"].is_string()) {
|
||||
const std::string active_extruder = status_cache["toolhead"]["extruder"].get<std::string>();
|
||||
if (active_extruder != "extruder" && active_extruder.rfind("extruder", 0) == 0 && active_extruder.size() > 8 &&
|
||||
std::all_of(active_extruder.begin() + 8, active_extruder.end(), [](unsigned char c) { return std::isdigit(c) != 0; })) {
|
||||
try {
|
||||
const int parsed_id = std::stoi(active_extruder.substr(8));
|
||||
if (parsed_id >= 0 && parsed_id < 16 && extruder_status.count(parsed_id) != 0) {
|
||||
current_extruder_id = parsed_id;
|
||||
}
|
||||
} catch (...) {
|
||||
// Keep the main extruder as the fallback for malformed status data.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int extruder_count = std::min<int>(static_cast<int>(extruder_status.size()), 0xF);
|
||||
device_extruder["state"] = extruder_count | (current_extruder_id << 4);
|
||||
device_extruder["info"] = nlohmann::json::array();
|
||||
|
||||
auto encoded_temperature = [](const nlohmann::json& status, const char* key) {
|
||||
if (!status.contains(key) || !status[key].is_number()) {
|
||||
return 0;
|
||||
}
|
||||
const double temperature = status[key].get<double>();
|
||||
return std::clamp(static_cast<int>(temperature + 0.5), 0, 65535);
|
||||
};
|
||||
|
||||
for (const auto& [extruder_id, status] : extruder_status) {
|
||||
const int current_temperature = encoded_temperature(*status, "temperature");
|
||||
const int target_temperature = encoded_temperature(*status, "target");
|
||||
device_extruder["info"].push_back({
|
||||
{"id", extruder_id},
|
||||
{"filam_bak", nlohmann::json::array()},
|
||||
{"info", 1 << 3}, // The configured Moonraker extruder has a toolhead.
|
||||
{"temp", current_temperature | (target_temperature << 16)},
|
||||
{"spre", 0},
|
||||
{"snow", 0},
|
||||
{"star", 0},
|
||||
{"stat", 0},
|
||||
{"hnow", 0},
|
||||
});
|
||||
}
|
||||
|
||||
payload["print"]["cfg"] = "";
|
||||
payload["print"]["fun"] = "";
|
||||
payload["print"]["aux"] = "";
|
||||
payload["print"]["stat"] = "";
|
||||
}
|
||||
|
||||
if (status_cache.contains("heater_bed") && status_cache["heater_bed"].is_object()) {
|
||||
const auto& bed = status_cache["heater_bed"];
|
||||
if (bed.contains("temperature") && bed["temperature"].is_number()) {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/UpdateDialogs.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
namespace Slic3r {
|
||||
namespace Utils {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
// Generic command / response TCP telnet like console class.
|
||||
// Used by the MKS host to send G-code commands to test connection ("M105") and to start printing ("M23 filename", "M24").
|
||||
class TCPConsole
|
||||
{
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
public:
|
||||
TCPConsole() : m_resolver(m_io_context), m_socket(m_io_context) { set_defaults(); }
|
||||
TCPConsole(const std::string& host_name, const std::string& port_name) : m_resolver(m_io_context), m_socket(m_io_context)
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
|
||||
namespace net = boost::asio; // from <boost/asio.hpp>
|
||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||
|
||||
class WebSocketClient {
|
||||
public:
|
||||
@@ -31,7 +36,7 @@ public:
|
||||
}
|
||||
try {
|
||||
// Close the WebSocket connection
|
||||
ws_.close(boost::beast::websocket::close_code::normal);
|
||||
ws_.close(websocket::close_code::normal);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
}
|
||||
@@ -44,7 +49,7 @@ public:
|
||||
auto const results = resolver_.resolve(host, port);
|
||||
|
||||
// Make the connection on the IP address we get from a lookup
|
||||
auto ep = boost::asio::connect(ws_.next_layer(), results);
|
||||
auto ep = net::connect(ws_.next_layer(), results);
|
||||
std::string _host = host;
|
||||
//if _host last char is '/', remove it
|
||||
if(_host.size()>0&&_host[host.size()-1] == '/'){
|
||||
@@ -53,10 +58,10 @@ public:
|
||||
|
||||
// _host += ':' + std::to_string(ep.port());
|
||||
// Set a decorator to change the User-Agent of the handshake
|
||||
ws_.set_option(boost::beast::websocket::stream_base::decorator(
|
||||
[](boost::beast::websocket::request_type& req)
|
||||
ws_.set_option(websocket::stream_base::decorator(
|
||||
[](websocket::request_type& req)
|
||||
{
|
||||
req.set(boost::beast::http::field::user_agent,"ElegooSlicer");
|
||||
req.set(http::field::user_agent,"ElegooSlicer");
|
||||
}));
|
||||
// Perform the WebSocket handshake
|
||||
ws_.handshake(_host, path);
|
||||
@@ -65,25 +70,25 @@ public:
|
||||
|
||||
void send(const std::string& message){
|
||||
// Send a message
|
||||
ws_.write(boost::asio::buffer(message));
|
||||
ws_.write(net::buffer(message));
|
||||
}
|
||||
|
||||
std::string receive(int timeout = 0){
|
||||
// This buffer will hold the incoming message
|
||||
boost::beast::flat_buffer buffer;
|
||||
beast::flat_buffer buffer;
|
||||
|
||||
// Read a message into our buffer
|
||||
ws_.read(buffer);
|
||||
|
||||
// Return the message as a string
|
||||
return boost::beast::buffers_to_string(buffer.data());
|
||||
return beast::buffers_to_string(buffer.data());
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
boost::asio::io_context ioc_;
|
||||
boost::asio::ip::tcp::resolver resolver_;
|
||||
boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws_;
|
||||
net::io_context ioc_;
|
||||
tcp::resolver resolver_;
|
||||
websocket::stream<tcp::socket> ws_;
|
||||
bool is_connect;
|
||||
};
|
||||
|
||||
|
||||
@@ -115,9 +115,6 @@ static const std::unordered_map<std::string, AuditEventCategory> audit_event_cat
|
||||
{"subprocess.Popen", AuditEventCategory::ProcessCreate},
|
||||
{"_winapi.CreateProcess", AuditEventCategory::ProcessCreate},
|
||||
{"_posixsubprocess.fork_exec", AuditEventCategory::ProcessCreate},
|
||||
|
||||
// processreplace: exec* replaces the current process image rather than spawning a child
|
||||
{"os.exec", AuditEventCategory::ProcessReplace},
|
||||
};
|
||||
|
||||
// Returns the category event_name belongs to, or AuditEventCategory::None when it isn't audited.
|
||||
@@ -711,7 +708,6 @@ static const std::unordered_map<std::string, std::vector<Py_ssize_t>> audit_targ
|
||||
{"pty.spawn", {0}},
|
||||
{"_winapi.CreateProcess", {1, 0}},
|
||||
{"_posixsubprocess.fork_exec", {0}},
|
||||
{"os.exec", {0}},
|
||||
};
|
||||
|
||||
AuditEventCategory open_category(PyObject* args)
|
||||
@@ -765,7 +761,6 @@ std::vector<std::string>* permission_list_for(AuditEventCategory category, Plugi
|
||||
case AuditEventCategory::Http: return &permissions.network_http;
|
||||
case AuditEventCategory::Socket: return &permissions.network_socket;
|
||||
case AuditEventCategory::ProcessCreate: return &permissions.process;
|
||||
case AuditEventCategory::ProcessReplace: return &permissions.process;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -837,8 +832,6 @@ wxString audit_message(AuditEventCategory category, const wxString& plugin_name,
|
||||
return wxString::Format(_L("Plugin \"%s\" is requesting to open a network connection to:\n%s"), plugin_name, target_list);
|
||||
case AuditEventCategory::ProcessCreate:
|
||||
return wxString::Format(_L("Plugin \"%s\" is requesting to run the following command(s):\n%s"), plugin_name, target_list);
|
||||
case AuditEventCategory::ProcessReplace:
|
||||
return wxString::Format(_L("Plugin \"%s\" is requesting to replace the running application with:\n%s"), plugin_name, target_list);
|
||||
default:
|
||||
return wxString::Format(_L("Plugin \"%s\" is requesting permission for the Python audit event \"%s\"."), plugin_name, event_name);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ enum class AuditEventCategory {
|
||||
Http,
|
||||
Socket,
|
||||
ProcessCreate,
|
||||
ProcessReplace,
|
||||
Threading,
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <wx/app.h>
|
||||
#include <wx/defs.h>
|
||||
#include <wx/thread.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/toplevel.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/progdlg.h>
|
||||
@@ -416,44 +415,6 @@ struct UiDockPanelHandle
|
||||
int id{0};
|
||||
};
|
||||
|
||||
// Polls until the plater is shown on screen, then runs the callback with it (null when the app
|
||||
// is closing instead) and deletes itself. A plugin loaded at startup opens its panes from
|
||||
// on_load, before the main window exists, and a pane added before that window is laid out is
|
||||
// sized against the unsized frame and keeps that width (AuiMgr::track_docked_size).
|
||||
class PlaterShownWaiter : public wxTimer
|
||||
{
|
||||
public:
|
||||
explicit PlaterShownWaiter(std::function<void(GUI::Plater*)> fn) : m_fn(std::move(fn)) {}
|
||||
|
||||
// True once the callback ran.
|
||||
bool try_run()
|
||||
{
|
||||
const bool closing = GUI::wxGetApp().is_closing();
|
||||
GUI::Plater* plater = closing ? nullptr : GUI::wxGetApp().plater();
|
||||
if (!closing && (plater == nullptr || !plater->IsShownOnScreen()))
|
||||
return false;
|
||||
Stop();
|
||||
m_fn(plater);
|
||||
// Off the timer callback's stack: wxGTK's timeout callback still reads the timer after Notify().
|
||||
GUI::wxGetApp().CallAfter([this]() { delete this; });
|
||||
return true;
|
||||
}
|
||||
|
||||
void Notify() override { try_run(); }
|
||||
|
||||
private:
|
||||
std::function<void(GUI::Plater*)> m_fn;
|
||||
};
|
||||
|
||||
void run_when_plater_shown(std::function<void(GUI::Plater*)> fn)
|
||||
{
|
||||
GUI::wxGetApp().CallAfter([fn = std::move(fn)]() mutable {
|
||||
auto* waiter = new PlaterShownWaiter(std::move(fn));
|
||||
if (!waiter->try_run())
|
||||
waiter->Start(100);
|
||||
});
|
||||
}
|
||||
|
||||
py::object ui_create_dock_panel(const std::string& html, const std::string& title, int width, int height,
|
||||
py::object on_message, py::object on_close, const std::string& dock)
|
||||
{
|
||||
@@ -473,13 +434,14 @@ py::object ui_create_dock_panel(const std::string& html, const std::string& titl
|
||||
const int new_id = UiRegistry::instance().reserve_id();
|
||||
UiRegistry::instance().bind(new_id, nullptr, plugin_key);
|
||||
|
||||
run_when_plater_shown([new_id, plugin_key, html, title, dock, w, h,
|
||||
msg_adapter = std::move(msg_adapter),
|
||||
close_holder = std::move(close_holder)](GUI::Plater* plater) mutable {
|
||||
GUI::wxGetApp().CallAfter([new_id, plugin_key, html, title, dock, w, h,
|
||||
msg_adapter = std::move(msg_adapter),
|
||||
close_holder = std::move(close_holder)]() mutable {
|
||||
if (!UiRegistry::instance().is_open(new_id))
|
||||
return;
|
||||
|
||||
if (plater == nullptr) {
|
||||
GUI::Plater* plater = GUI::wxGetApp().plater();
|
||||
if (plater == nullptr || GUI::wxGetApp().is_closing()) {
|
||||
UiRegistry::instance().remove(new_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,18 +17,20 @@
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// Put serializers in correct ADL namespaces for each type
|
||||
|
||||
namespace Slic3r {
|
||||
namespace FilamentGroupUtils {
|
||||
|
||||
inline void to_json(nlohmann::json& j, const Color& c) {
|
||||
inline void to_json(json& j, const Color& c) {
|
||||
char buf[10];
|
||||
snprintf(buf, sizeof(buf), "#%02X%02X%02X%02X", c.r, c.g, c.b, c.a);
|
||||
j = std::string(buf);
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, Color& c) {
|
||||
inline void from_json(const json& j, Color& c) {
|
||||
std::string s = j.get<std::string>();
|
||||
if (s.size() >= 7 && s[0] == '#') {
|
||||
c.r = (unsigned char)std::stoi(s.substr(1, 2), nullptr, 16);
|
||||
@@ -38,8 +40,8 @@ inline void from_json(const nlohmann::json& j, Color& c) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json& j, const FilamentInfo& fi) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const FilamentInfo& fi) {
|
||||
j = json{
|
||||
{"color", fi.color},
|
||||
{"type", fi.type},
|
||||
{"is_support", fi.is_support},
|
||||
@@ -47,15 +49,15 @@ inline void to_json(nlohmann::json& j, const FilamentInfo& fi) {
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentInfo& fi) {
|
||||
inline void from_json(const json& j, FilamentInfo& fi) {
|
||||
fi.color = j.at("color").get<Color>();
|
||||
j.at("type").get_to(fi.type);
|
||||
j.at("is_support").get_to(fi.is_support);
|
||||
fi.usage_type = (FilamentUsageType)j.at("usage_type").get<int>();
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json& j, const MachineFilamentInfo& mfi) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const MachineFilamentInfo& mfi) {
|
||||
j = json{
|
||||
{"color", mfi.color},
|
||||
{"type", mfi.type},
|
||||
{"is_support", mfi.is_support},
|
||||
@@ -65,7 +67,7 @@ inline void to_json(nlohmann::json& j, const MachineFilamentInfo& mfi) {
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, MachineFilamentInfo& mfi) {
|
||||
inline void from_json(const json& j, MachineFilamentInfo& mfi) {
|
||||
mfi.color = j.at("color").get<Color>();
|
||||
j.at("type").get_to(mfi.type);
|
||||
j.at("is_support").get_to(mfi.is_support);
|
||||
@@ -78,8 +80,8 @@ inline void from_json(const nlohmann::json& j, MachineFilamentInfo& mfi) {
|
||||
|
||||
namespace MultiNozzleUtils {
|
||||
|
||||
inline void to_json(nlohmann::json& j, const NozzleInfo& ni) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const NozzleInfo& ni) {
|
||||
j = json{
|
||||
{"diameter", ni.diameter},
|
||||
{"volume_type", (int)ni.volume_type},
|
||||
{"extruder_id", ni.extruder_id},
|
||||
@@ -87,15 +89,15 @@ inline void to_json(nlohmann::json& j, const NozzleInfo& ni) {
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, NozzleInfo& ni) {
|
||||
inline void from_json(const json& j, NozzleInfo& ni) {
|
||||
j.at("diameter").get_to(ni.diameter);
|
||||
ni.volume_type = (NozzleVolumeType)j.at("volume_type").get<int>();
|
||||
j.at("extruder_id").get_to(ni.extruder_id);
|
||||
j.at("group_id").get_to(ni.group_id);
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json& j, const FilamentChangeTimeParams& p) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const FilamentChangeTimeParams& p) {
|
||||
j = json{
|
||||
{"selector_load_time", p.selector_load_time},
|
||||
{"selector_unload_time", p.selector_unload_time},
|
||||
{"standard_load_time", p.standard_load_time},
|
||||
@@ -103,7 +105,7 @@ inline void to_json(nlohmann::json& j, const FilamentChangeTimeParams& p) {
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentChangeTimeParams& p) {
|
||||
inline void from_json(const json& j, FilamentChangeTimeParams& p) {
|
||||
j.at("selector_load_time").get_to(p.selector_load_time);
|
||||
j.at("selector_unload_time").get_to(p.selector_unload_time);
|
||||
j.at("standard_load_time").get_to(p.standard_load_time);
|
||||
@@ -114,22 +116,22 @@ inline void from_json(const nlohmann::json& j, FilamentChangeTimeParams& p) {
|
||||
|
||||
// ============ Helper: set<int> as JSON array ============
|
||||
namespace FGTestDetail {
|
||||
inline nlohmann::json set_to_json(const std::set<int>& s) {
|
||||
return nlohmann::json(std::vector<int>(s.begin(), s.end()));
|
||||
inline json set_to_json(const std::set<int>& s) {
|
||||
return json(std::vector<int>(s.begin(), s.end()));
|
||||
}
|
||||
|
||||
inline std::set<int> json_to_set(const nlohmann::json& j) {
|
||||
inline std::set<int> json_to_set(const json& j) {
|
||||
auto v = j.get<std::vector<int>>();
|
||||
return std::set<int>(v.begin(), v.end());
|
||||
}
|
||||
|
||||
inline nlohmann::json nvt_set_to_json(const std::set<NozzleVolumeType>& s) {
|
||||
inline json nvt_set_to_json(const std::set<NozzleVolumeType>& s) {
|
||||
std::vector<int> v;
|
||||
for (auto t : s) v.push_back((int)t);
|
||||
return nlohmann::json(v);
|
||||
return json(v);
|
||||
}
|
||||
|
||||
inline std::set<NozzleVolumeType> json_to_nvt_set(const nlohmann::json& j) {
|
||||
inline std::set<NozzleVolumeType> json_to_nvt_set(const json& j) {
|
||||
std::set<NozzleVolumeType> s;
|
||||
for (auto& item : j) s.insert((NozzleVolumeType)item.get<int>());
|
||||
return s;
|
||||
@@ -137,28 +139,28 @@ inline std::set<NozzleVolumeType> json_to_nvt_set(const nlohmann::json& j) {
|
||||
} // namespace FGTestDetail
|
||||
|
||||
// ============ FilamentGroupContext::ModelInfo ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext::ModelInfo& mi) {
|
||||
inline void to_json(json& j, const FilamentGroupContext::ModelInfo& mi) {
|
||||
using namespace FGTestDetail;
|
||||
j["flush_matrix"] = mi.flush_matrix;
|
||||
j["layer_filaments"] = mi.layer_filaments;
|
||||
|
||||
j["filament_info"] = nlohmann::json::array();
|
||||
j["filament_info"] = json::array();
|
||||
for (auto& fi : mi.filament_info)
|
||||
j["filament_info"].push_back(fi);
|
||||
|
||||
j["filament_ids"] = mi.filament_ids;
|
||||
|
||||
j["unprintable_filaments"] = nlohmann::json::array();
|
||||
j["unprintable_filaments"] = json::array();
|
||||
for (auto& s : mi.unprintable_filaments)
|
||||
j["unprintable_filaments"].push_back(set_to_json(s));
|
||||
|
||||
nlohmann::json uv = nlohmann::json::object();
|
||||
json uv = json::object();
|
||||
for (auto& [fil, types] : mi.unprintable_volumes)
|
||||
uv[std::to_string(fil)] = nvt_set_to_json(types);
|
||||
j["unprintable_volumes"] = uv;
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext::ModelInfo& mi) {
|
||||
inline void from_json(const json& j, FilamentGroupContext::ModelInfo& mi) {
|
||||
using namespace FGTestDetail;
|
||||
j.at("flush_matrix").get_to(mi.flush_matrix);
|
||||
j.at("layer_filaments").get_to(mi.layer_filaments);
|
||||
@@ -181,8 +183,8 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::ModelInfo&
|
||||
}
|
||||
|
||||
// ============ FilamentGroupContext::GroupInfo ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext::GroupInfo& gi) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const FilamentGroupContext::GroupInfo& gi) {
|
||||
j = json{
|
||||
{"total_filament_num", gi.total_filament_num},
|
||||
{"max_gap_threshold", gi.max_gap_threshold},
|
||||
{"mode", (int)gi.mode},
|
||||
@@ -193,7 +195,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::GroupInfo& gi
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext::GroupInfo& gi) {
|
||||
inline void from_json(const json& j, FilamentGroupContext::GroupInfo& gi) {
|
||||
j.at("total_filament_num").get_to(gi.total_filament_num);
|
||||
j.at("max_gap_threshold").get_to(gi.max_gap_threshold);
|
||||
gi.mode = (FGMode)j.at("mode").get<int>();
|
||||
@@ -204,12 +206,12 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::GroupInfo&
|
||||
}
|
||||
|
||||
// ============ FilamentGroupContext::MachineInfo ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext::MachineInfo& mi) {
|
||||
inline void to_json(json& j, const FilamentGroupContext::MachineInfo& mi) {
|
||||
j["max_group_size"] = mi.max_group_size;
|
||||
|
||||
j["machine_filament_info"] = nlohmann::json::array();
|
||||
j["machine_filament_info"] = json::array();
|
||||
for (auto& vec : mi.machine_filament_info) {
|
||||
nlohmann::json arr = nlohmann::json::array();
|
||||
json arr = json::array();
|
||||
for (auto& mfi : vec) arr.push_back(mfi);
|
||||
j["machine_filament_info"].push_back(arr);
|
||||
}
|
||||
@@ -218,7 +220,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::MachineInfo&
|
||||
j["master_extruder_id"] = mi.master_extruder_id;
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext::MachineInfo& mi) {
|
||||
inline void from_json(const json& j, FilamentGroupContext::MachineInfo& mi) {
|
||||
j.at("max_group_size").get_to(mi.max_group_size);
|
||||
|
||||
mi.machine_filament_info.clear();
|
||||
@@ -234,10 +236,10 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::MachineInfo
|
||||
}
|
||||
|
||||
// ============ FilamentGroupContext::SpeedInfo ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext::SpeedInfo& si) {
|
||||
nlohmann::json fpt = nlohmann::json::object();
|
||||
inline void to_json(json& j, const FilamentGroupContext::SpeedInfo& si) {
|
||||
json fpt = json::object();
|
||||
for (auto& [fil, inner] : si.filament_print_time) {
|
||||
nlohmann::json inner_j = nlohmann::json::object();
|
||||
json inner_j = json::object();
|
||||
for (auto& [layer, time] : inner)
|
||||
inner_j[std::to_string(layer)] = time;
|
||||
fpt[std::to_string(fil)] = inner_j;
|
||||
@@ -250,7 +252,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::SpeedInfo& si
|
||||
j["ams_preload_enabled"] = si.ams_preload_enabled;
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext::SpeedInfo& si) {
|
||||
inline void from_json(const json& j, FilamentGroupContext::SpeedInfo& si) {
|
||||
si.filament_print_time.clear();
|
||||
if (j.contains("filament_print_time")) {
|
||||
for (auto& [k, v] : j.at("filament_print_time").items()) {
|
||||
@@ -267,23 +269,23 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::SpeedInfo&
|
||||
}
|
||||
|
||||
// ============ FilamentGroupContext::NozzleInfo ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext::NozzleInfo& ni) {
|
||||
nlohmann::json enl = nlohmann::json::object();
|
||||
inline void to_json(json& j, const FilamentGroupContext::NozzleInfo& ni) {
|
||||
json enl = json::object();
|
||||
for (auto& [ext, nozzles] : ni.extruder_nozzle_list)
|
||||
enl[std::to_string(ext)] = nozzles;
|
||||
j["extruder_nozzle_list"] = enl;
|
||||
|
||||
j["nozzle_list"] = nlohmann::json::array();
|
||||
j["nozzle_list"] = json::array();
|
||||
for (auto& n : ni.nozzle_list)
|
||||
j["nozzle_list"].push_back(n);
|
||||
|
||||
nlohmann::json ns = nlohmann::json::object();
|
||||
json ns = json::object();
|
||||
for (auto& [noz, fil] : ni.nozzle_status)
|
||||
ns[std::to_string(noz)] = fil;
|
||||
j["nozzle_status"] = ns;
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext::NozzleInfo& ni) {
|
||||
inline void from_json(const json& j, FilamentGroupContext::NozzleInfo& ni) {
|
||||
ni.extruder_nozzle_list.clear();
|
||||
for (auto& [k, v] : j.at("extruder_nozzle_list").items())
|
||||
ni.extruder_nozzle_list[std::stoi(k)] = v.get<std::vector<int>>();
|
||||
@@ -300,8 +302,8 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::NozzleInfo&
|
||||
}
|
||||
|
||||
// ============ Full FilamentGroupContext ============
|
||||
inline void to_json(nlohmann::json& j, const FilamentGroupContext& ctx) {
|
||||
nlohmann::json mi, gi, mai, si, ni;
|
||||
inline void to_json(json& j, const FilamentGroupContext& ctx) {
|
||||
json mi, gi, mai, si, ni;
|
||||
to_json(mi, ctx.model_info);
|
||||
to_json(gi, ctx.group_info);
|
||||
to_json(mai, ctx.machine_info);
|
||||
@@ -314,7 +316,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext& ctx) {
|
||||
j["nozzle_info"] = ni;
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FilamentGroupContext& ctx) {
|
||||
inline void from_json(const json& j, FilamentGroupContext& ctx) {
|
||||
from_json(j.at("model_info"), ctx.model_info);
|
||||
from_json(j.at("group_info"), ctx.group_info);
|
||||
from_json(j.at("machine_info"), ctx.machine_info);
|
||||
@@ -334,11 +336,11 @@ struct TestMetadata {
|
||||
int seed = 0;
|
||||
};
|
||||
|
||||
inline void to_json(nlohmann::json& j, const TestMetadata& m) {
|
||||
j = nlohmann::json{{"id", m.id}, {"config_type", m.config_type}, {"seed", m.seed}};
|
||||
inline void to_json(json& j, const TestMetadata& m) {
|
||||
j = json{{"id", m.id}, {"config_type", m.config_type}, {"seed", m.seed}};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, TestMetadata& m) {
|
||||
inline void from_json(const json& j, TestMetadata& m) {
|
||||
j.at("id").get_to(m.id);
|
||||
j.at("config_type").get_to(m.config_type);
|
||||
j.at("seed").get_to(m.seed);
|
||||
@@ -352,8 +354,8 @@ struct TestResult {
|
||||
std::vector<std::string> violations;
|
||||
};
|
||||
|
||||
inline void to_json(nlohmann::json& j, const TestResult& r) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const TestResult& r) {
|
||||
j = json{
|
||||
{"filament_map", r.filament_map},
|
||||
{"flush_cost", r.flush_cost},
|
||||
{"elapsed_ms", r.elapsed_ms},
|
||||
@@ -362,7 +364,7 @@ inline void to_json(nlohmann::json& j, const TestResult& r) {
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, TestResult& r) {
|
||||
inline void from_json(const json& j, TestResult& r) {
|
||||
j.at("filament_map").get_to(r.filament_map);
|
||||
j.at("flush_cost").get_to(r.flush_cost);
|
||||
j.at("elapsed_ms").get_to(r.elapsed_ms);
|
||||
@@ -378,15 +380,15 @@ struct BaseResult {
|
||||
bool constraints_ok = true;
|
||||
};
|
||||
|
||||
inline void to_json(nlohmann::json& j, const BaseResult& g) {
|
||||
j = nlohmann::json{
|
||||
inline void to_json(json& j, const BaseResult& g) {
|
||||
j = json{
|
||||
{"full_score", g.full_score},
|
||||
{"flush_cost", g.flush_cost},
|
||||
{"constraints_ok", g.constraints_ok}
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, BaseResult& g) {
|
||||
inline void from_json(const json& j, BaseResult& g) {
|
||||
j.at("full_score").get_to(g.full_score);
|
||||
j.at("flush_cost").get_to(g.flush_cost);
|
||||
j.at("constraints_ok").get_to(g.constraints_ok);
|
||||
@@ -401,7 +403,7 @@ struct TestCase {
|
||||
|
||||
inline TestCase load_test_case(const std::string& path) {
|
||||
std::ifstream f(path);
|
||||
nlohmann::json j = nlohmann::json::parse(f);
|
||||
json j = json::parse(f);
|
||||
TestCase tc;
|
||||
tc.metadata = j.at("metadata").get<TestMetadata>();
|
||||
Slic3r::from_json(j.at("context"), tc.context);
|
||||
@@ -411,9 +413,9 @@ inline TestCase load_test_case(const std::string& path) {
|
||||
}
|
||||
|
||||
inline void save_test_case(const std::string& path, const TestCase& tc) {
|
||||
nlohmann::json j;
|
||||
json j;
|
||||
j["metadata"] = tc.metadata;
|
||||
nlohmann::json ctx_j;
|
||||
json ctx_j;
|
||||
Slic3r::to_json(ctx_j, tc.context);
|
||||
j["context"] = ctx_j;
|
||||
if (tc.base_result)
|
||||
@@ -430,14 +432,14 @@ inline void save_result(const std::string& case_path, const TestResult& result)
|
||||
else
|
||||
result_path += ".result.json";
|
||||
|
||||
nlohmann::json j = result;
|
||||
json j = result;
|
||||
std::ofstream f(result_path);
|
||||
f << j.dump(2);
|
||||
}
|
||||
|
||||
inline TestResult load_result(const std::string& result_path) {
|
||||
std::ifstream f(result_path);
|
||||
nlohmann::json j = nlohmann::json::parse(f);
|
||||
json j = json::parse(f);
|
||||
return j.get<TestResult>();
|
||||
}
|
||||
|
||||
|
||||
@@ -863,23 +863,3 @@ TEST_CASE("The device drying options are rebuilt as each filament's values in sl
|
||||
set_filament_dev_options(config, {&two_values, &no_value});
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values == std::vector<std::string>({"1", "0", ""}));
|
||||
}
|
||||
|
||||
TEST_CASE("The mixed filament metadata is sized to the filament count", "[Config]")
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.option<ConfigOptionBools>("filament_is_mixed", true)->values = {false, false, true};
|
||||
config.option<ConfigOptionStrings>("filament_mixed_components", true)->values = {"", "", "1,2"};
|
||||
config.option<ConfigOptionBools>("filament_mixed_gradient", true)->values = {false};
|
||||
config.option<ConfigOptionStrings>("filament_mixed_gradient_range", true)->values = {""};
|
||||
|
||||
resize_mixed_filament_metadata(config, 3, 3);
|
||||
REQUIRE(config.option<ConfigOptionBools>("filament_is_mixed")->values == std::vector<unsigned char>({false, false, true}));
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_components")->values == std::vector<std::string>({"", "", "1,2"}));
|
||||
REQUIRE(config.option<ConfigOptionBools>("filament_mixed_gradient")->values == std::vector<unsigned char>({false, false, false}));
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_gradient_range")->values == std::vector<std::string>({"", "", ""}));
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve")->values == std::vector<std::string>({"", "", ""}));
|
||||
|
||||
resize_mixed_filament_metadata(config, 2, 4);
|
||||
REQUIRE(config.option<ConfigOptionBools>("filament_is_mixed")->values == std::vector<unsigned char>({false, false, false, false}));
|
||||
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_components")->values == std::vector<std::string>({"", "", "", ""}));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "test_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
namespace {
|
||||
|
||||
const char *const BELOW_PAD_TEST_OBJECTS[] = {
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Line.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
void test_support_model_collision(const std::string &obj_filename,
|
||||
const sla::SupportTreeConfig &input_supportcfg,
|
||||
const sla::HollowingConfig &hollowingcfg,
|
||||
|
||||
@@ -33,31 +33,33 @@ namespace Slic3r::sla { struct PadConfig; }
|
||||
namespace Slic3r::sla { struct SupportTreeConfig; }
|
||||
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
enum e_validity {
|
||||
ASSUME_NO_EMPTY = 1,
|
||||
ASSUME_MANIFOLD = 2,
|
||||
ASSUME_NO_REPAIR = 4
|
||||
};
|
||||
|
||||
void check_validity(const Slic3r::TriangleMesh &input_mesh,
|
||||
void check_validity(const TriangleMesh &input_mesh,
|
||||
int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD |
|
||||
ASSUME_NO_REPAIR);
|
||||
|
||||
struct PadByproducts
|
||||
{
|
||||
Slic3r::ExPolygons model_contours;
|
||||
Slic3r::ExPolygons support_contours;
|
||||
Slic3r::TriangleMesh mesh;
|
||||
ExPolygons model_contours;
|
||||
ExPolygons support_contours;
|
||||
TriangleMesh mesh;
|
||||
};
|
||||
|
||||
void test_concave_hull(const Slic3r::ExPolygons &polys);
|
||||
void test_concave_hull(const ExPolygons &polys);
|
||||
|
||||
void test_pad(const std::string & obj_filename,
|
||||
const Slic3r::sla::PadConfig &padcfg,
|
||||
const sla::PadConfig &padcfg,
|
||||
PadByproducts & out);
|
||||
|
||||
inline void test_pad(const std::string & obj_filename,
|
||||
const Slic3r::sla::PadConfig &padcfg = {})
|
||||
const sla::PadConfig &padcfg = {})
|
||||
{
|
||||
PadByproducts byproducts;
|
||||
test_pad(obj_filename, padcfg, byproducts);
|
||||
@@ -67,53 +69,53 @@ struct SupportByproducts
|
||||
{
|
||||
std::string obj_fname;
|
||||
std::vector<float> slicegrid;
|
||||
std::vector<Slic3r::ExPolygons> model_slices;
|
||||
Slic3r::sla::SupportTreeBuilder supporttree;
|
||||
Slic3r::TriangleMesh input_mesh;
|
||||
std::vector<ExPolygons> model_slices;
|
||||
sla::SupportTreeBuilder supporttree;
|
||||
TriangleMesh input_mesh;
|
||||
};
|
||||
|
||||
const constexpr float CLOSING_RADIUS = 0.005f;
|
||||
|
||||
void check_support_tree_integrity(const Slic3r::sla::SupportTreeBuilder &stree,
|
||||
const Slic3r::sla::SupportTreeConfig &cfg);
|
||||
void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
|
||||
const sla::SupportTreeConfig &cfg);
|
||||
|
||||
void test_supports(const std::string &obj_filename,
|
||||
const Slic3r::sla::SupportTreeConfig &supportcfg,
|
||||
const Slic3r::sla::HollowingConfig &hollowingcfg,
|
||||
const Slic3r::sla::DrainHoles &drainholes,
|
||||
const sla::SupportTreeConfig &supportcfg,
|
||||
const sla::HollowingConfig &hollowingcfg,
|
||||
const sla::DrainHoles &drainholes,
|
||||
SupportByproducts &out);
|
||||
|
||||
inline void test_supports(const std::string &obj_filename,
|
||||
const Slic3r::sla::SupportTreeConfig &supportcfg,
|
||||
const sla::SupportTreeConfig &supportcfg,
|
||||
SupportByproducts &out)
|
||||
{
|
||||
Slic3r::sla::HollowingConfig hcfg;
|
||||
sla::HollowingConfig hcfg;
|
||||
hcfg.enabled = false;
|
||||
test_supports(obj_filename, supportcfg, hcfg, {}, out);
|
||||
}
|
||||
|
||||
inline void test_supports(const std::string &obj_filename,
|
||||
const Slic3r::sla::SupportTreeConfig &supportcfg = {})
|
||||
const sla::SupportTreeConfig &supportcfg = {})
|
||||
{
|
||||
SupportByproducts byproducts;
|
||||
test_supports(obj_filename, supportcfg, byproducts);
|
||||
}
|
||||
|
||||
void export_failed_case(const std::vector<Slic3r::ExPolygons> &support_slices,
|
||||
void export_failed_case(const std::vector<ExPolygons> &support_slices,
|
||||
const SupportByproducts &byproducts);
|
||||
|
||||
|
||||
void test_support_model_collision(
|
||||
const std::string &obj_filename,
|
||||
const Slic3r::sla::SupportTreeConfig &input_supportcfg,
|
||||
const Slic3r::sla::HollowingConfig &hollowingcfg,
|
||||
const Slic3r::sla::DrainHoles &drainholes);
|
||||
const sla::SupportTreeConfig &input_supportcfg,
|
||||
const sla::HollowingConfig &hollowingcfg,
|
||||
const sla::DrainHoles &drainholes);
|
||||
|
||||
inline void test_support_model_collision(
|
||||
const std::string &obj_filename,
|
||||
const Slic3r::sla::SupportTreeConfig &input_supportcfg = {})
|
||||
const sla::SupportTreeConfig &input_supportcfg = {})
|
||||
{
|
||||
Slic3r::sla::HollowingConfig hcfg;
|
||||
sla::HollowingConfig hcfg;
|
||||
hcfg.enabled = false;
|
||||
test_support_model_collision(obj_filename, input_supportcfg, hcfg, {});
|
||||
}
|
||||
@@ -154,8 +156,8 @@ template <class I, class II> void test_pairhash()
|
||||
|
||||
REQUIRE(a != b);
|
||||
|
||||
II hash_ab = Slic3r::sla::pairhash<I, II>(a, b);
|
||||
II hash_ba = Slic3r::sla::pairhash<I, II>(b, a);
|
||||
II hash_ab = sla::pairhash<I, II>(a, b);
|
||||
II hash_ba = sla::pairhash<I, II>(b, a);
|
||||
REQUIRE(hash_ab == hash_ba);
|
||||
|
||||
auto it = ints.find(hash_ab);
|
||||
@@ -178,23 +180,23 @@ static constexpr const TPixel FullBlack = 0;
|
||||
|
||||
template <class A, int N> constexpr int arraysize(const A (&)[N]) { return N; }
|
||||
|
||||
void check_raster_transformations(Slic3r::sla::RasterBase::Orientation o,
|
||||
Slic3r::sla::RasterBase::TMirroring mirroring);
|
||||
void check_raster_transformations(sla::RasterBase::Orientation o,
|
||||
sla::RasterBase::TMirroring mirroring);
|
||||
|
||||
Slic3r::ExPolygon square_with_hole(double v);
|
||||
ExPolygon square_with_hole(double v);
|
||||
|
||||
inline double pixel_area(TPixel px, const Slic3r::sla::PixelDim &pxdim)
|
||||
inline double pixel_area(TPixel px, const sla::PixelDim &pxdim)
|
||||
{
|
||||
return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack);
|
||||
}
|
||||
|
||||
double raster_white_area(const Slic3r::sla::RasterGrayscaleAA &raster);
|
||||
long raster_pxsum(const Slic3r::sla::RasterGrayscaleAA &raster);
|
||||
double raster_white_area(const sla::RasterGrayscaleAA &raster);
|
||||
long raster_pxsum(const sla::RasterGrayscaleAA &raster);
|
||||
|
||||
double predict_error(const Slic3r::ExPolygon &p, const Slic3r::sla::PixelDim &pd);
|
||||
double predict_error(const ExPolygon &p, const sla::PixelDim &pd);
|
||||
|
||||
Slic3r::sla::SupportPoints calc_support_pts(
|
||||
const Slic3r::TriangleMesh & mesh,
|
||||
const Slic3r::sla::SupportPointGenerator::Config &cfg = {});
|
||||
sla::SupportPoints calc_support_pts(
|
||||
const TriangleMesh & mesh,
|
||||
const sla::SupportPointGenerator::Config &cfg = {});
|
||||
|
||||
#endif // SLA_TEST_UTILS_HPP
|
||||
|
||||
@@ -5,6 +5,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_config_value_formatter.cpp
|
||||
test_creality_cfs_match.cpp
|
||||
test_dev_mapping.cpp
|
||||
test_dev_extder_system.cpp
|
||||
test_filament_bitmap_utils.cpp
|
||||
test_device_progress.cpp
|
||||
test_device_manager_integration.cpp
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Match the include environment that libslic3r_gui TUs get from pchheader.hpp: Windows.h with
|
||||
// WIN32_LEAN_AND_MEAN/NOMINMAX must come first so rpcndr.h's `byte` is processed before <cstddef>
|
||||
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
|
||||
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
|
||||
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
|
||||
#include "catch2/catch_message.hpp"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <wx/timer.h>
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevExtruderSystem.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("Extruder state decodes a generic toolhead set", "[DevExtderSystem]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
// device.extruder.state packing: count 0..3, current 4..7, target 8..11,
|
||||
// switch 12..14, loading 15..18, busy 19.
|
||||
const int extruder_count = 4;
|
||||
const int current_extruder = 2;
|
||||
const int switch_state = ES_BUSY;
|
||||
const int loading_extruder = 1;
|
||||
const int state = extruder_count | (current_extruder << 4) | (3 << 8) |
|
||||
(switch_state << 12) | (loading_extruder << 15) | (1 << 19);
|
||||
|
||||
json extruder = { {"state", state}, {"info", json::array()} };
|
||||
for (int id = 0; id < extruder_count; ++id) {
|
||||
// info: bit 1 has filament, bit 3 nozzle present.
|
||||
// temp: current 0..15, target 16..31. spre/snow/star: slot 0..7, ams 8..15.
|
||||
extruder["info"].push_back({
|
||||
{"id", id},
|
||||
{"filam_bak", json::array({id})},
|
||||
{"info", (id % 2 == 0 ? 2 : 0) | 8},
|
||||
{"temp", (200 + id) | ((250 + id) << 16)},
|
||||
{"spre", 10 << 8},
|
||||
{"snow", id | ((20 + id) << 8)},
|
||||
{"star", 30 << 8},
|
||||
{"stat", 0},
|
||||
{"hnow", id},
|
||||
});
|
||||
}
|
||||
|
||||
ExtderSystemParser::ParseV2_0(extruder, obj.GetExtderSystem());
|
||||
|
||||
REQUIRE(obj.GetExtderSystem()->GetTotalExtderCount() == extruder_count);
|
||||
CHECK(obj.GetExtderSystem()->GetCurrentExtderId() == current_extruder);
|
||||
CHECK(static_cast<int>(obj.GetExtderSystem()->GetSwitchState()) == switch_state);
|
||||
CHECK(obj.GetExtderSystem()->GetLoadingExtderId() == loading_extruder);
|
||||
CHECK(obj.GetExtderSystem()->IsBusyLoading());
|
||||
|
||||
for (int id = 0; id < extruder_count; ++id) {
|
||||
const auto ext = obj.GetExtderSystem()->GetExtderById(id);
|
||||
REQUIRE(ext.has_value());
|
||||
INFO("extruder " << id);
|
||||
CHECK(ext->GetExtId() == id);
|
||||
CHECK(ext->GetCurrentTemp() == 200 + id);
|
||||
CHECK(ext->GetTargetTemp() == 250 + id);
|
||||
CHECK(ext->HasFilamentInExt() == (id % 2 == 0));
|
||||
CHECK(ext->GetFilamBackup() == std::vector<int>{id});
|
||||
CHECK(ext->GetSlotPre().ams_id == "10");
|
||||
CHECK(ext->GetSlotNow().ams_id == std::to_string(20 + id));
|
||||
CHECK(ext->GetSlotNow().slot_id == std::to_string(id));
|
||||
CHECK(ext->GetSlotTarget().ams_id == "30");
|
||||
CHECK(ext->GetNozzleId() == id);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
|
||||
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
|
||||
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -17,6 +19,7 @@
|
||||
#include "slic3r/GUI/DeviceCore/DevDefs.h"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <catch2/catch_message.hpp>
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
@@ -65,6 +68,162 @@ TEST_CASE("AMS tray placeholder state follows the latest status", "[DevFilaSyste
|
||||
CHECK_FALSE(tray->is_slot_placeholder);
|
||||
}
|
||||
|
||||
TEST_CASE("Agent filament slots preserve every extruder and AMS binding", "[DevFilaSystem]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
const json filament = json::parse(R"({
|
||||
"units": [
|
||||
{ "id": "0", "extruder": 0, "slots": [
|
||||
{ "index": 0, "loaded": true, "material": "PLA", "color": "FF5733FF" },
|
||||
{ "index": 1, "loaded": true, "material": "PLA", "color": "33C1FFFF" },
|
||||
{ "index": 2, "loaded": true, "material": "PLA", "color": "8D33FFFF" },
|
||||
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
|
||||
] },
|
||||
{ "id": "1", "extruder": 1, "slots": [
|
||||
{ "index": 0, "loaded": true, "material": "ASA", "color": "33C1FFFF" },
|
||||
{ "index": 1, "loaded": true, "material": "ASA", "color": "8D33FFFF" },
|
||||
{ "index": 2, "loaded": true, "material": "ASA", "color": "FF5733FF" },
|
||||
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
|
||||
] },
|
||||
{ "id": "2", "extruder": 2, "slots": [
|
||||
{ "index": 0, "loaded": true, "material": "ABS", "color": "8D33FFFF" },
|
||||
{ "index": 1, "loaded": true, "material": "ABS", "color": "FF5733FF" },
|
||||
{ "index": 2, "loaded": true, "material": "ABS", "color": "33C1FFFF" },
|
||||
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
|
||||
] },
|
||||
{ "id": "3", "extruder": 3, "slots": [
|
||||
{ "index": 0, "loaded": true, "material": "PETG", "color": "FF5733FF" },
|
||||
{ "index": 1, "loaded": true, "material": "PETG", "color": "33C1FFFF" },
|
||||
{ "index": 2, "loaded": true, "material": "PETG", "color": "8D33FFFF" },
|
||||
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
|
||||
] }
|
||||
],
|
||||
"external": [
|
||||
{ "extruder": 3, "loaded": true, "material": "PETG", "preset_id": "P3" },
|
||||
{ "extruder": 0, "loaded": true, "material": "PLA", "preset_id": "P0" },
|
||||
{ "extruder": 2, "loaded": false, "material": "ABS", "preset_id": "P2" },
|
||||
{ "extruder": 1, "loaded": true, "material": "ASA", "preset_id": "P1" }
|
||||
]
|
||||
})");
|
||||
|
||||
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
|
||||
|
||||
REQUIRE(obj.vt_slot.size() == 4);
|
||||
CHECK(obj.vt_slot[0].id == "255");
|
||||
CHECK(obj.vt_slot[1].id == "254");
|
||||
CHECK(obj.vt_slot[2].id == "253");
|
||||
CHECK(obj.vt_slot[3].id == "252");
|
||||
CHECK(obj.vt_slot[0].setting_id == "P0");
|
||||
CHECK(obj.vt_slot[1].setting_id == "P1");
|
||||
CHECK(obj.vt_slot[2].setting_id == "P2");
|
||||
CHECK(obj.vt_slot[3].setting_id == "P3");
|
||||
|
||||
REQUIRE(obj.GetFilaSystem()->GetAmsCount() == 4);
|
||||
for (int extruder = 0; extruder < 4; ++extruder) {
|
||||
const auto& ams = obj.GetFilaSystem()->GetAmsList().at(std::to_string(extruder));
|
||||
CHECK(ams->GetExtruderId() == extruder);
|
||||
CHECK(ams->GetBindedExtruderSet() == std::set<int>{extruder});
|
||||
REQUIRE(ams->GetTrays().size() == 4);
|
||||
CHECK(ams->GetTrays().at("0")->color != ams->GetTrays().at("1")->color);
|
||||
CHECK(ams->GetTrays().at("1")->color != ams->GetTrays().at("2")->color);
|
||||
CHECK_FALSE(ams->GetTrays().at("3")->is_exists);
|
||||
}
|
||||
|
||||
CHECK(obj.contains_tray("253", "0"));
|
||||
CHECK(obj.get_tray("253", "0").setting_id == "P2");
|
||||
CHECK(obj.GetFilaSystem()->GetExtruderIdByAmsId("253") == 2);
|
||||
|
||||
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
|
||||
REQUIRE(tray_index_map.count(252) == 1);
|
||||
CHECK(tray_index_map.at(252).first == 252);
|
||||
CHECK(tray_index_map.at(252).second == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Generic AMS tray index map uses cumulative lane counts", "[DevMapping]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
// A well-formed generic payload pairs "units" with "external". An empty external array resets
|
||||
// vt_slot, clearing the default virtual tray (255) MachineObject's constructor seeds; otherwise
|
||||
// that phantom external tray lands in GetTrayIndexMap and inflates the map size.
|
||||
json filament = { {"units", json::array()}, {"external", json::array()} };
|
||||
for (int ams_id = 0; ams_id < 2; ++ams_id) {
|
||||
json unit = {
|
||||
{"id", std::to_string(ams_id)},
|
||||
{"extruder", ams_id},
|
||||
{"slots", json::array()}
|
||||
};
|
||||
for (int slot_id = 0; slot_id < 5; ++slot_id)
|
||||
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
|
||||
filament["units"].push_back(std::move(unit));
|
||||
}
|
||||
|
||||
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
|
||||
|
||||
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
|
||||
REQUIRE(tray_index_map.size() == 10);
|
||||
CHECK(tray_index_map.at(0).first == 0);
|
||||
CHECK(tray_index_map.at(0).second == 0);
|
||||
CHECK(tray_index_map.at(4).first == 0);
|
||||
CHECK(tray_index_map.at(4).second == 4);
|
||||
CHECK(tray_index_map.at(5).first == 1);
|
||||
CHECK(tray_index_map.at(5).second == 0);
|
||||
CHECK(tray_index_map.at(9).first == 1);
|
||||
CHECK(tray_index_map.at(9).second == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("Generic tray index map handles unequal per-unit slot counts", "[DevMapping]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
// An "external" array must accompany "units" so vt_slot is reset (see the cumulative test above).
|
||||
json filament = { {"units", json::array()}, {"external", json::array()} };
|
||||
const std::vector<int> slot_counts = {4, 12};
|
||||
for (int ams_id = 0; ams_id < static_cast<int>(slot_counts.size()); ++ams_id) {
|
||||
json unit = { {"id", std::to_string(ams_id)}, {"extruder", ams_id}, {"slots", json::array()} };
|
||||
for (int slot_id = 0; slot_id < slot_counts[ams_id]; ++slot_id)
|
||||
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
|
||||
filament["units"].push_back(std::move(unit));
|
||||
}
|
||||
|
||||
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
|
||||
|
||||
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
|
||||
REQUIRE(tray_index_map.size() == 16);
|
||||
// Unit 0 occupies global lanes 0..3; unit 1 continues at 4..15 because the counter advances
|
||||
// by the reported tray count, not by a fixed four-slot stride.
|
||||
CHECK(tray_index_map.at(0).first == 0);
|
||||
CHECK(tray_index_map.at(0).second == 0);
|
||||
CHECK(tray_index_map.at(3).first == 0);
|
||||
CHECK(tray_index_map.at(3).second == 3);
|
||||
CHECK(tray_index_map.at(4).first == 1);
|
||||
CHECK(tray_index_map.at(4).second == 0);
|
||||
CHECK(tray_index_map.at(15).first == 1);
|
||||
CHECK(tray_index_map.at(15).second == 11);
|
||||
}
|
||||
|
||||
TEST_CASE("A generic AMS unit reports its reported tray count", "[DevFilaSystem]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
json filament = { {"units", json::array()}, {"external", json::array()} };
|
||||
const std::vector<int> slot_counts = {8, 1};
|
||||
for (int ams_id = 0; ams_id < static_cast<int>(slot_counts.size()); ++ams_id) {
|
||||
json unit = { {"id", std::to_string(ams_id)}, {"extruder", ams_id}, {"slots", json::array()} };
|
||||
for (int slot_id = 0; slot_id < slot_counts[ams_id]; ++slot_id)
|
||||
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
|
||||
filament["units"].push_back(std::move(unit));
|
||||
}
|
||||
|
||||
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
|
||||
|
||||
// Non-Bambu units are not capped at the four-slot Bambu contract; the reported tray count wins.
|
||||
const auto& ams_list = obj.GetFilaSystem()->GetAmsList();
|
||||
REQUIRE(ams_list.at("0")->GetSlotCount() == 8);
|
||||
REQUIRE(ams_list.at("1")->GetSlotCount() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Switch-bound AMS trays map to the left extruder", "[DevMapping]")
|
||||
{
|
||||
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
|
||||
|
||||
Reference in New Issue
Block a user