Fixed conflicts after merge with master
@ -160,7 +160,10 @@ REM Build deps
|
||||
:BUILD_DEPS
|
||||
SET EXIT_STATUS=3
|
||||
SET PS_CURRENT_STEP=deps
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" .vs
|
||||
IF "%PS_STEPS_DIRTY%" EQU "" (
|
||||
CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" .vs
|
||||
CALL :MAKE_OR_CLEAN_DIRECTORY "%PS_DESTDIR%"
|
||||
)
|
||||
cd deps\build || GOTO :END
|
||||
cmake.exe .. -DDESTDIR="%PS_DESTDIR%"
|
||||
IF %ERRORLEVEL% NEQ 0 IF "%PS_STEPS_DIRTY%" NEQ "" (
|
||||
@ -203,8 +206,8 @@ IF "%PS_CURRENT_STEP%" NEQ "arguments" (
|
||||
)
|
||||
SET EXIT_STATUS=5
|
||||
SET PS_CURRENT_STEP=run
|
||||
cd src\%PS_CONFIG% || GOTO :END
|
||||
IF "%PS_RUN%" EQU "none" GOTO :PROLOGUE
|
||||
cd src\%PS_CONFIG% || GOTO :END
|
||||
SET PS_PROJECT_IS_OPEN=
|
||||
FOR /F "tokens=2 delims=," %%I in (
|
||||
'tasklist /V /FI "IMAGENAME eq devenv.exe " /NH /FO CSV ^| find "%PS_SOLUTION_NAME%"'
|
||||
|
2
deps/wxWidgets/wxWidgets.cmake
vendored
@ -13,7 +13,7 @@ prusaslicer_add_cmake_project(wxWidgets
|
||||
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
|
||||
# GIT_TAG tm_cross_compile #${_wx_git_tag}
|
||||
URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip
|
||||
URL_HASH SHA256=a1e145a083d173cf320c0bd8522c7ee5829052b49b68fe5268ac84f0c576b940
|
||||
URL_HASH SHA256=21ed12eb5c215b00999f0374af652be0a6f785df10d18d0dfec8d81ed4abaea3
|
||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
|
||||
CMAKE_ARGS
|
||||
-DwxBUILD_PRECOMP=ON
|
||||
|
@ -13,7 +13,9 @@ This guide describes building PrusaSlicer statically against dependencies pulled
|
||||
|
||||
#### 0. Prerequisities
|
||||
|
||||
You must have CMake, GNU build tools and git. If you don't already have them, install them as usual from your distribution packages (e.g. on Ubuntu, you would run `sudo apt-get install cmake build-essential git`, etc.)
|
||||
CMake, GNU build tools, git and m4 macro processor have to be installed. Unless that's already the case, install them as usual from your distribution packages. E.g. on Ubuntu, run `sudo apt-get install cmake build-essential git m4`. The names of the packages may be different on different distros.
|
||||
|
||||
Although most of dependencies are handled by the build script, PrusaSlicer still expects that some libraries will be available in the system (GTK, MESA, gettext). E.g., on Ubuntu, install the required packages by running `sudo apt-get install libgtk-3-dev libglu1-mesa-dev gettext`. The names of the packages may be different on different distros.
|
||||
|
||||
#### 1. Cloning the repository
|
||||
|
||||
@ -28,12 +30,12 @@ This will download the source code into a new directory and `cd` into it. You ca
|
||||
|
||||
#### 2. Building dependencies
|
||||
|
||||
PrusaSlicer uses CMake and the build is quite simple, the only tricky part is resolution of dependencies. The supported and recommended way is to build the dependencies first and link to them statically. The source base contains a CMake script that automatically downloads and builds the required dependencies. All that is needed is to run the following (from the top of the cloned repository):
|
||||
PrusaSlicer uses CMake and the build is quite simple, the only tricky part is resolution of dependencies. The supported and recommended way is to build the dependencies first and link to them statically. PrusaSlicer source base contains a CMake script that automatically downloads and builds the required dependencies. All that is needed is to run the following (from the top of the cloned repository):
|
||||
|
||||
cd deps
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake .. -DDEP_WX_GTK3=ON
|
||||
make
|
||||
cd ../..
|
||||
|
||||
@ -43,24 +45,20 @@ PrusaSlicer uses CMake and the build is quite simple, the only tricky part is re
|
||||
|
||||
#### 3. Building PrusaSlicer
|
||||
|
||||
Now when you have the dependencies compiled, all that is needed is to tell CMake that we are interested in static build and point it to the dependencies. From the top of the repository, run
|
||||
Now when the dependencies are compiled, all that is needed is to tell CMake that we are interested in static build and point it to the dependencies. From the top of the repository, run
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DSLIC3R_STATIC=1 -DSLIC3R_PCH=OFF -DCMAKE_PREFIX_PATH=$(pwd)/../deps/build/destdir/usr/local
|
||||
cmake .. -DSLIC3R_STATIC=1 -DSLIC3R_GTK=3 -DSLIC3R_PCH=OFF -DCMAKE_PREFIX_PATH=$(pwd)/../deps/build/destdir/usr/local
|
||||
make -j4
|
||||
|
||||
And that's it. You can now run the freshly built PrusaSlicer binary:
|
||||
And that's it. It is now possible to run the freshly built PrusaSlicer binary:
|
||||
|
||||
cd src
|
||||
./prusa-slicer
|
||||
|
||||
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
Although most of the dependencies are handled by the build script, we still rely on some system libraries (such as GTK, GL, etc). It is quite likely that you have them already installed, but in case that CMake reports any library missing, install the respective package from your distribution and run CMake again.
|
||||
|
||||
|
||||
## Useful CMake flags when building dependencies
|
||||
|
||||
@ -86,8 +84,7 @@ See the CMake files to get the complete list.
|
||||
|
||||
As already mentioned above, dynamic linking of dependencies is possible, but PrusaSlicer team is unable to troubleshoot (Linux world is way too complex). Feel free to do so, but you are on your own. Several remarks though:
|
||||
|
||||
The list of dependencies can be easily obtained by inspecting the CMake scripts in the `deps/` directory. Many don't necessarily need to be as recent
|
||||
as the versions listed - generally versions available on conservative Linux distros such as Debian stable, Ubuntu LTS releases or Fedora are likely sufficient. If you decide to build this way, it is your responsibility to make sure that CMake finds all required dependencies. It is possible to look at your distribution PrusaSlicer package to see how the package maintainers solved the dependency issues.
|
||||
The list of dependencies can be easily obtained by inspecting the CMake scripts in the `deps/` directory. Some of the dependencies don't have to be as recent as the versions listed - generally versions available on conservative Linux distros such as Debian stable, Ubuntu LTS releases or Fedora are likely sufficient. If you decide to build this way, it is your responsibility to make sure that CMake finds all required dependencies. It is possible to look at your distribution PrusaSlicer package to see how the package maintainers solved the dependency issues.
|
||||
|
||||
#### wxWidgets
|
||||
By default, PrusaSlicer looks for wxWidgets 3.1. Our build script in fact downloads specific patched version of wxWidgets. If you want to link against wxWidgets 3.0 (which are still provided by most distributions because wxWidgets 3.1 have not yet been declared stable), you must set `-DSLIC3R_WX_STABLE=ON` when running CMake. Note that while PrusaSlicer can be linked against wWidgets 3.0, the combination is not well tested and there might be bugs in the resulting application.
|
||||
|
91
resources/icons/edit_button.svg
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 348.882 348.882"
|
||||
style="enable-background:new 0 0 348.882 348.882;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="edit_button_mod.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs209">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview207"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.3274346"
|
||||
inkscape:cx="89.583614"
|
||||
inkscape:cy="140.28321"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<path
|
||||
d="m 333.988,11.758 -0.42,-0.383 C 325.538,4.04 315.129,0 304.258,0 292.071,0 280.37,5.159 272.154,14.153 L 116.803,184.231 c -1.416,1.55 -2.49,3.379 -3.154,5.37 l -18.267,54.762 c -2.112,6.331 -1.052,13.333 2.835,18.729 3.918,5.438 10.23,8.685 16.886,8.685 0,0 0.001,0 0.001,0 2.879,0 5.693,-0.592 8.362,-1.76 l 52.89,-23.138 c 1.923,-0.841 3.648,-2.076 5.063,-3.626 L 336.771,73.176 C 352.937,55.479 351.69,27.929 333.988,11.758 Z m -203.607,222.489 10.719,-32.134 0.904,-0.99 20.316,18.556 -0.904,0.99 z M 314.621,52.943 182.553,197.53 162.237,178.974 294.305,34.386 c 2.583,-2.828 6.118,-4.386 9.954,-4.386 3.365,0 6.588,1.252 9.082,3.53 l 0.419,0.383 c 5.484,5.009 5.87,13.546 0.861,19.03 z"
|
||||
id="path170"
|
||||
style="fill:#ed6b21;fill-opacity:1" /><path
|
||||
d="m 303.85,138.388 c -8.284,0 -15,6.716 -15,15 v 127.347 c 0,21.034 -17.113,38.147 -38.147,38.147 H 68.904 c -21.035,0 -38.147,-17.113 -38.147,-38.147 V 100.413 c 0,-21.034 17.113,-38.147 38.147,-38.147 h 131.587 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 H 68.904 c -37.577,0 -68.147,30.571 -68.147,68.147 v 180.321 c 0,37.576 30.571,68.147 68.147,68.147 h 181.798 c 37.576,0 68.147,-30.571 68.147,-68.147 V 153.388 c 0.001,-8.284 -6.715,-15 -14.999,-15 z"
|
||||
id="path172"
|
||||
style="fill:#808080;fill-opacity:1" />
|
||||
<g
|
||||
id="g176">
|
||||
</g>
|
||||
<g
|
||||
id="g178">
|
||||
</g>
|
||||
<g
|
||||
id="g180">
|
||||
</g>
|
||||
<g
|
||||
id="g182">
|
||||
</g>
|
||||
<g
|
||||
id="g184">
|
||||
</g>
|
||||
<g
|
||||
id="g186">
|
||||
</g>
|
||||
<g
|
||||
id="g188">
|
||||
</g>
|
||||
<g
|
||||
id="g190">
|
||||
</g>
|
||||
<g
|
||||
id="g192">
|
||||
</g>
|
||||
<g
|
||||
id="g194">
|
||||
</g>
|
||||
<g
|
||||
id="g196">
|
||||
</g>
|
||||
<g
|
||||
id="g198">
|
||||
</g>
|
||||
<g
|
||||
id="g200">
|
||||
</g>
|
||||
<g
|
||||
id="g202">
|
||||
</g>
|
||||
<g
|
||||
id="g204">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
@ -1,4 +1,5 @@
|
||||
min_slic3r_version = 2.4.0-beta0
|
||||
1.4.0-beta2 Added SLA material colors. Updated BASF filament profiles.
|
||||
1.4.0-beta1 Updated pad wall slope angle for SLA printers. Updated Filatech Filacarbon profile for Prusa MINI.
|
||||
1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S.
|
||||
min_slic3r_version = 2.4.0-alpha0
|
||||
|
2
resources/profiles/Voron.idx
Normal file
@ -0,0 +1,2 @@
|
||||
min_slic3r_version = 2.4.0-beta0
|
||||
1.0.0 Initial version
|
1558
resources/profiles/Voron.ini
Normal file
BIN
resources/profiles/Voron/Voron_v0_120_thumbnail.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
resources/profiles/Voron/Voron_v1_250_afterburner_thumbnail.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/profiles/Voron/Voron_v1_300_afterburner_thumbnail.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/profiles/Voron/Voron_v2_250_afterburner_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/Voron_v2_250_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/Voron_v2_300_afterburner_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/Voron_v2_300_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/Voron_v2_350_afterburner_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/Voron_v2_350_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Voron/bedtexture-v0-120.png
Normal file
After Width: | Height: | Size: 284 KiB |
BIN
resources/profiles/Voron/bedtexture-v1-250.png
Normal file
After Width: | Height: | Size: 509 KiB |
BIN
resources/profiles/Voron/bedtexture-v1-300.png
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
resources/profiles/Voron/bedtexture-v2-250.png
Normal file
After Width: | Height: | Size: 509 KiB |
BIN
resources/profiles/Voron/bedtexture-v2-300.png
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
resources/profiles/Voron/bedtexture-v2-350.png
Normal file
After Width: | Height: | Size: 251 KiB |
BIN
resources/profiles/Voron/printbed-v0-120.stl
Normal file
BIN
resources/profiles/Voron/printbed-v1-250.stl
Normal file
BIN
resources/profiles/Voron/printbed-v1-300.stl
Normal file
9858
resources/profiles/Voron/printbed-v2-250.stl
Normal file
9858
resources/profiles/Voron/printbed-v2-300.stl
Normal file
9858
resources/profiles/Voron/printbed-v2-350.stl
Normal file
@ -154,7 +154,8 @@ namespace ImGui
|
||||
const wchar_t DocumentationHoverButton = 0x2601;
|
||||
const wchar_t ClippyMarker = 0x2602;
|
||||
const wchar_t InfoMarker = 0x2603;
|
||||
|
||||
const wchar_t SliderFloatEditBtnIcon = 0x2604;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
@ -182,6 +182,9 @@ void AppConfig::set_defaults()
|
||||
|
||||
if (get("dark_color_mode").empty())
|
||||
set("dark_color_mode", "0");
|
||||
|
||||
if (get("sys_menu_enabled").empty())
|
||||
set("sys_menu_enabled", "1");
|
||||
#endif // _WIN32
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
|
@ -135,7 +135,7 @@ ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
double px_w, double px_h)
|
||||
{
|
||||
ExPolygons polys; polys.reserve(rings.size());
|
||||
auto polys = reserve_vector<ExPolygon>(rings.size());
|
||||
|
||||
for (const marchsq::Ring &ring : rings) {
|
||||
Polygon poly; Points &pts = poly.points;
|
||||
@ -147,7 +147,7 @@ ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
polys.emplace_back(poly);
|
||||
}
|
||||
|
||||
// reverse the raster transformations
|
||||
// TODO: Is a union necessary?
|
||||
return union_ex(polys);
|
||||
}
|
||||
|
||||
@ -270,11 +270,11 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||
if (!png::decode_png(rb, img)) return;
|
||||
|
||||
auto rings = marchsq::execute(img, 128, rstp.win);
|
||||
uint8_t isoval = 128;
|
||||
auto rings = marchsq::execute(img, isoval, rstp.win);
|
||||
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||
|
||||
// Invert the raster transformations indicated in
|
||||
// the profile metadata
|
||||
// Invert the raster transformations indicated in the profile metadata
|
||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||
|
||||
slices[i] = std::move(expolys);
|
||||
@ -310,7 +310,24 @@ ConfigSubstitutions import_sla_archive(
|
||||
std::string exclude_entries{"thumbnail"};
|
||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||
DynamicPrintConfig profile_in, profile_use;
|
||||
ConfigSubstitutions config_substitutions = profile_in.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
ConfigSubstitutions config_substitutions =
|
||||
profile_in.load(arch.profile,
|
||||
ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
if (profile_in.empty()) { // missing profile... do guess work
|
||||
// try to recover the layer height from the config.ini which was
|
||||
// present in all versions of sl1 files.
|
||||
if (auto lh_opt = arch.config.find("layerHeight");
|
||||
lh_opt != arch.config.not_found())
|
||||
{
|
||||
auto lh_str = lh_opt->second.data();
|
||||
try {
|
||||
double lh = std::stod(lh_str); // TODO replace with std::from_chars
|
||||
profile_out.set("layer_height", lh);
|
||||
profile_out.set("initial_layer_height", lh);
|
||||
} catch(...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// If the archive contains an empty profile, use the one that was passed as output argument
|
||||
// then replace it with the readed profile to report that it was empty.
|
||||
|
@ -40,7 +40,8 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool contained_z = -1e10 < obj_min_z && obj_max_z < print_volume_height;
|
||||
|
||||
const bool contained_z = -1e10 < obj_min_z && obj_max_z <= print_volume_height;
|
||||
return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
|
||||
@ -1263,10 +1264,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||
instances[instance]->get_mirror()
|
||||
);
|
||||
|
||||
z -= instances[instance]->get_offset()(2);
|
||||
z -= instances[instance]->get_offset().z();
|
||||
|
||||
// Lower part per-instance bounding boxes
|
||||
std::vector<BoundingBoxf3> lower_bboxes { instances.size() };
|
||||
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||
Vec3d local_displace = Vec3d::Zero();
|
||||
|
||||
for (ModelVolume *volume : volumes) {
|
||||
const auto volume_matrix = volume->get_matrix();
|
||||
@ -1286,8 +1287,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower->add_volume(*volume);
|
||||
}
|
||||
else if (! volume->mesh().empty()) {
|
||||
|
||||
else if (! volume->mesh().empty()) {
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
@ -1327,13 +1327,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||
assert(vol->config.id() != volume->config.id());
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
// Compute the lower part instances' bounding boxes to figure out where to place
|
||||
// the upper part
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
for (size_t i = 0; i < instances.size(); i++) {
|
||||
lower_bboxes[i].merge(instances[i]->transform_mesh_bounding_box(lower_mesh, true));
|
||||
}
|
||||
}
|
||||
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1341,17 +1338,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||
ModelObjectPtrs res;
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
||||
upper->invalidate_bounding_box();
|
||||
upper->center_around_origin();
|
||||
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
upper->center_around_origin();
|
||||
upper->translate_instances(-upper->origin_translation);
|
||||
upper->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (size_t i = 0; i < instances.size(); i++) {
|
||||
for (size_t i = 0; i < instances.size(); ++i) {
|
||||
auto &instance = upper->instances[i];
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation()(2);
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
const Vec3d displace = lower_bboxes[i].size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace;
|
||||
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset + displace);
|
||||
@ -1361,14 +1359,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
||||
res.push_back(upper);
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
||||
lower->invalidate_bounding_box();
|
||||
lower->center_around_origin();
|
||||
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
lower->center_around_origin();
|
||||
lower->translate_instances(-lower->origin_translation);
|
||||
lower->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (auto *instance : lower->instances) {
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation()(2);
|
||||
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset);
|
||||
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
|
||||
|
@ -1113,7 +1113,7 @@ static inline Polygon to_polygon(const std::vector<Linef> &lines)
|
||||
// It iterates through all nodes on the border between two different colors, and from this point,
|
||||
// start selection always left most edges for every node to construct CCW polygons.
|
||||
// Assumes that graph is planar (without self-intersection edges)
|
||||
static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MMU_Graph &graph)
|
||||
static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders)
|
||||
{
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
// When there is no next arc, then is returned original_arc or edge with is marked as used
|
||||
@ -1153,7 +1153,7 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
||||
return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
|
||||
};
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> polygons_segments;
|
||||
std::vector<ExPolygons> expolygons_segments(num_extruders + 1);
|
||||
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
|
||||
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
|
||||
@ -1183,12 +1183,11 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MM
|
||||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
Polygon poly = to_polygon(face_lines);
|
||||
if (poly.is_counter_clockwise() && poly.is_valid())
|
||||
polygons_segments.emplace_back(poly, arc.color);
|
||||
if (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid())
|
||||
expolygons_segments[arc.color].emplace_back(std::move(poly));
|
||||
}
|
||||
}
|
||||
return polygons_segments;
|
||||
return expolygons_segments;
|
||||
}
|
||||
|
||||
// Used in remove_multiple_edges_in_vertices()
|
||||
@ -1269,21 +1268,20 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
|
||||
}
|
||||
}
|
||||
|
||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
||||
const float cut_width,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||
std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
const float cut_width,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()),[&segmented_regions, &input_expolygons, &cut_width, &throw_on_cancel_callback](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
std::vector<std::pair<ExPolygon, size_t>> segmented_regions_cuts;
|
||||
for (const std::pair<ExPolygon, size_t> &colored_expoly : segmented_regions[layer_idx]) {
|
||||
ExPolygons cut_colored_expoly = diff_ex(colored_expoly.first, offset_ex(input_expolygons[layer_idx], cut_width));
|
||||
for (ExPolygon &expoly : cut_colored_expoly)
|
||||
segmented_regions_cuts.emplace_back(std::move(expoly), colored_expoly.second);
|
||||
}
|
||||
const size_t num_extruders_plus_one = segmented_regions[layer_idx].size();
|
||||
std::vector<ExPolygons> segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id
|
||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx)
|
||||
if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty())
|
||||
segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], cut_width));
|
||||
segmented_regions[layer_idx] = std::move(segmented_regions_cuts);
|
||||
}
|
||||
}); // end of parallel_for
|
||||
@ -1323,7 +1321,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
// Project upwards pointing painted triangles over top surfaces,
|
||||
// project downards pointing painted triangles over bottom surfaces.
|
||||
std::vector<std::vector<Polygons>> top_raw(num_extruders), bottom_raw(num_extruders);
|
||||
std::vector<float> zs = zs_from_layers(print_object.layers());
|
||||
std::vector<float> zs = zs_from_layers(layers);
|
||||
Transform3d object_trafo = print_object.trafo_centered();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
@ -1532,31 +1530,42 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
||||
return triangles_by_color_merged;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_layers(
|
||||
const std::vector<std::vector<std::pair<ExPolygon, size_t>>> &segmented_regions,
|
||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
static std::vector<std::vector<ExPolygons>> merge_segmented_layers(
|
||||
const std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||
const size_t num_extruders,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged(segmented_regions.size());
|
||||
const size_t num_layers = segmented_regions.size();
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions_merged(num_layers);
|
||||
segmented_regions_merged.assign(num_layers, std::vector<ExPolygons>(num_extruders));
|
||||
assert(num_extruders + 1 == top_and_bottom_layers.size());
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
for (const std::pair<ExPolygon, size_t> &colored_expoly : segmented_regions[layer_idx]) {
|
||||
assert(segmented_regions[layer_idx].size() == num_extruders + 1);
|
||||
// Zero is skipped because it is the default color of the volume
|
||||
for (size_t extruder_id = 1; extruder_id < num_extruders + 1; ++extruder_id) {
|
||||
throw_on_cancel_callback();
|
||||
// Zero is the default color of the volume.
|
||||
if(colored_expoly.second == 0)
|
||||
continue;
|
||||
ExPolygons cut_colored_expoly = {colored_expoly.first};
|
||||
for (const std::vector<ExPolygons> &top_and_bottom_layer : top_and_bottom_layers)
|
||||
cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]);
|
||||
for (ExPolygon &ex_poly : cut_colored_expoly)
|
||||
segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second - 1);
|
||||
}
|
||||
if (!segmented_regions[layer_idx][extruder_id].empty()) {
|
||||
ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id];
|
||||
for (const std::vector<ExPolygons> &top_and_bottom_by_extruder : top_and_bottom_layers)
|
||||
if (!top_and_bottom_by_extruder[layer_idx].empty() && !segmented_regions_trimmed.empty())
|
||||
segmented_regions_trimmed = diff_ex(segmented_regions_trimmed, top_and_bottom_by_extruder[layer_idx]);
|
||||
|
||||
for (size_t color_idx = 1; color_idx < top_and_bottom_layers.size(); ++color_idx)
|
||||
for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx])
|
||||
segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx - 1);
|
||||
segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed);
|
||||
}
|
||||
|
||||
if (!top_and_bottom_layers[extruder_id][layer_idx].empty()) {
|
||||
bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty();
|
||||
append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]);
|
||||
|
||||
// Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers.
|
||||
if (!was_top_and_bottom_empty)
|
||||
segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON));
|
||||
}
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging segmented layers in parallel - end";
|
||||
@ -1565,7 +1574,7 @@ static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_la
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<std::pair<ExPolygon, size_t>> ®ions, const ExPolygons &lslices)
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<ExPolygons> ®ions, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
@ -1574,12 +1583,12 @@ static void export_regions_to_svg(const std::string &path, const std::vector<std
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
||||
for (const std::pair<ExPolygon, size_t> ®ion : regions) {
|
||||
int region_color = int(region.second);
|
||||
if (region_color >= 0 && region_color < int(colors.size()))
|
||||
svg.draw(region.first, colors[region_color]);
|
||||
for (const ExPolygons &by_extruder : regions) {
|
||||
size_t extrude_idx = &by_extruder - ®ions.front();
|
||||
if (extrude_idx >= 0 && extrude_idx < int(colors.size()))
|
||||
svg.draw(by_extruder, colors[extrude_idx], stroke_width);
|
||||
else
|
||||
svg.draw(region.first, "black");
|
||||
svg.draw(by_extruder, "black", stroke_width);
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
@ -1667,20 +1676,23 @@ static bool has_layer_only_one_color(const std::vector<std::vector<ColoredLine>>
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions(print_object.layers().size());
|
||||
std::vector<std::vector<PaintedLine>> painted_lines(print_object.layers().size());
|
||||
std::array<std::mutex, 64> painted_lines_mutex;
|
||||
std::vector<EdgeGrid::Grid> edge_grids(print_object.layers().size());
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<ExPolygons> input_expolygons(layers.size());
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size();
|
||||
const size_t num_layers = print_object.layers().size();
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions(num_layers);
|
||||
segmented_regions.assign(num_layers, std::vector<ExPolygons>(num_extruders + 1));
|
||||
std::vector<std::vector<PaintedLine>> painted_lines(num_layers);
|
||||
std::array<std::mutex, 64> painted_lines_mutex;
|
||||
std::vector<EdgeGrid::Grid> edge_grids(num_layers);
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<ExPolygons> input_expolygons(num_layers);
|
||||
|
||||
throw_on_cancel_callback();
|
||||
|
||||
// Merge all regions and remove small holes
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&layers, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
ExPolygons ex_polygons;
|
||||
@ -1711,7 +1723,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end";
|
||||
|
||||
for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) {
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
||||
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
||||
@ -1723,8 +1735,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin";
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders), [&mv, &print_object, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
||||
throw_on_cancel_callback();
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
@ -1732,7 +1743,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
continue;
|
||||
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
@ -1748,15 +1759,15 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON),
|
||||
auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON),
|
||||
auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z + EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
--last_layer;
|
||||
|
||||
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
||||
const Layer *layer = *layer_it;
|
||||
size_t layer_idx = layer_it - print_object.layers().begin();
|
||||
size_t layer_idx = layer_it - layers.begin();
|
||||
if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z())
|
||||
continue;
|
||||
|
||||
@ -1799,7 +1810,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
<< std::count_if(painted_lines.begin(), painted_lines.end(), [](const std::vector<PaintedLine> &pl) { return !pl.empty(); });
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - layers segmentation in parallel - begin";
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, print_object.layers().size()), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&edge_grids, &input_expolygons, &painted_lines, &segmented_regions, &num_extruders, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
if (!painted_lines[layer_idx].empty()) {
|
||||
@ -1832,8 +1843,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
assert(!color_poly.front().empty());
|
||||
if (has_layer_only_one_color(color_poly)) {
|
||||
// If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer.
|
||||
for (const ExPolygon &ex_polygon : input_expolygons[layer_idx])
|
||||
segmented_regions[layer_idx].emplace_back(ex_polygon, size_t(color_poly.front().front().color));
|
||||
segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx];
|
||||
} else {
|
||||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||
@ -1846,9 +1856,7 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> segmentation = extract_colored_segments(graph);
|
||||
for (std::pair<Polygon, size_t> ®ion : segmentation)
|
||||
segmented_regions[layer_idx].emplace_back(std::move(region));
|
||||
segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders);
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
@ -1868,11 +1876,11 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
||||
throw_on_cancel_callback();
|
||||
}
|
||||
|
||||
// return segmented_regions;
|
||||
std::vector<std::vector<ExPolygons>> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback);
|
||||
// The first index is extruder number (includes default extruder), and the second one is layer number
|
||||
std::vector<std::vector<ExPolygons>> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback);
|
||||
std::vector<std::vector<ExPolygons>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), num_extruders, throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
@ -11,7 +11,7 @@ class PrintObject;
|
||||
class ExPolygon;
|
||||
|
||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -534,6 +534,7 @@ static std::vector<std::string> s_Preset_sla_print_options {
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_sla_material_options {
|
||||
"material_colour",
|
||||
"material_type",
|
||||
"initial_layer_height",
|
||||
"bottle_cost",
|
||||
|
@ -3163,6 +3163,13 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
|
||||
// SLA Material settings.
|
||||
|
||||
def = this->add("material_colour", coStrings);
|
||||
def->label = L("Color");
|
||||
def->tooltip = L("This is only used in the Slic3r interface as a visual help.");
|
||||
def->gui_type = ConfigOptionDef::GUIType::color;
|
||||
def->set_default_value(new ConfigOptionStrings{ "#29B2B2" });
|
||||
|
||||
def = this->add("material_type", coString);
|
||||
def->label = L("SLA material type");
|
||||
def->tooltip = L("SLA material type");
|
||||
|
@ -538,7 +538,7 @@ template<typename ThrowOnCancel>
|
||||
static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
|
||||
{
|
||||
// Returns MMU segmentation based on painting in MMU segmentation gizmo
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel);
|
||||
std::vector<std::vector<ExPolygons>> segmentation = multi_material_segmentation_by_painting(print_object, throw_on_cancel);
|
||||
assert(segmentation.size() == print_object.layer_count());
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, segmentation.size(), std::max(segmentation.size() / 128, size_t(1))),
|
||||
@ -568,9 +568,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||
bool layer_split = false;
|
||||
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) {
|
||||
ByExtruder ®ion = by_extruder[extruder_id];
|
||||
for (const std::pair<ExPolygon, size_t> &colored_polygon : segmentation[layer_id])
|
||||
if (colored_polygon.second == extruder_id)
|
||||
region.expolygons.emplace_back(std::move(colored_polygon.first));
|
||||
append(region.expolygons, std::move(segmentation[layer_id][extruder_id]));
|
||||
if (! region.expolygons.empty()) {
|
||||
region.bbox = get_extents(region.expolygons);
|
||||
layer_split = true;
|
||||
@ -632,6 +630,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
||||
if (mine.empty())
|
||||
break;
|
||||
}
|
||||
// Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region().
|
||||
// ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region()
|
||||
// (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from
|
||||
// layerm.region() could produce a huge number of small unprintable regions for the model's base extruder.
|
||||
// This could, on some models, produce bulges with the model's base color (#7109).
|
||||
if (! mine.empty())
|
||||
mine = opening(union_ex(mine), float(scale_(5 * EPSILON)), float(scale_(5 * EPSILON)));
|
||||
if (! mine.empty()) {
|
||||
ByRegion &dst = by_region[layerm.region().print_object_region_id()];
|
||||
if (dst.expolygons.empty()) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <numeric>
|
||||
|
||||
#include "SlicesToTriangleMesh.hpp"
|
||||
|
||||
@ -22,11 +23,16 @@ inline indexed_triangle_set wall_strip(const Polygon &poly,
|
||||
|
||||
ret.vertices.reserve(ret.vertices.size() + 2 *offs);
|
||||
|
||||
// The expression unscaled(p).cast<float>().eval() is important here
|
||||
// as it ensures identical conversion of 2D scaled coordinates to float 3D
|
||||
// to that used by the tesselation. This way, the duplicated vertices in the
|
||||
// output mesh can be found with the == operator of the points.
|
||||
// its_merge_vertices will then reliably remove the duplicates.
|
||||
for (const Point &p : poly.points)
|
||||
ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(lower_z_mm)));
|
||||
ret.vertices.emplace_back(to_3d(unscaled(p).cast<float>().eval(), float(lower_z_mm)));
|
||||
|
||||
for (const Point &p : poly.points)
|
||||
ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(upper_z_mm)));
|
||||
ret.vertices.emplace_back(to_3d(unscaled(p).cast<float>().eval(), float(upper_z_mm)));
|
||||
|
||||
for (size_t i = startidx + 1; i < startidx + offs; ++i) {
|
||||
ret.indices.emplace_back(i - 1, i, i + offs - 1);
|
||||
@ -84,12 +90,14 @@ indexed_triangle_set slices_to_mesh(
|
||||
const ExPolygons &upper = slices[i + 1];
|
||||
const ExPolygons &lower = slices[i];
|
||||
|
||||
ExPolygons dff1 = diff_ex(lower, upper);
|
||||
ExPolygons dff2 = diff_ex(upper, lower);
|
||||
its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP));
|
||||
its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN));
|
||||
// Small 0 area artefacts can be created by diff_ex, and the
|
||||
// tesselation also can create 0 area triangles. These will be removed
|
||||
// by its_remove_degenerate_faces.
|
||||
ExPolygons free_top = diff_ex(lower, upper);
|
||||
ExPolygons overhang = diff_ex(upper, lower);
|
||||
its_merge(layers[i], triangulate_expolygons_3d(free_top, grid[i], NORMALS_UP));
|
||||
its_merge(layers[i], triangulate_expolygons_3d(overhang, grid[i], NORMALS_DOWN));
|
||||
its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1]));
|
||||
|
||||
});
|
||||
|
||||
auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) {
|
||||
@ -99,37 +107,30 @@ indexed_triangle_set slices_to_mesh(
|
||||
auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(),
|
||||
indexed_triangle_set{}, merge_fn);
|
||||
|
||||
// sla::Contour3D ret = tbb::parallel_reduce(
|
||||
// tbb::blocked_range(layers.begin(), layers.end()),
|
||||
// sla::Contour3D{},
|
||||
// [](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D
|
||||
// init) {
|
||||
// for(auto it = r.begin(); it != r.end(); ++it )
|
||||
// init.merge(*it); return init;
|
||||
// },
|
||||
// []( const sla::Contour3D &a, const sla::Contour3D &b ) {
|
||||
// sla::Contour3D res{a}; res.merge(b); return res;
|
||||
// });
|
||||
|
||||
its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN));
|
||||
its_merge(ret, straight_walls(slices.front(), zmin, grid.front()));
|
||||
its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP));
|
||||
|
||||
|
||||
// FIXME: these repairs do not fix the mesh entirely. There will be cracks
|
||||
// in the output. It is very hard to do the meshing in a way that does not
|
||||
// leave errors.
|
||||
its_merge_vertices(ret);
|
||||
its_remove_degenerate_faces(ret);
|
||||
its_compactify_vertices(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void slices_to_mesh(indexed_triangle_set & mesh,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh)
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh)
|
||||
{
|
||||
std::vector<indexed_triangle_set> wall_meshes(slices.size());
|
||||
std::vector<float> grid(slices.size(), zmin + ilh);
|
||||
|
||||
for (size_t i = 1; i < grid.size(); ++i)
|
||||
grid[i] = grid[i - 1] + lh;
|
||||
|
||||
|
||||
for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh;
|
||||
|
||||
indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid);
|
||||
its_merge(mesh, cntr);
|
||||
}
|
||||
|
@ -7,10 +7,10 @@
|
||||
namespace Slic3r {
|
||||
|
||||
void slices_to_mesh(indexed_triangle_set & mesh,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh);
|
||||
const std::vector<ExPolygons> &slices,
|
||||
double zmin,
|
||||
double lh,
|
||||
double ilh);
|
||||
|
||||
inline indexed_triangle_set slices_to_mesh(
|
||||
const std::vector<ExPolygons> &slices, double zmin, double lh, double ilh)
|
||||
|
@ -74,22 +74,31 @@
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha4 techs
|
||||
// 2.4.0.beta1 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA4 1
|
||||
#define ENABLE_2_4_0_BETA1 1
|
||||
|
||||
// Enable rendering modifiers and similar objects always as transparent
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4)
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_BETA1)
|
||||
// Enable the fix for the detection of the out of bed state for sinking objects
|
||||
// and detection of out of bed using the bed perimeter
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4)
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_BETA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.beta2 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_BETA2 1
|
||||
|
||||
// Enable modified ImGuiWrapper::slider_float() to create a compound widget where
|
||||
// an additional button can be used to set the keyboard focus into the slider
|
||||
// to allow the user to type in the desired value
|
||||
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4)
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_BETA2)
|
||||
// Enable showing world coordinates of volumes' offset relative to the instance containing them
|
||||
#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (0 && ENABLE_WORLD_COORDINATE)
|
||||
// Enable editing instance coordinates of volumes
|
||||
#define ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES (1 && ENABLE_WORLD_COORDINATE)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
@ -705,22 +705,16 @@ void its_flip_triangles(indexed_triangle_set &its)
|
||||
|
||||
int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit)
|
||||
{
|
||||
int last = 0;
|
||||
for (int i = 0; i < int(its.indices.size()); ++ i) {
|
||||
const stl_triangle_vertex_indices &face = its.indices[i];
|
||||
if (face(0) != face(1) && face(0) != face(2) && face(1) != face(2)) {
|
||||
if (last < i)
|
||||
its.indices[last] = its.indices[i];
|
||||
++ last;
|
||||
}
|
||||
}
|
||||
int removed = int(its.indices.size()) - last;
|
||||
if (removed) {
|
||||
its.indices.erase(its.indices.begin() + last, its.indices.end());
|
||||
// Optionally shrink the vertices.
|
||||
if (shrink_to_fit)
|
||||
its.indices.shrink_to_fit();
|
||||
}
|
||||
auto it = std::remove_if(its.indices.begin(), its.indices.end(), [](auto &face) {
|
||||
return face(0) == face(1) || face(0) == face(2) || face(1) == face(2);
|
||||
});
|
||||
|
||||
int removed = std::distance(it, its.indices.end());
|
||||
its.indices.erase(it, its.indices.end());
|
||||
|
||||
if (removed && shrink_to_fit)
|
||||
its.indices.shrink_to_fit();
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../GUI/GUI_App.hpp"
|
||||
#include "../GUI/I18N.hpp"
|
||||
#include "../GUI/MainFrame.hpp"
|
||||
#include "../GUI/MsgDialog.hpp"
|
||||
|
||||
#include <wx/richmsgdlg.h>
|
||||
|
||||
@ -591,7 +592,7 @@ bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot:
|
||||
SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
|
||||
return true;
|
||||
} catch (std::exception &err) {
|
||||
wxRichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||
RichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||
_L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message),
|
||||
_L("PrusaSlicer error"),
|
||||
wxYES_NO);
|
||||
|
@ -1131,29 +1131,45 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||
if (config == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
||||
if (extruders_opt == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
|
||||
if (filamemts_opt == nullptr)
|
||||
return;
|
||||
|
||||
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
||||
if (colors_count == 0)
|
||||
return;
|
||||
|
||||
std::vector<Color> colors(colors_count);
|
||||
|
||||
unsigned char rgb[3];
|
||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
||||
std::vector<Color> colors;
|
||||
|
||||
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||
{
|
||||
const ConfigOptionStrings* resin_clr = dynamic_cast<const ConfigOptionStrings*>(config->option("material_colour"));
|
||||
if (resin_clr == nullptr)
|
||||
return;
|
||||
assert(resin_clr->values.size() == 1);
|
||||
colors.resize(1);
|
||||
|
||||
const std::string& txt_color = config->opt_string("material_colour", 0);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
else {
|
||||
const std::string& txt_color = config->opt_string("filament_colour", i);
|
||||
colors[0].set(txt_color, rgb);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
|
||||
if (extruders_opt == nullptr)
|
||||
return;
|
||||
|
||||
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
|
||||
if (filamemts_opt == nullptr)
|
||||
return;
|
||||
|
||||
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
|
||||
if (colors_count == 0)
|
||||
return;
|
||||
colors.resize(colors_count);
|
||||
|
||||
for (unsigned int i = 0; i < colors_count; ++i) {
|
||||
const std::string& txt_color = config->opt_string("extruder_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
else {
|
||||
const std::string& txt_color = config->opt_string("filament_colour", i);
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
|
||||
colors[i].set(txt_color, rgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ struct LifetimeGuard
|
||||
|
||||
BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
, list(new wxListView(this, wxID_ANY))
|
||||
, list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSIMPLE_BORDER))
|
||||
, replies(new ReplySet)
|
||||
, label(new wxStaticText(this, wxID_ANY, ""))
|
||||
, timer(new wxTimer())
|
||||
|
@ -27,6 +27,10 @@
|
||||
#include <wx/wupdlock.h>
|
||||
#include <wx/debug.h>
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
#include <wx/msw/dark_mode.h>
|
||||
#endif // _MSW_DARK_MODE
|
||||
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
@ -551,7 +555,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
|
||||
wizard_p()->on_printer_pick(this, evt);
|
||||
});
|
||||
|
||||
append(new wxStaticLine(this));
|
||||
append(new StaticLine(this));
|
||||
|
||||
append(picker);
|
||||
printer_pickers.push_back(picker);
|
||||
@ -2796,7 +2800,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||
|
||||
auto *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *hline = new wxStaticLine(this);
|
||||
auto* hline = new StaticLine(this);
|
||||
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
|
||||
@ -2872,7 +2876,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||
p->index->go_to(size_t{0});
|
||||
|
||||
vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
vsizer->Add(hline, 0, wxEXPAND);
|
||||
vsizer->Add(hline, 0, wxEXPAND | wxLEFT | wxRIGHT, VERTICAL_SPACING);
|
||||
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
|
||||
SetSizer(vsizer);
|
||||
|
@ -286,8 +286,12 @@ void GCodeViewer::SequentialView::Marker::render() const
|
||||
if (width != last_window_width || length != last_text_length) {
|
||||
last_window_width = width;
|
||||
last_text_length = length;
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
|
||||
imgui.end();
|
||||
@ -3379,8 +3383,12 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f);
|
||||
|
||||
// to avoid the tooltip to change size when moving the mouse
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
}
|
||||
|
||||
@ -4115,8 +4123,12 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
if (can_show_mode_button(mode)) {
|
||||
if (imgui.button(label)) {
|
||||
m_time_estimate_mode = mode;
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -722,8 +722,13 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
||||
}
|
||||
|
||||
// force re-render while the windows gets to its final size (it takes several frames)
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
if (ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
m_canvas.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
imgui.end();
|
||||
ImGui::PopStyleColor();
|
||||
@ -734,47 +739,48 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
||||
void GLCanvas3D::Tooltip::set_text(const std::string& text)
|
||||
{
|
||||
// If the mouse is inside an ImGUI dialog, then the tooltip is suppressed.
|
||||
const std::string &new_text = m_in_imgui ? std::string() : text;
|
||||
if (m_text != new_text) {
|
||||
if (m_text.empty())
|
||||
m_start_time = std::chrono::steady_clock::now();
|
||||
|
||||
m_text = new_text;
|
||||
}
|
||||
m_text = m_in_imgui ? std::string() : text;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas) const
|
||||
void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas)
|
||||
{
|
||||
static ImVec2 size(0.0f, 0.0f);
|
||||
|
||||
auto validate_position = [](const Vec2d& position, const GLCanvas3D& canvas, const ImVec2& wnd_size) {
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
float x = std::clamp((float)position(0), 0.0f, (float)cnv_size.get_width() - wnd_size.x);
|
||||
float y = std::clamp((float)position(1) + 16, 0.0f, (float)cnv_size.get_height() - wnd_size.y);
|
||||
const Size cnv_size = canvas.get_canvas_size();
|
||||
const float x = std::clamp((float)position.x(), 0.0f, (float)cnv_size.get_width() - wnd_size.x);
|
||||
const float y = std::clamp((float)position.y() + 16.0f, 0.0f, (float)cnv_size.get_height() - wnd_size.y);
|
||||
return Vec2f(x, y);
|
||||
};
|
||||
|
||||
if (m_text.empty())
|
||||
if (m_text.empty()) {
|
||||
m_start_time = std::chrono::steady_clock::now();
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the tooltip as hidden until the delay is expired
|
||||
// use a value of alpha slightly different from 0.0f because newer imgui does not calculate properly the window size if alpha == 0.0f
|
||||
float alpha = (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start_time).count() < 500) ? 0.01f : 1.0f;
|
||||
const float alpha = (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start_time).count() < 500) ? 0.01f : 1.0f;
|
||||
|
||||
Vec2f position = validate_position(mouse_position, canvas, size);
|
||||
const Vec2f position = validate_position(mouse_position, canvas, size);
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
|
||||
imgui.set_next_window_pos(position(0), position(1), ImGuiCond_Always, 0.0f, 0.0f);
|
||||
imgui.set_next_window_pos(position.x(), position.y(), ImGuiCond_Always, 0.0f, 0.0f);
|
||||
|
||||
imgui.begin(wxString("canvas_tooltip"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
|
||||
ImGui::TextUnformatted(m_text.c_str());
|
||||
|
||||
// force re-render while the windows gets to its final size (it may take several frames) or while hidden
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (alpha < 1.0f || ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
imgui.set_requires_extra_frame();
|
||||
#else
|
||||
if (alpha < 1.0f || ImGui::GetWindowContentRegionWidth() + 2.0f * ImGui::GetStyle().WindowPadding.x != ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x)
|
||||
canvas.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
size = ImGui::GetWindowSize();
|
||||
|
||||
@ -2250,14 +2256,29 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
auto gizmo = wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current();
|
||||
if (gizmo != nullptr) m_dirty |= gizmo->update_items_state();
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// ImGuiWrapper::m_requires_extra_frame may have been set by a render made outside of the OnIdle mechanism
|
||||
bool imgui_requires_extra_frame = wxGetApp().imgui()->requires_extra_frame();
|
||||
m_dirty |= imgui_requires_extra_frame;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
if (!m_dirty)
|
||||
return;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
// this needs to be done here.
|
||||
// during the render launched by the refresh the value may be set again
|
||||
wxGetApp().imgui()->reset_requires_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
_refresh_if_shown_on_screen();
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied || imgui_requires_extra_frame || wxGetApp().imgui()->requires_extra_frame()) {
|
||||
#else
|
||||
if (m_extra_frame_requested || mouse3d_controller_applied) {
|
||||
m_dirty = true;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_extra_frame_requested = false;
|
||||
evt.RequestMore();
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ class GLCanvas3D
|
||||
public:
|
||||
bool is_empty() const { return m_text.empty(); }
|
||||
void set_text(const std::string& text);
|
||||
void render(const Vec2d& mouse_position, GLCanvas3D& canvas) const;
|
||||
void render(const Vec2d& mouse_position, GLCanvas3D& canvas);
|
||||
// Indicates that the mouse is inside an ImGUI dialog, therefore the tooltip should be suppressed.
|
||||
void set_in_imgui(bool b) { m_in_imgui = b; }
|
||||
bool is_in_imgui() const { return m_in_imgui; }
|
||||
|
@ -79,7 +79,9 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||
// For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
|
||||
// Because of this, objects had darker colors inside the multi-material gizmo.
|
||||
// Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
|
||||
if (platform_flavor() == PlatformFlavor::OSXOnArm)
|
||||
// Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed
|
||||
// triangle normals inside fragment shader have the right direction.
|
||||
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12)
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
|
||||
else
|
||||
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "GUI_Init.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "format.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
@ -410,7 +411,7 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
||||
"location again.\n\n"
|
||||
"What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str());
|
||||
wxString caption = from_u8((boost::format(_u8L("%s - BREAKING CHANGE")) % SLIC3R_APP_NAME).str());
|
||||
wxRichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
|
||||
RichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
|
||||
dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application"));
|
||||
if (dlg.ShowModal() != wxID_NO)
|
||||
return false;
|
||||
@ -845,7 +846,7 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
|
||||
return false;
|
||||
BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
|
||||
// ask about using older data folder
|
||||
wxRichMessageDialog msg(nullptr, backup ?
|
||||
RichMessageDialog msg(nullptr, backup ?
|
||||
wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
|
||||
"\nIts version is %s."
|
||||
"\nLast version you used in current configuration folder is %s."
|
||||
@ -935,7 +936,7 @@ bool GUI_App::on_init_inner()
|
||||
// win32 build on win64 and viceversa
|
||||
#ifdef _WIN64
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") {
|
||||
wxRichMessageDialog dlg(nullptr,
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 64-bit architecture on 32-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
@ -945,7 +946,7 @@ bool GUI_App::on_init_inner()
|
||||
}
|
||||
#elif _WIN32
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
|
||||
wxRichMessageDialog dlg(nullptr,
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 32-bit architecture on 64-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
@ -990,7 +991,7 @@ bool GUI_App::on_init_inner()
|
||||
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
|
||||
|
||||
if (!msg.empty() && !ssl_accept) {
|
||||
wxRichMessageDialog
|
||||
RichMessageDialog
|
||||
dlg(nullptr,
|
||||
wxString::Format(_L("%s\nDo you want to continue?"), msg),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
@ -1020,11 +1021,7 @@ bool GUI_App::on_init_inner()
|
||||
wxInitAllImageHandlers();
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (bool dark_mode = app_config->get("dark_color_mode") == "1") {
|
||||
NppDarkMode::InitDarkMode();
|
||||
if (dark_mode != NppDarkMode::IsDarkMode())
|
||||
NppDarkMode::SetDarkMode(dark_mode);
|
||||
}
|
||||
NppDarkMode::InitDarkMode(app_config->get("dark_color_mode") == "1", app_config->get("sys_menu_enabled") == "1");
|
||||
#endif
|
||||
SplashScreen* scrn = nullptr;
|
||||
if (app_config->get("show_splash_screen") == "1") {
|
||||
@ -1347,10 +1344,9 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
UpdateDarkUI(dvc, highlited ? dark_mode() : false);
|
||||
wxItemAttr attr(dark_mode() ? m_color_highlight_default : m_color_label_default,
|
||||
m_color_window_default,
|
||||
m_normal_font);
|
||||
dvc->SetHeaderAttr(attr);
|
||||
#ifdef _MSW_DARK_MODE
|
||||
dvc->RefreshHeaderDarkMode(&m_normal_font);
|
||||
#endif //_MSW_DARK_MODE
|
||||
if (dvc->HasFlag(wxDV_ROW_LINES))
|
||||
dvc->SetAlternateRowColour(m_color_highlight_default);
|
||||
if (dvc->GetBorder() != wxBORDER_SIMPLE)
|
||||
@ -1574,12 +1570,44 @@ void fatal_error(wxWindow* parent)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
static void update_scrolls(wxWindow* window)
|
||||
{
|
||||
wxWindowList::compatibility_iterator node = window->GetChildren().GetFirst();
|
||||
while (node)
|
||||
{
|
||||
wxWindow* win = node->GetData();
|
||||
if (dynamic_cast<wxScrollHelper*>(win) ||
|
||||
dynamic_cast<wxTreeCtrl*>(win) ||
|
||||
dynamic_cast<wxTextCtrl*>(win))
|
||||
NppDarkMode::SetDarkExplorerTheme(win->GetHWND());
|
||||
|
||||
update_scrolls(win);
|
||||
node = node->GetNext();
|
||||
}
|
||||
}
|
||||
#endif //_MSW_DARK_MODE
|
||||
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
void GUI_App::force_menu_update()
|
||||
{
|
||||
NppDarkMode::SetSystemMenuForApp(app_config->get("sys_menu_enabled") == "1");
|
||||
}
|
||||
#endif //_MSW_DARK_MODE
|
||||
|
||||
void GUI_App::force_colors_update()
|
||||
{
|
||||
#ifdef _MSW_DARK_MODE
|
||||
NppDarkMode::SetDarkMode(app_config->get("dark_color_mode") == "1");
|
||||
if (WXHWND wxHWND = wxToolTip::GetToolTipCtrl())
|
||||
NppDarkMode::SetDarkExplorerTheme((HWND)wxHWND);
|
||||
NppDarkMode::SetDarkTitleBar(mainframe->GetHWND());
|
||||
#endif //_MSW_DARK_MODE
|
||||
m_force_colors_update = true;
|
||||
}
|
||||
#endif
|
||||
#endif //_WIN32
|
||||
|
||||
// Called after the Preferences dialog is closed and the program settings are saved.
|
||||
// Update the UI based on the current preferences.
|
||||
@ -1587,11 +1615,15 @@ void GUI_App::update_ui_from_settings()
|
||||
{
|
||||
update_label_colours();
|
||||
#ifdef _WIN32
|
||||
// Upadte UU colors before Update UI from settings
|
||||
// Upadte UI colors before Update UI from settings
|
||||
if (m_force_colors_update) {
|
||||
m_force_colors_update = false;
|
||||
mainframe->force_color_changed();
|
||||
mainframe->diff_dialog.force_color_changed();
|
||||
mainframe->printhost_queue_dlg()->force_color_changed();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
update_scrolls(mainframe);
|
||||
#endif //_MSW_DARK_MODE
|
||||
}
|
||||
#endif
|
||||
mainframe->update_ui_from_settings();
|
||||
@ -2824,7 +2856,7 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/*
|
||||
bool launch = true;
|
||||
|
||||
if (get_app_config()->get("suppress_hyperlinks").empty()) {
|
||||
wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
|
||||
RichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
|
||||
dialog.ShowCheckBox(_L("Remember my choice"));
|
||||
int answer = dialog.ShowModal();
|
||||
launch = answer == wxID_YES;
|
||||
|
@ -210,6 +210,9 @@ public:
|
||||
const wxColour& get_color_hovered_btn_label() { return m_color_hovered_btn_label; }
|
||||
const wxColour& get_color_selected_btn_bg() { return m_color_selected_btn_bg; }
|
||||
void force_colors_update();
|
||||
#ifdef _MSW_DARK_MODE
|
||||
void force_menu_update();
|
||||
#endif //_MSW_DARK_MODE
|
||||
#endif
|
||||
|
||||
const wxFont& small_font() { return m_small_font; }
|
||||
|
@ -2044,8 +2044,7 @@ void ObjectList::split()
|
||||
void ObjectList::merge(bool to_multipart_object)
|
||||
{
|
||||
// merge selected objects to the multipart object
|
||||
if (to_multipart_object)
|
||||
{
|
||||
if (to_multipart_object) {
|
||||
auto get_object_idxs = [this](std::vector<int>& obj_idxs, wxDataViewItemArray& sels)
|
||||
{
|
||||
// check selections and split instances to the separated objects...
|
||||
@ -2056,8 +2055,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!instance_selection)
|
||||
{
|
||||
if (!instance_selection) {
|
||||
for (wxDataViewItem item : sels) {
|
||||
assert(m_objects_model->GetItemType(item) & itObject);
|
||||
obj_idxs.emplace_back(m_objects_model->GetIdByItem(item));
|
||||
@ -2069,8 +2067,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
std::map<int, std::set<int>> sel_map;
|
||||
std::set<int> empty_set;
|
||||
for (wxDataViewItem item : sels) {
|
||||
if (m_objects_model->GetItemType(item) & itObject)
|
||||
{
|
||||
if (m_objects_model->GetItemType(item) & itObject) {
|
||||
int obj_idx = m_objects_model->GetIdByItem(item);
|
||||
int inst_cnt = (*m_objects)[obj_idx]->instances.size();
|
||||
if (inst_cnt == 1)
|
||||
@ -2087,8 +2084,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
// all objects, created from the instances will be added to the end of list
|
||||
int new_objects_cnt = 0; // count of this new objects
|
||||
|
||||
for (auto map_item : sel_map)
|
||||
{
|
||||
for (auto map_item : sel_map) {
|
||||
int obj_idx = map_item.first;
|
||||
// object with just 1 instance
|
||||
if (map_item.second.empty()) {
|
||||
@ -2148,37 +2144,36 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
new_object->name = _u8L("Merged");
|
||||
ModelConfig &config = new_object->config;
|
||||
|
||||
for (int obj_idx : obj_idxs)
|
||||
{
|
||||
for (int obj_idx : obj_idxs) {
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
|
||||
const Geometry::Transformation& transformation = object->instances[0]->get_transformation();
|
||||
Vec3d scale = transformation.get_scaling_factor();
|
||||
Vec3d mirror = transformation.get_mirror();
|
||||
Vec3d rotation = transformation.get_rotation();
|
||||
const Vec3d scale = transformation.get_scaling_factor();
|
||||
const Vec3d mirror = transformation.get_mirror();
|
||||
const Vec3d rotation = transformation.get_rotation();
|
||||
|
||||
if (object->id() == (*m_objects)[obj_idxs.front()]->id())
|
||||
new_object->add_instance();
|
||||
Transform3d volume_offset_correction = new_object->instances[0]->get_transformation().get_matrix().inverse() * transformation.get_matrix();
|
||||
const Transform3d& volume_offset_correction = transformation.get_matrix();
|
||||
|
||||
// merge volumes
|
||||
for (const ModelVolume* volume : object->volumes) {
|
||||
ModelVolume* new_volume = new_object->add_volume(*volume);
|
||||
|
||||
//set rotation
|
||||
Vec3d vol_rot = new_volume->get_rotation() + rotation;
|
||||
const Vec3d vol_rot = new_volume->get_rotation() + rotation;
|
||||
new_volume->set_rotation(vol_rot);
|
||||
|
||||
// set scale
|
||||
Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale);
|
||||
const Vec3d vol_sc_fact = new_volume->get_scaling_factor().cwiseProduct(scale);
|
||||
new_volume->set_scaling_factor(vol_sc_fact);
|
||||
|
||||
// set mirror
|
||||
Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror);
|
||||
const Vec3d vol_mirror = new_volume->get_mirror().cwiseProduct(mirror);
|
||||
new_volume->set_mirror(vol_mirror);
|
||||
|
||||
// set offset
|
||||
Vec3d vol_offset = volume_offset_correction* new_volume->get_offset();
|
||||
const Vec3d vol_offset = volume_offset_correction* new_volume->get_offset();
|
||||
new_volume->set_offset(vol_offset);
|
||||
}
|
||||
new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
||||
@ -2211,6 +2206,11 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
for (const auto& range : object->layer_config_ranges)
|
||||
new_object->layer_config_ranges.emplace(range);
|
||||
}
|
||||
|
||||
new_object->center_around_origin();
|
||||
new_object->translate_instances(-new_object->origin_translation);
|
||||
new_object->origin_translation = Vec3d::Zero();
|
||||
|
||||
// remove selected objects
|
||||
remove();
|
||||
|
||||
@ -2221,8 +2221,7 @@ void ObjectList::merge(bool to_multipart_object)
|
||||
}
|
||||
// merge all parts to the one single object
|
||||
// all part's settings will be lost
|
||||
else
|
||||
{
|
||||
else {
|
||||
wxDataViewItem item = GetSelection();
|
||||
if (!item)
|
||||
return;
|
||||
|
@ -1084,6 +1084,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
||||
|
||||
void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
{
|
||||
if (value <= 0.0)
|
||||
return;
|
||||
|
||||
if (std::abs(m_cache.scale_rounded(axis) - value) < EPSILON)
|
||||
return;
|
||||
|
||||
@ -1100,6 +1103,9 @@ void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
|
||||
void ObjectManipulation::change_size_value(int axis, double value)
|
||||
{
|
||||
if (value <= 0.0)
|
||||
return;
|
||||
|
||||
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
|
||||
return;
|
||||
|
||||
@ -1202,10 +1208,26 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double
|
||||
change_position_value(axis, new_value);
|
||||
else if (opt_key == "rotation")
|
||||
change_rotation_value(axis, new_value);
|
||||
else if (opt_key == "scale")
|
||||
change_scale_value(axis, new_value);
|
||||
else if (opt_key == "size")
|
||||
change_size_value(axis, new_value);
|
||||
else if (opt_key == "scale") {
|
||||
if (new_value > 0.0)
|
||||
change_scale_value(axis, new_value);
|
||||
else {
|
||||
new_value = m_cache.scale(axis);
|
||||
m_cache.scale(axis) = 0.0;
|
||||
m_cache.scale_rounded(axis) = DBL_MAX;
|
||||
change_scale_value(axis, new_value);
|
||||
}
|
||||
}
|
||||
else if (opt_key == "size") {
|
||||
if (new_value > 0.0)
|
||||
change_size_value(axis, new_value);
|
||||
else {
|
||||
new_value = m_cache.size(axis);
|
||||
m_cache.size(axis) = 0.0;
|
||||
m_cache.size_rounded(axis) = DBL_MAX;
|
||||
change_size_value(axis, new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectManipulation::set_uniform_scaling(const bool new_value)
|
||||
|
@ -167,7 +167,11 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
|
||||
if (last_h != win_h || last_y != y) {
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->set_requires_extra_frame();
|
||||
#else
|
||||
m_parent.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (last_h != win_h)
|
||||
last_h = win_h;
|
||||
if (last_y != y)
|
||||
|
@ -170,7 +170,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
float slider_start_position = std::max(position_before_text_y, position_after_text_y - slider_height);
|
||||
ImGui::SetCursorPosY(slider_start_position);
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
wxString tooltip = format_wxstr(_L("Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when "
|
||||
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]);
|
||||
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data(), 1.0f, true, tooltip)) {
|
||||
#else
|
||||
if (m_imgui->slider_float("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data())) {
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_parent.set_slope_normal_angle(90.f - m_highlight_by_angle_threshold_deg);
|
||||
if (! m_parent.is_using_slope()) {
|
||||
m_parent.use_slope(true);
|
||||
@ -182,9 +188,11 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
ImGui::SetCursorPosY(std::max(position_before_text_y + slider_height, position_after_text_y));
|
||||
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(format_wxstr(_L("Preselects faces by overhang angle. It is possible to restrict paintable facets to only preselected faces when "
|
||||
"the option \"%1%\" is enabled."), m_desc["on_overhangs_only"]), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->disabled_begin(m_highlight_by_angle_threshold_deg == 0.f);
|
||||
ImGui::NewLine();
|
||||
@ -267,9 +275,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled);
|
||||
|
||||
@ -284,14 +296,20 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel")))
|
||||
#else
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
}
|
||||
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@ -310,11 +328,16 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_left_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
@ -441,9 +441,13 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
m_imgui->checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled);
|
||||
|
||||
@ -460,14 +464,20 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
"placed after the number with no whitespace in between.");
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel")))
|
||||
#else
|
||||
if(m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
}
|
||||
|
||||
#if !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // !ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
@ -484,11 +494,16 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
@ -128,9 +128,13 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel"));
|
||||
#else
|
||||
m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Alt + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("cursor_type"));
|
||||
@ -168,11 +172,16 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
#else
|
||||
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
|
||||
m_c->object_clipper()->set_position(clp_dist, true);
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_L("Ctrl + Mouse wheel"), max_tooltip_width);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_imgui->button(m_desc.at("remove_all"))) {
|
||||
|
@ -256,7 +256,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%":
|
||||
((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%");
|
||||
|
||||
if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) {
|
||||
if(m_imgui->slider_float("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)){
|
||||
if (m_configuration.decimate_ratio < 0.f)
|
||||
m_configuration.decimate_ratio = 0.01f;
|
||||
if (m_configuration.decimate_ratio > 100.f)
|
||||
|
@ -644,7 +644,11 @@ RENDER_AGAIN:
|
||||
if ((last_h != win_h) || (last_y != y))
|
||||
{
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_imgui->set_requires_extra_frame();
|
||||
#else
|
||||
m_parent.request_extra_frame();
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
if (last_h != win_h)
|
||||
last_h = win_h;
|
||||
if (last_y != y)
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
@ -48,7 +51,9 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
||||
{ImGui::RightArrowHoverButton , "notification_right_hover" },
|
||||
{ImGui::PreferencesButton , "notification_preferences" },
|
||||
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
{ImGui::SliderFloatEditBtnIcon, "edit_button" },
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
};
|
||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
{ImGui::CloseNotifButton , "notification_close" },
|
||||
@ -85,14 +90,6 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
|
||||
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED;
|
||||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_glyph_ranges(nullptr)
|
||||
, m_font_cjk(false)
|
||||
, m_font_size(18.0)
|
||||
, m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
, m_mouse_buttons(0)
|
||||
, m_disabled(false)
|
||||
, m_new_frame_open(false)
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
|
||||
@ -484,6 +481,53 @@ void ImGuiWrapper::tooltip(const wxString &label, float wrap_width)
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
bool ret = ImGui::SliderFloat(label, v, v_min, v_max, format, power);
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered())
|
||||
this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width);
|
||||
|
||||
if (clamp)
|
||||
*v = std::clamp(*v, v_min, v_max);
|
||||
|
||||
if (show_edit_btn) {
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
|
||||
ImGui::SameLine();
|
||||
|
||||
std::wstring btn_name;
|
||||
btn_name = ImGui::SliderFloatEditBtnIcon + boost::nowide::widen(std::string(label));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.5f, 0.5f, 0.5f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.5f, 0.5f, 0.5f, 1.0f });
|
||||
if (ImGui::Button(into_u8(btn_name).c_str())) {
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
this->set_requires_extra_frame();
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
if (ImGui::IsItemHovered())
|
||||
this->tooltip(into_u8(_L("Edit")).c_str(), max_tooltip_width);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
return this->slider_float(label.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/, const wxString& tooltip /*= ""*/, bool show_edit_btn /*= true*/)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||
}
|
||||
#else
|
||||
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/)
|
||||
{
|
||||
bool ret = ImGui::SliderFloat(label, v, v_min, v_max, format, power);
|
||||
@ -502,6 +546,7 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl
|
||||
auto label_utf8 = into_u8(label);
|
||||
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp);
|
||||
}
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
|
||||
{
|
||||
|
@ -22,15 +22,18 @@ namespace GUI {
|
||||
|
||||
class ImGuiWrapper
|
||||
{
|
||||
const ImWchar *m_glyph_ranges;
|
||||
const ImWchar* m_glyph_ranges{ nullptr };
|
||||
// Chinese, Japanese, Korean
|
||||
bool m_font_cjk;
|
||||
float m_font_size;
|
||||
unsigned m_font_texture;
|
||||
float m_style_scaling;
|
||||
unsigned m_mouse_buttons;
|
||||
bool m_disabled;
|
||||
bool m_new_frame_open;
|
||||
bool m_font_cjk{ false };
|
||||
float m_font_size{ 18.0 };
|
||||
unsigned m_font_texture{ 0 };
|
||||
float m_style_scaling{ 1.0 };
|
||||
unsigned m_mouse_buttons{ 0 };
|
||||
bool m_disabled{ false };
|
||||
bool m_new_frame_open{ false };
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool m_requires_extra_frame{ false };
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
std::string m_clipboard_text;
|
||||
|
||||
public:
|
||||
@ -90,9 +93,15 @@ public:
|
||||
void tooltip(const wxString &label, float wrap_width);
|
||||
|
||||
// Float sliders: Manually inserted values aren't clamped by ImGui.Using this wrapper function does (when clamp==true).
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = "", bool show_edit_btn = true);
|
||||
#else
|
||||
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
|
||||
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
|
||||
@ -108,6 +117,12 @@ public:
|
||||
bool want_text_input() const;
|
||||
bool want_any_input() const;
|
||||
|
||||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
||||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
static const ImVec4 COL_GREY_DARK;
|
||||
static const ImVec4 COL_GREY_LIGHT;
|
||||
static const ImVec4 COL_ORANGE_DARK;
|
||||
|
@ -122,7 +122,9 @@ public:
|
||||
std::string err;
|
||||
ConfigSubstitutions config_substitutions;
|
||||
|
||||
priv(Plater *plt) : plater{plt} {}
|
||||
ImportDlg import_dlg;
|
||||
|
||||
priv(Plater *plt) : plater{plt}, import_dlg{plt} {}
|
||||
};
|
||||
|
||||
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
|
||||
@ -176,14 +178,12 @@ void SLAImportJob::prepare()
|
||||
{
|
||||
reset();
|
||||
|
||||
ImportDlg dlg{p->plater};
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
auto path = dlg.get_path();
|
||||
if (p->import_dlg.ShowModal() == wxID_OK) {
|
||||
auto path = p->import_dlg.get_path();
|
||||
auto nm = wxFileName(path);
|
||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||
p->sel = dlg.get_selection();
|
||||
p->win = dlg.get_marchsq_windowsize();
|
||||
p->sel = p->import_dlg.get_selection();
|
||||
p->win = p->import_dlg.get_marchsq_windowsize();
|
||||
p->config_substitutions.clear();
|
||||
} else {
|
||||
p->path = "";
|
||||
@ -236,7 +236,7 @@ void SLAImportJob::finalize()
|
||||
|
||||
if (!p->mesh.empty()) {
|
||||
bool is_centered = false;
|
||||
p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh},
|
||||
p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{std::move(p->mesh)},
|
||||
name, is_centered);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace GUI {
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id, wxBitmap bitmap)
|
||||
: wxDialog(parent ? parent : dynamic_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, boldfont(wxGetApp().normal_font()/*wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)*/)
|
||||
, boldfont(wxGetApp().normal_font())
|
||||
, content_sizer(new wxBoxSizer(wxVERTICAL))
|
||||
, btn_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
@ -36,6 +36,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
this->CenterOnParent();
|
||||
|
||||
auto *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
@ -46,6 +47,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||
rightsizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
rightsizer->Add(content_sizer, 1, wxEXPAND);
|
||||
btn_sizer->AddStretchSpacer();
|
||||
|
||||
if (button_id != wxID_NONE) {
|
||||
auto *button = new wxButton(this, button_id);
|
||||
@ -53,18 +55,16 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
||||
btn_sizer->Add(button);
|
||||
}
|
||||
|
||||
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
|
||||
|
||||
if (! bitmap.IsOk()) {
|
||||
bitmap = create_scaled_bitmap("PrusaSlicer_192px.png", this, 192);
|
||||
}
|
||||
|
||||
logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
|
||||
logo = new wxStaticBitmap(this, wxID_ANY, bitmap.IsOk() ? bitmap : wxNullBitmap);
|
||||
|
||||
topsizer->Add(logo, 0, wxALL, BORDER);
|
||||
topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, BORDER);
|
||||
|
||||
SetSizerAndFit(topsizer);
|
||||
main_sizer->Add(topsizer, 1, wxEXPAND);
|
||||
main_sizer->Add(new StaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, HORIZ_SPACING);
|
||||
main_sizer->Add(btn_sizer, 0, wxALL | wxEXPAND, VERT_SPACING);
|
||||
|
||||
SetSizerAndFit(main_sizer);
|
||||
}
|
||||
|
||||
void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
|
||||
@ -72,7 +72,7 @@ void MsgDialog::add_btn(wxWindowID btn_id, bool set_focus /*= false*/)
|
||||
wxButton* btn = new wxButton(this, btn_id);
|
||||
if (set_focus)
|
||||
btn->SetFocus();
|
||||
btn_sizer->Add(btn, 0, wxRIGHT, HORIZ_SPACING);
|
||||
btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, HORIZ_SPACING);
|
||||
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); });
|
||||
};
|
||||
|
||||
@ -209,33 +209,38 @@ MessageDialog::MessageDialog(wxWindow* parent,
|
||||
apply_style(style);
|
||||
finalize();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MessageWithCheckDialog
|
||||
// RichMessageDialog
|
||||
|
||||
MessageWithCheckDialog::MessageWithCheckDialog( wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& checkbox_label,
|
||||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
RichMessageDialog::RichMessageDialog(wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& caption/* = wxEmptyString*/,
|
||||
long style/* = wxOK*/)
|
||||
: MsgDialog(parent, caption.IsEmpty() ? wxString::Format(_L("%s info"), SLIC3R_APP_NAME) : caption, wxEmptyString, wxID_NONE)
|
||||
{
|
||||
add_msg_content(this, content_sizer, message);
|
||||
|
||||
m_check = new wxCheckBox(this, wxID_ANY, checkbox_label);
|
||||
content_sizer->Add(m_check, 0, wxTOP, 10);
|
||||
m_checkBox = new wxCheckBox(this, wxID_ANY, m_checkBoxText);
|
||||
m_checkBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_checkBoxValue = m_checkBox->GetValue(); });
|
||||
|
||||
btn_sizer->Insert(0, m_checkBox, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
apply_style(style);
|
||||
finalize();
|
||||
}
|
||||
|
||||
bool MessageWithCheckDialog::GetCheckVal()
|
||||
int RichMessageDialog::ShowModal()
|
||||
{
|
||||
if (m_check)
|
||||
return m_check->GetValue();
|
||||
return false;
|
||||
if (m_checkBoxText.IsEmpty())
|
||||
m_checkBox->Hide();
|
||||
else
|
||||
m_checkBox->SetLabelText(m_checkBoxText);
|
||||
Layout();
|
||||
|
||||
return wxDialog::ShowModal();
|
||||
}
|
||||
#endif
|
||||
|
||||
// InfoDialog
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/richmsgdlg.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxCheckBox;
|
||||
@ -17,7 +20,6 @@ namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
// A message / query dialog with a bitmap on the left and any content on the right
|
||||
// with buttons underneath.
|
||||
struct MsgDialog : wxDialog
|
||||
@ -87,6 +89,23 @@ public:
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
// Generic static line, used intead of wxStaticLine
|
||||
class StaticLine: public wxTextCtrl
|
||||
{
|
||||
public:
|
||||
StaticLine( wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxLI_HORIZONTAL,
|
||||
const wxString& name = wxString::FromAscii(wxTextCtrlNameStr))
|
||||
: wxTextCtrl(parent, id, wxEmptyString, pos, size!=wxDefaultSize ? size : (style == wxLI_HORIZONTAL ? wxSize(10, 1) : wxSize(1, 10)), wxSIMPLE_BORDER, wxDefaultValidator, name)
|
||||
{
|
||||
this->Enable(false);
|
||||
}
|
||||
~StaticLine() {}
|
||||
};
|
||||
|
||||
// Generic message dialog, used intead of wxMessageDialog
|
||||
class MessageDialog : public MsgDialog
|
||||
{
|
||||
@ -101,7 +120,158 @@ public:
|
||||
MessageDialog &operator=(const MessageDialog&) = delete;
|
||||
virtual ~MessageDialog() = default;
|
||||
};
|
||||
|
||||
// Generic rich message dialog, used intead of wxRichMessageDialog
|
||||
class RichMessageDialog : public MsgDialog
|
||||
{
|
||||
wxCheckBox* m_checkBox{ nullptr };
|
||||
wxString m_checkBoxText;
|
||||
bool m_checkBoxValue{ false };
|
||||
|
||||
public:
|
||||
RichMessageDialog( wxWindow *parent,
|
||||
const wxString& message,
|
||||
const wxString& caption = wxEmptyString,
|
||||
long style = wxOK);
|
||||
RichMessageDialog(RichMessageDialog&&) = delete;
|
||||
RichMessageDialog(const RichMessageDialog&) = delete;
|
||||
RichMessageDialog &operator=(RichMessageDialog&&) = delete;
|
||||
RichMessageDialog &operator=(const RichMessageDialog&) = delete;
|
||||
virtual ~RichMessageDialog() = default;
|
||||
|
||||
int ShowModal() override;
|
||||
|
||||
void ShowCheckBox(const wxString& checkBoxText, bool checked = false)
|
||||
{
|
||||
m_checkBoxText = checkBoxText;
|
||||
m_checkBoxValue = checked;
|
||||
}
|
||||
|
||||
wxString GetCheckBoxText() const { return m_checkBoxText; }
|
||||
bool IsCheckBoxChecked() const { return m_checkBoxValue; }
|
||||
|
||||
// This part o fcode isported from the "wx\msgdlg.h"
|
||||
using wxMD = wxMessageDialogBase;
|
||||
// customization of the message box buttons
|
||||
virtual bool SetYesNoLabels(const wxMD::ButtonLabel& yes, const wxMD::ButtonLabel& no)
|
||||
{
|
||||
DoSetCustomLabel(m_yes, yes);
|
||||
DoSetCustomLabel(m_no, no);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetYesNoCancelLabels(const wxMD::ButtonLabel& yes,
|
||||
const wxMD::ButtonLabel& no,
|
||||
const wxMD::ButtonLabel& cancel)
|
||||
{
|
||||
DoSetCustomLabel(m_yes, yes);
|
||||
DoSetCustomLabel(m_no, no);
|
||||
DoSetCustomLabel(m_cancel, cancel);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetOKLabel(const wxMD::ButtonLabel& ok)
|
||||
{
|
||||
DoSetCustomLabel(m_ok, ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetOKCancelLabels(const wxMD::ButtonLabel& ok,
|
||||
const wxMD::ButtonLabel& cancel)
|
||||
{
|
||||
DoSetCustomLabel(m_ok, ok);
|
||||
DoSetCustomLabel(m_cancel, cancel);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool SetHelpLabel(const wxMD::ButtonLabel& help)
|
||||
{
|
||||
DoSetCustomLabel(m_help, help);
|
||||
return true;
|
||||
}
|
||||
// test if any custom labels were set
|
||||
bool HasCustomLabels() const
|
||||
{
|
||||
return !(m_ok.empty() && m_cancel.empty() && m_help.empty() &&
|
||||
m_yes.empty() && m_no.empty());
|
||||
}
|
||||
|
||||
// these functions return the label to be used for the button which is
|
||||
// either a custom label explicitly set by the user or the default label,
|
||||
// i.e. they always return a valid string
|
||||
wxString GetYesLabel() const
|
||||
{
|
||||
return m_yes.empty() ? GetDefaultYesLabel() : m_yes;
|
||||
}
|
||||
wxString GetNoLabel() const
|
||||
{
|
||||
return m_no.empty() ? GetDefaultNoLabel() : m_no;
|
||||
}
|
||||
wxString GetOKLabel() const
|
||||
{
|
||||
return m_ok.empty() ? GetDefaultOKLabel() : m_ok;
|
||||
}
|
||||
wxString GetCancelLabel() const
|
||||
{
|
||||
return m_cancel.empty() ? GetDefaultCancelLabel() : m_cancel;
|
||||
}
|
||||
wxString GetHelpLabel() const
|
||||
{
|
||||
return m_help.empty() ? GetDefaultHelpLabel() : m_help;
|
||||
}
|
||||
|
||||
protected:
|
||||
// this function is called by our public SetXXXLabels() and should assign
|
||||
// the value to var with possibly some transformation (e.g. Cocoa version
|
||||
// currently uses this to remove any accelerators from the button strings
|
||||
// while GTK+ one handles stock items specifically here)
|
||||
void DoSetCustomLabel(wxString& var, const wxMD::ButtonLabel& label)
|
||||
{
|
||||
var = label.GetAsString();
|
||||
}
|
||||
|
||||
// these functions return the custom label or empty string and should be
|
||||
// used only in specific circumstances such as creating the buttons with
|
||||
// these labels (in which case it makes sense to only use a custom label if
|
||||
// it was really given and fall back on stock label otherwise), use the
|
||||
// Get{Yes,No,OK,Cancel}Label() methods above otherwise
|
||||
const wxString& GetCustomYesLabel() const { return m_yes; }
|
||||
const wxString& GetCustomNoLabel() const { return m_no; }
|
||||
const wxString& GetCustomOKLabel() const { return m_ok; }
|
||||
const wxString& GetCustomHelpLabel() const { return m_help; }
|
||||
const wxString& GetCustomCancelLabel() const { return m_cancel; }
|
||||
|
||||
private:
|
||||
// these functions may be overridden to provide different defaults for the
|
||||
// default button labels (this is used by wxGTK)
|
||||
virtual wxString GetDefaultYesLabel() const { return wxGetTranslation("Yes"); }
|
||||
virtual wxString GetDefaultNoLabel() const { return wxGetTranslation("No"); }
|
||||
virtual wxString GetDefaultOKLabel() const { return wxGetTranslation("OK"); }
|
||||
virtual wxString GetDefaultCancelLabel() const { return wxGetTranslation("Cancel"); }
|
||||
virtual wxString GetDefaultHelpLabel() const { return wxGetTranslation("Help"); }
|
||||
|
||||
// labels for the buttons, initially empty meaning that the defaults should
|
||||
// be used, use GetYes/No/OK/CancelLabel() to access them
|
||||
wxString m_yes,
|
||||
m_no,
|
||||
m_ok,
|
||||
m_cancel,
|
||||
m_help;
|
||||
};
|
||||
#else
|
||||
// just a wrapper for wxStaticLine to use the same code on all platforms
|
||||
class StaticLine : public wxStaticLine
|
||||
{
|
||||
public:
|
||||
StaticLine(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxLI_HORIZONTAL,
|
||||
const wxString& name = wxString::FromAscii(wxStaticLineNameStr))
|
||||
: wxStaticLine(parent, id, pos, size, style, name) {}
|
||||
~StaticLine() {}
|
||||
};
|
||||
// just a wrapper to wxMessageBox to use the same code on all platforms
|
||||
class MessageDialog : public wxMessageDialog
|
||||
{
|
||||
@ -113,25 +283,19 @@ public:
|
||||
: wxMessageDialog(parent, message, caption, style) {}
|
||||
~MessageDialog() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
class MessageWithCheckDialog : public MsgDialog
|
||||
// just a wrapper to wxRichMessageBox to use the same code on all platforms
|
||||
class RichMessageDialog : public wxRichMessageDialog
|
||||
{
|
||||
wxCheckBox* m_check{ nullptr };
|
||||
public:
|
||||
MessageWithCheckDialog(wxWindow* parent,
|
||||
RichMessageDialog(wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& checkbox_label,
|
||||
const wxString& caption = wxEmptyString,
|
||||
long style = wxOK);
|
||||
MessageWithCheckDialog(MessageWithCheckDialog&&) = delete;
|
||||
MessageWithCheckDialog(const MessageWithCheckDialog&) = delete;
|
||||
MessageWithCheckDialog& operator=(MessageWithCheckDialog&&) = delete;
|
||||
MessageWithCheckDialog& operator=(const MessageWithCheckDialog&) = delete;
|
||||
virtual ~MessageWithCheckDialog() = default;
|
||||
|
||||
bool GetCheckVal();
|
||||
long style = wxOK)
|
||||
: wxRichMessageDialog(parent, message, caption, style) {}
|
||||
~RichMessageDialog() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Generic info dialog, used for displaying exceptions
|
||||
class InfoDialog : public MsgDialog
|
||||
|
@ -1090,6 +1090,8 @@ void Sidebar::msw_rescale()
|
||||
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
|
||||
|
||||
p->scrolled->Layout();
|
||||
|
||||
p->searcher.dlg_msw_rescale();
|
||||
}
|
||||
|
||||
void Sidebar::sys_color_changed()
|
||||
@ -1128,6 +1130,8 @@ void Sidebar::sys_color_changed()
|
||||
p->btn_export_gcode_removable->msw_rescale();
|
||||
|
||||
p->scrolled->Layout();
|
||||
|
||||
p->searcher.dlg_sys_color_changed();
|
||||
}
|
||||
|
||||
void Sidebar::search()
|
||||
@ -1924,7 +1928,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
|
||||
@ -2486,15 +2490,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
model.convert_from_meters(true);
|
||||
};
|
||||
if (answer_convert_from_meters == wxOK_DEFAULT) {
|
||||
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
"The dimensions of the object from file %s seem to be defined in meters.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
|
||||
"The dimensions of some objects from file %s seem to be defined in meters.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
|
||||
_L("Apply to all the remaining small objects being loaded."),
|
||||
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
|
||||
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||
int answer = dlg.ShowModal();
|
||||
if (dlg.GetCheckVal())
|
||||
if (dlg.IsCheckBoxChecked())
|
||||
answer_convert_from_meters = answer;
|
||||
else
|
||||
convert_model_if(model, answer == wxID_YES);
|
||||
@ -2508,15 +2512,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
convert_from_imperial_units(model, true);
|
||||
};
|
||||
if (answer_convert_from_imperial_units == wxOK_DEFAULT) {
|
||||
MessageWithCheckDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
RichMessageDialog dlg(q, format_wxstr(_L_PLURAL(
|
||||
"The dimensions of the object from file %s seem to be defined in inches.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
|
||||
"The dimensions of some objects from file %s seem to be defined in inches.\n"
|
||||
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
|
||||
_L("Apply to all the remaining small objects being loaded."),
|
||||
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
|
||||
dlg.ShowCheckBox(_L("Apply to all the remaining small objects being loaded."));
|
||||
int answer = dlg.ShowModal();
|
||||
if (dlg.GetCheckVal())
|
||||
if (dlg.IsCheckBoxChecked())
|
||||
answer_convert_from_imperial_units = answer;
|
||||
else
|
||||
convert_model_if(model, answer == wxID_YES);
|
||||
@ -6230,6 +6234,15 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_key == "material_colour") {
|
||||
update_scheduled = true; // update should be scheduled (for update 3DScene)
|
||||
|
||||
// update material color in full config
|
||||
std::vector<std::string> material_colors = { config.opt_string("material_colour", (unsigned)0) };
|
||||
p->config->option<ConfigOptionStrings>("material_colour")->values = material_colors;
|
||||
continue;
|
||||
}
|
||||
|
||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||
if (opt_key == "printer_technology") {
|
||||
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
||||
|
@ -350,8 +350,6 @@ void PreferencesDialog::build(size_t selected_tab)
|
||||
tabs->Layout();
|
||||
this->layout();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
def.label = L("Sequential slider applied only to top layer");
|
||||
@ -395,16 +393,6 @@ void PreferencesDialog::build(size_t selected_tab)
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
}
|
||||
def.label = L("Use Dark color mode (experimental)");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, UI will use Dark mode colors. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("dark_color_mode") == "1" });
|
||||
option = Option(def, "dark_color_mode");
|
||||
m_optgroup_gui->append_single_option_line(option);
|
||||
|
||||
if (is_editor) {
|
||||
def.label = L("Set settings tabs as menu items (experimental)");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, Settings Tabs will be placed as menu items. "
|
||||
@ -484,6 +472,36 @@ void PreferencesDialog::build(size_t selected_tab)
|
||||
}
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
#ifdef _WIN32
|
||||
// Add "Dark Mode" tab
|
||||
if (is_editor) {
|
||||
// Add "Dark Mode" tab
|
||||
m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs);
|
||||
m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
};
|
||||
|
||||
def.label = L("Enable dark mode");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, UI will use Dark mode colors. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("dark_color_mode") == "1" });
|
||||
option = Option(def, "dark_color_mode");
|
||||
m_optgroup_dark_mode->append_single_option_line(option);
|
||||
|
||||
def.label = L("Use system menu for application");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, application will use standart Windows system menu,\n"
|
||||
"but on some combination od display scales it can looks ugly. "
|
||||
"If disabled, old UI will be used.");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" });
|
||||
option = Option(def, "sys_menu_enabled");
|
||||
m_optgroup_dark_mode->append_single_option_line(option);
|
||||
|
||||
activate_options_tab(m_optgroup_dark_mode);
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
||||
// update alignment of the controls for all tabs
|
||||
update_ctrls_alignment();
|
||||
|
||||
@ -525,7 +543,7 @@ void PreferencesDialog::accept(wxEvent&)
|
||||
// if (m_values.find("no_defaults") != m_values.end()
|
||||
// warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
|
||||
|
||||
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu" };
|
||||
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
|
||||
|
||||
for (const std::string& option : options_to_recreate_GUI) {
|
||||
if (m_values.find(option) != m_values.end()) {
|
||||
@ -588,11 +606,14 @@ void PreferencesDialog::accept(wxEvent&)
|
||||
|
||||
EndModal(wxID_OK);
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
#ifdef _WIN32
|
||||
if (m_values.find("dark_color_mode") != m_values.end())
|
||||
wxGetApp().force_colors_update();
|
||||
#endif
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (m_values.find("sys_menu_enabled") != m_values.end())
|
||||
wxGetApp().force_menu_update();
|
||||
#endif //_MSW_DARK_MODE
|
||||
#endif // _WIN32
|
||||
if (m_settings_layout_changed)
|
||||
;// application will be recreated after Preference dialog will be destroyed
|
||||
else
|
||||
|
@ -29,6 +29,9 @@ class PreferencesDialog : public DPIDialog
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_general;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui;
|
||||
#ifdef _WIN32
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_dark_mode;
|
||||
#endif //_WIN32
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
std::shared_ptr<ConfigOptionsGroup> m_optgroup_render;
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
@ -101,6 +101,8 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
||||
EndDialog(wxID_OK);
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
Fit();
|
||||
CenterOnParent();
|
||||
|
||||
@ -331,6 +333,14 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_sys_color_changed()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
wxGetApp().UpdateDVCDarkUI(job_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx)
|
||||
{
|
||||
wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list");
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
}
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
private:
|
||||
enum Column {
|
||||
|
@ -401,6 +401,18 @@ void OptionsSearcher::show_dialog()
|
||||
search_dialog->Popup();
|
||||
}
|
||||
|
||||
void OptionsSearcher::dlg_sys_color_changed()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->on_sys_color_changed();
|
||||
}
|
||||
|
||||
void OptionsSearcher::dlg_msw_rescale()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->msw_rescale();
|
||||
}
|
||||
|
||||
void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category)
|
||||
{
|
||||
groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category};
|
||||
@ -666,7 +678,7 @@ void SearchDialog::OnLeftDown(wxMouseEvent& event)
|
||||
ProcessSelection(search_list->GetSelection());
|
||||
}
|
||||
|
||||
void SearchDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
void SearchDialog::msw_rescale()
|
||||
{
|
||||
const int& em = em_unit();
|
||||
|
||||
|
@ -138,6 +138,8 @@ public:
|
||||
void sort_options_by_label() { sort_options(); }
|
||||
|
||||
void show_dialog();
|
||||
void dlg_sys_color_changed();
|
||||
void dlg_msw_rescale();
|
||||
};
|
||||
|
||||
|
||||
@ -180,9 +182,11 @@ public:
|
||||
void Popup(wxPoint position = wxDefaultPosition);
|
||||
void ProcessSelection(wxDataViewItem selection);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void msw_rescale();
|
||||
void on_sys_color_changed() override;
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override { msw_rescale(); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -4157,6 +4157,7 @@ void TabSLAMaterial::build()
|
||||
auto page = add_options_page(L("Material"), "resin");
|
||||
|
||||
auto optgroup = page->new_optgroup(L("Material"));
|
||||
optgroup->append_single_option_line("material_colour");
|
||||
optgroup->append_single_option_line("bottle_cost");
|
||||
optgroup->append_single_option_line("bottle_volume");
|
||||
optgroup->append_single_option_line("bottle_weight");
|
||||
@ -4164,6 +4165,12 @@ void TabSLAMaterial::build()
|
||||
|
||||
optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value)
|
||||
{
|
||||
if (opt_key == "material_colour") {
|
||||
update_dirty();
|
||||
on_value_change(opt_key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
|
||||
if (opt_key == "bottle_volume") {
|
||||
|