Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorenricoturri1966 <enricoturri@seznam.cz>2021-09-30 08:57:37 +0300
committerenricoturri1966 <enricoturri@seznam.cz>2021-09-30 08:57:37 +0300
commit064bc63b23984c580a8e9f38a562203c050b5665 (patch)
tree8f08bef5cce8ba8c4327bb5ada94c2b35a43e843
parent8109748e846f5338b04ac831eb61d4ad2581c624 (diff)
parent88c701888d193ae1c5fdf1d4d4da79bc1a50ac18 (diff)
Fixed conflicts after merge with masteret_layer_time_preview
-rw-r--r--lib/Slic3r/Test.pm1
-rw-r--r--resources/icons/exclamation_manifold.svg17
-rw-r--r--resources/icons/white/exclamation_manifold.svg17
-rw-r--r--resources/profiles/PrusaResearch.idx365
-rw-r--r--resources/profiles/PrusaResearch.ini178
-rw-r--r--resources/shaders/gouraud_light_instanced.vs4
-rw-r--r--sandboxes/aabb-evaluation/aabb-evaluation.cpp3
-rw-r--r--sandboxes/meshboolean/MeshBoolean.cpp1
-rw-r--r--sandboxes/opencsg/Engine.cpp5
-rw-r--r--sandboxes/opencsg/ShaderCSGDisplay.cpp9
-rw-r--r--src/PrusaSlicer.cpp2
-rw-r--r--src/admesh/connect.cpp68
-rw-r--r--src/admesh/stl.h35
-rw-r--r--src/admesh/stl_io.cpp11
-rw-r--r--src/libslic3r/AppConfig.cpp3
-rw-r--r--src/libslic3r/BlacklistedLibraryCheck.cpp2
-rw-r--r--src/libslic3r/CMakeLists.txt2
-rw-r--r--src/libslic3r/ClipperUtils.cpp53
-rw-r--r--src/libslic3r/ClipperUtils.hpp5
-rw-r--r--src/libslic3r/Config.cpp6
-rw-r--r--src/libslic3r/Config.hpp4
-rw-r--r--src/libslic3r/ExPolygon.cpp62
-rw-r--r--src/libslic3r/ExPolygon.hpp20
-rw-r--r--src/libslic3r/Fill/FillAdaptive.cpp4
-rw-r--r--src/libslic3r/Format/3mf.cpp121
-rw-r--r--src/libslic3r/Format/AMF.cpp58
-rw-r--r--src/libslic3r/Format/OBJ.cpp120
-rw-r--r--src/libslic3r/Format/PRUS.cpp333
-rw-r--r--src/libslic3r/Format/PRUS.hpp11
-rw-r--r--src/libslic3r/Format/SL1.cpp24
-rw-r--r--src/libslic3r/Format/SL1.hpp4
-rw-r--r--src/libslic3r/Format/STL.cpp3
-rw-r--r--src/libslic3r/GCode.cpp7
-rw-r--r--src/libslic3r/GCode/GCodeProcessor.cpp256
-rw-r--r--src/libslic3r/GCode/GCodeProcessor.hpp18
-rw-r--r--src/libslic3r/GCodeReader.cpp96
-rw-r--r--src/libslic3r/GCodeReader.hpp14
-rw-r--r--src/libslic3r/GCodeWriter.cpp223
-rw-r--r--src/libslic3r/GCodeWriter.hpp79
-rw-r--r--src/libslic3r/LocalesUtils.cpp45
-rw-r--r--src/libslic3r/LocalesUtils.hpp3
-rw-r--r--src/libslic3r/MeshBoolean.cpp36
-rw-r--r--src/libslic3r/MeshSplitImpl.hpp155
-rw-r--r--src/libslic3r/Model.cpp159
-rw-r--r--src/libslic3r/Model.hpp14
-rw-r--r--src/libslic3r/MultiPoint.hpp4
-rw-r--r--src/libslic3r/Point.cpp9
-rw-r--r--src/libslic3r/Point.hpp34
-rw-r--r--src/libslic3r/Polygon.cpp21
-rw-r--r--src/libslic3r/Polygon.hpp39
-rw-r--r--src/libslic3r/Polyline.cpp14
-rw-r--r--src/libslic3r/Polyline.hpp6
-rw-r--r--src/libslic3r/Preset.cpp8
-rw-r--r--src/libslic3r/PresetBundle.hpp2
-rw-r--r--src/libslic3r/Print.cpp41
-rw-r--r--src/libslic3r/PrintApply.cpp6
-rw-r--r--src/libslic3r/PrintBase.cpp1
-rw-r--r--src/libslic3r/PrintObject.cpp2
-rw-r--r--src/libslic3r/PrintObjectSlice.cpp19
-rw-r--r--src/libslic3r/SLA/Hollowing.cpp6
-rw-r--r--src/libslic3r/SLA/Pad.cpp2
-rw-r--r--src/libslic3r/SLA/ReprojectPointsOnMesh.hpp1
-rw-r--r--src/libslic3r/SLA/Rotfinder.cpp3
-rw-r--r--src/libslic3r/SLAPrint.cpp17
-rw-r--r--src/libslic3r/SLAPrint.hpp9
-rw-r--r--src/libslic3r/SLAPrintSteps.cpp1
-rw-r--r--src/libslic3r/SimplifyMesh.hpp4
-rw-r--r--src/libslic3r/SlicingAdaptive.cpp20
-rw-r--r--src/libslic3r/SupportMaterial.cpp26
-rw-r--r--src/libslic3r/Technologies.hpp25
-rw-r--r--src/libslic3r/Thread.cpp18
-rw-r--r--src/libslic3r/Thread.hpp3
-rw-r--r--src/libslic3r/TriangleMesh.cpp877
-rw-r--r--src/libslic3r/TriangleMesh.hpp167
-rw-r--r--src/libslic3r/TriangleMeshSlicer.cpp74
-rw-r--r--src/libslic3r/TriangleMeshSlicer.hpp16
-rw-r--r--src/libslic3r/TriangleSelector.cpp59
-rw-r--r--src/libslic3r/TriangleSelector.hpp4
-rw-r--r--src/libslic3r/Utils.hpp13
-rw-r--r--src/libslic3r/utils.cpp3
-rw-r--r--src/platform/msw/PrusaSlicer-gcodeviewer.rc.in2
-rw-r--r--src/platform/msw/PrusaSlicer.rc.in2
-rw-r--r--src/platform/osx/Info.plist.in2
-rw-r--r--src/slic3r/CMakeLists.txt2
-rw-r--r--src/slic3r/GUI/2DBed.cpp2
-rw-r--r--src/slic3r/GUI/3DScene.cpp143
-rw-r--r--src/slic3r/GUI/3DScene.hpp23
-rw-r--r--src/slic3r/GUI/AboutDialog.cpp2
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.cpp4
-rw-r--r--src/slic3r/GUI/ConfigWizard.cpp84
-rw-r--r--src/slic3r/GUI/ConfigWizard_private.hpp2
-rw-r--r--src/slic3r/GUI/DoubleSlider.cpp9
-rw-r--r--src/slic3r/GUI/ExtraRenderers.cpp18
-rw-r--r--src/slic3r/GUI/Field.cpp2
-rw-r--r--src/slic3r/GUI/GCodeViewer.cpp310
-rw-r--r--src/slic3r/GUI/GCodeViewer.hpp29
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp68
-rw-r--r--src/slic3r/GUI/GLCanvas3D.hpp12
-rw-r--r--src/slic3r/GUI/GLModel.cpp35
-rw-r--r--src/slic3r/GUI/GLModel.hpp9
-rw-r--r--src/slic3r/GUI/GLToolbar.cpp14
-rw-r--r--src/slic3r/GUI/GUI.cpp3
-rw-r--r--src/slic3r/GUI/GUI_App.cpp185
-rw-r--r--src/slic3r/GUI/GUI_App.hpp10
-rw-r--r--src/slic3r/GUI/GUI_Factories.cpp48
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp209
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.hpp12
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.cpp10
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.hpp2
-rw-r--r--src/slic3r/GUI/GUI_Preview.cpp41
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoBase.cpp12
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoCut.cpp12
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoCut.hpp6
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp9
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp26
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp2
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp8
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMove.cpp2
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp29
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp5
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp3
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp7
-rw-r--r--src/slic3r/GUI/InstanceCheck.cpp10
-rw-r--r--src/slic3r/GUI/Jobs/ArrangeJob.hpp17
-rw-r--r--src/slic3r/GUI/Jobs/FillBedJob.hpp5
-rw-r--r--src/slic3r/GUI/Jobs/Job.cpp48
-rw-r--r--src/slic3r/GUI/Jobs/Job.hpp7
-rw-r--r--src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp33
-rw-r--r--src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp26
-rw-r--r--src/slic3r/GUI/Jobs/PlaterJob.hpp5
-rw-r--r--src/slic3r/GUI/Jobs/RotoptimizeJob.hpp6
-rw-r--r--src/slic3r/GUI/Jobs/SLAImportJob.cpp85
-rw-r--r--src/slic3r/GUI/Jobs/SLAImportJob.hpp8
-rw-r--r--src/slic3r/GUI/MainFrame.cpp32
-rw-r--r--src/slic3r/GUI/MainFrame.hpp2
-rw-r--r--src/slic3r/GUI/MeshUtils.cpp16
-rw-r--r--src/slic3r/GUI/MeshUtils.hpp19
-rw-r--r--src/slic3r/GUI/Notebook.hpp99
-rw-r--r--src/slic3r/GUI/NotificationManager.cpp133
-rw-r--r--src/slic3r/GUI/NotificationManager.hpp30
-rw-r--r--src/slic3r/GUI/ObjectDataViewModel.cpp71
-rw-r--r--src/slic3r/GUI/ObjectDataViewModel.hpp19
-rw-r--r--src/slic3r/GUI/PhysicalPrinterDialog.cpp4
-rw-r--r--src/slic3r/GUI/Plater.cpp442
-rw-r--r--src/slic3r/GUI/Plater.hpp17
-rw-r--r--src/slic3r/GUI/Preferences.cpp19
-rw-r--r--src/slic3r/GUI/ProjectDirtyStateManager.cpp6
-rw-r--r--src/slic3r/GUI/RemovableDriveManager.cpp6
-rw-r--r--src/slic3r/GUI/Search.cpp37
-rw-r--r--src/slic3r/GUI/Search.hpp4
-rw-r--r--src/slic3r/GUI/Selection.cpp123
-rw-r--r--src/slic3r/GUI/Selection.hpp22
-rw-r--r--src/slic3r/GUI/Tab.cpp36
-rw-r--r--src/slic3r/GUI/Tab.hpp2
-rw-r--r--src/slic3r/GUI/UnsavedChangesDialog.cpp151
-rw-r--r--src/slic3r/GUI/UnsavedChangesDialog.hpp24
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp7
-rw-r--r--src/slic3r/Utils/FixModelByWin10.cpp23
-rw-r--r--src/slic3r/Utils/FixModelByWin10.hpp8
-rw-r--r--src/slic3r/Utils/PresetUpdater.cpp22
-rw-r--r--src/slic3r/Utils/UndoRedo.cpp47
-rw-r--r--src/slic3r/Utils/UndoRedo.hpp25
-rw-r--r--tests/fff_print/test_data.cpp61
-rw-r--r--tests/fff_print/test_trianglemesh.cpp138
-rw-r--r--tests/libslic3r/test_3mf.cpp3
-rw-r--r--tests/libslic3r/test_aabbindirect.cpp1
-rw-r--r--tests/libslic3r/test_clipper_utils.cpp4
-rw-r--r--tests/libslic3r/test_config.cpp35
-rw-r--r--tests/libslic3r/test_hollowing.cpp1
-rw-r--r--tests/libslic3r/test_marchingsquares.cpp1
-rw-r--r--tests/sla_print/sla_print_tests.cpp1
-rw-r--r--tests/sla_print/sla_raycast_tests.cpp1
-rw-r--r--tests/sla_print/sla_supptgen_tests.cpp5
-rw-r--r--tests/sla_print/sla_test_utils.cpp60
-rw-r--r--tests/sla_print/sla_test_utils.hpp5
-rw-r--r--version.inc2
-rw-r--r--xs/t/01_trianglemesh.t110
-rw-r--r--xs/xsp/Model.xsp11
-rw-r--r--xs/xsp/TriangleMesh.xsp138
-rw-r--r--xs/xsp/my.map1
-rw-r--r--xs/xsp/typemap.xspt1
181 files changed, 4334 insertions, 3566 deletions
diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm
index 8d2a79f2a..d0696aab0 100644
--- a/lib/Slic3r/Test.pm
+++ b/lib/Slic3r/Test.pm
@@ -139,7 +139,6 @@ sub mesh {
my $mesh = Slic3r::TriangleMesh->new;
$mesh->ReadFromPerl($vertices, $facets);
- $mesh->repair;
$mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz};
$mesh->translate(@{$params{translate}}) if $params{translate};
return $mesh;
diff --git a/resources/icons/exclamation_manifold.svg b/resources/icons/exclamation_manifold.svg
new file mode 100644
index 000000000..cd8ba5954
--- /dev/null
+++ b/resources/icons/exclamation_manifold.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#808080" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
+ </g>
+ <g>
+ <circle fill="#808080" cx="8" cy="13" r="1"/>
+ </g>
+ <g>
+ <path fill="#808080" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
+ l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
+ </g>
+</g>
+</svg>
diff --git a/resources/icons/white/exclamation_manifold.svg b/resources/icons/white/exclamation_manifold.svg
new file mode 100644
index 000000000..a18590167
--- /dev/null
+++ b/resources/icons/white/exclamation_manifold.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#FFFFFF" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
+ </g>
+ <g>
+ <circle fill="#FFFFFF" cx="8" cy="13" r="1"/>
+ </g>
+ <g>
+ <path fill="#FFFFFF" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
+ l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
+ </g>
+</g>
+</svg>
diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index 8a34a9a11..ee3a57fe8 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,181 +1,184 @@
-min_slic3r_version = 2.4.0-alpha0
-1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles.
-1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
-1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
-1.4.0-alpha4 Decreased Area Fill (SL1S).
-1.4.0-alpha3 Updated SL1S tilt times.
-1.4.0-alpha2 Updated Prusa MINI machine limits.
-1.4.0-alpha1 Added new SL1S resin profiles.
-1.4.0-alpha0 Bumped up config version.
-1.3.0-alpha2 Added SL1S SPEED profiles.
-1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values.
-1.3.0-alpha0 Disabled thick bridges, updated support settings.
-min_slic3r_version = 2.3.2-alpha0
-1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
-1.3.0 Added SL1S SPEED profiles.
-min_slic3r_version = 2.3.0-rc1
-1.2.8 Added multiple add:north and Extrudr filament profiles.
-1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI.
-1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber".
-1.2.5 Updated firmware version. Added filament profiles. Various improvements.
-1.2.4 Updated cost/density values in filament settings. Various changes in print settings.
-1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles.
-1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles.
-1.2.1 Updated FW version for MK2.5 family printers.
-1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version.
-min_slic3r_version = 2.3.0-beta2
-1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values.
-min_slic3r_version = 2.3.0-beta0
-1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights.
-min_slic3r_version = 2.3.0-alpha4
-1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
-1.2.0-alpha0 Added filament spool weights
-min_slic3r_version = 2.2.0-alpha3
-1.1.14 Updated firmware version.
-1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles.
-1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles.
-1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
-1.1.10 Updated firmware version.
-1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials.
-1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles.
-1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles.
-1.1.6 Updated firmware version for MK2.5/S and MK3/S.
-1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend
-1.1.4 Added Prusament PC Blend filament profile.
-1.1.3 Added SLA material and filament profile
-1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET.
-1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS.
-1.1.1-beta Updated for PrusaSlicer 2.2.0-beta
-1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc
-1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
-# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
-# so they will see the print bed.
-max_slic3r_version = 2.2.0-alpha2
-min_slic3r_version = 2.2.0-alpha0
-1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
-1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
-min_slic3r_version = 2.1.1-beta0
-1.0.12 Updated firmware version.
-1.0.11 Updated firmware version.
-1.0.10 Updated firmware version for MK2.5/S and MK3/S.
-1.0.9 Updated firmware version for MK2.5/S and MK3/S.
-1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog.
-1.0.7 Updated layer height limits for MINI
-1.0.6 Added Prusa MINI profiles
-min_slic3r_version = 2.1.0-alpha0
-1.0.5 Added SLA materials
-1.0.4 Updated firmware version and 0.25mm nozzle profiles
-1.0.3 Added filament profiles
-1.0.2 Added SLA materials
-1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers.
-1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports.
-1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament.
-1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times
-1.0.0-alpha2 Printer model and nozzle diameter check
-1.0.0-alpha1 Added Prusament ASA profile
-1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX
-min_slic3r_version = 1.42.0-alpha6
-0.8.11 Updated firmware version.
-0.8.10 Updated firmware version.
-0.8.9 Updated firmware version for MK2.5/S and MK3/S.
-0.8.8 Updated firmware version for MK2.5/S and MK3/S.
-0.8.7 Updated firmware version
-0.8.6 Updated firmware version for MK2.5/S and MK3/S
-0.8.5 Updated SL1 printer and material settings
-0.8.4 Added Prusament ASA profile
-0.8.3 FW version and SL1 materials update
-0.8.2 FFF and SL1 settings update
-0.8.1 Output settings and SLA materials update
-0.8.0 Updated for the PrusaSlicer 2.0.0 final release
-0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S
-0.8.0-rc1 Updated SLA profiles
-0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release
-0.8.0-beta4 Updated SLA profiles
-0.8.0-beta3 Updated SLA profiles
-0.8.0-beta2 Updated SLA profiles
-0.8.0-beta1 Updated SLA profiles
-0.8.0-beta Updated SLA profiles
-0.8.0-alpha9 Updated SLA and FFF profiles
-0.8.0-alpha8 Updated SLA profiles
-0.8.0-alpha7 Updated SLA profiles
-0.8.0-alpha6 Updated SLA profiles
-min_slic3r_version = 1.42.0-alpha
-0.8.0-alpha Updated SLA profiles
-0.4.0-alpha4 Updated SLA profiles
-0.4.0-alpha3 Update of SLA profiles
-0.4.0-alpha2 First SLA profiles
-min_slic3r_version = 1.41.3-alpha
-0.4.12 Updated firmware version for MK2.5/S and MK3/S.
-0.4.11 Updated firmware version for MK2.5/S and MK3/S.
-0.4.10 Updated firmware version
-0.4.9 Updated firmware version for MK2.5/S and MK3/S
-0.4.8 MK2.5/3/S FW update
-0.4.7 MK2/S/MMU FW update
-0.4.6 Updated firmware versions for MK2.5/S and MK3/S
-0.4.5 Enabled remaining time support for MK2/S/MMU1
-0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.1 New MK2.5S and MK3S FW versions
-0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-min_slic3r_version = 1.41.1
-0.3.11 Updated firmware version for MK2.5/S and MK3/S.
-0.3.10 Updated firmware version
-0.3.9 Updated firmware version for MK2.5/S and MK3/S
-0.3.8 MK2.5/3/S FW update
-0.3.7 MK2/S/MMU FW update
-0.3.6 Updated firmware versions for MK2.5 and MK3
-0.3.5 New MK2.5 and MK3 FW versions
-0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.3.3 Prusament PETG released
-0.3.2 New MK2.5 and MK3 FW versions
-0.3.1 New MK2.5 and MK3 FW versions
-0.3.0 New MK2.5 and MK3 FW version
-min_slic3r_version = 1.41.0-alpha
-0.2.9 New MK2.5 and MK3 FW versions
-0.2.8 New MK2.5 and MK3 FW version
-min_slic3r_version = 1.41.1
-0.2.7 New MK2.5 and MK3 FW version
-0.2.6 Added MMU2 MK2.5 settings
-min_slic3r_version = 1.41.0-alpha
-0.2.5 Prusament is out - added prusament settings
-0.2.4 Added soluble support profiles for MMU2
-0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
-0.2.2 Edited MMU2 Single mode purge line
-0.2.1 Added PET and BVOH settings for MMU2
-0.2.0-beta5 Fixed MMU1 ramming parameters
-0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
-0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
-0.2.0-beta2 Edited first layer speed and wipe tower position
-0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
-0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
-0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references
-0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
-0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
-0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
-0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
-0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material
-0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
-0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
-min_slic3r_version = 1.40.0
-0.1.18 Updated firmware version
-0.1.17 Updated firmware version for MK2.5/S and MK3/S
-0.1.16 MK2.5/3/S FW update
-0.1.15 MK2/S/MMU FW update
-0.1.14 Updated firmware versions for MK2.5 and MK3
-0.1.13 New MK2.5 and MK3 FW versions
-0.1.12 New MK2.5 and MK3 FW versions
-0.1.11 fw version changed to 3.3.1
-0.1.10 MK3 jerk and acceleration update
-0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
-0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes
-0.1.7 Fixed errors in 0.25mm and 0.6mm profiles
-0.1.6 Split the MK2.5 profile from the MK2S
-min_slic3r_version = 1.40.0-beta
-0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles
-0.1.4 edited fw version, added z-raise after print
-min_slic3r_version = 1.40.0-alpha
-0.1.3 Fixed an incorrect position of the max_print_height parameter
-0.1.2 Wipe tower changes
-0.1.1 Minor print speed adjustments
-0.1.0 Initial
+min_slic3r_version = 2.4.0-alpha0
+1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default.
+1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles.
+1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
+1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
+1.4.0-alpha4 Decreased Area Fill (SL1S).
+1.4.0-alpha3 Updated SL1S tilt times.
+1.4.0-alpha2 Updated Prusa MINI machine limits.
+1.4.0-alpha1 Added new SL1S resin profiles.
+1.4.0-alpha0 Bumped up config version.
+1.3.0-alpha2 Added SL1S SPEED profiles.
+1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values.
+1.3.0-alpha0 Disabled thick bridges, updated support settings.
+min_slic3r_version = 2.3.2-alpha0
+1.3.2 Added material profiles for Prusament Resin.
+1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
+1.3.0 Added SL1S SPEED profiles.
+min_slic3r_version = 2.3.0-rc1
+1.2.9 Added material profiles for Prusament Resin.
+1.2.8 Added multiple add:north and Extrudr filament profiles.
+1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI.
+1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber".
+1.2.5 Updated firmware version. Added filament profiles. Various improvements.
+1.2.4 Updated cost/density values in filament settings. Various changes in print settings.
+1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles.
+1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles.
+1.2.1 Updated FW version for MK2.5 family printers.
+1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version.
+min_slic3r_version = 2.3.0-beta2
+1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values.
+min_slic3r_version = 2.3.0-beta0
+1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights.
+min_slic3r_version = 2.3.0-alpha4
+1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
+1.2.0-alpha0 Added filament spool weights
+min_slic3r_version = 2.2.0-alpha3
+1.1.14 Updated firmware version.
+1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles.
+1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles.
+1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
+1.1.10 Updated firmware version.
+1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials.
+1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles.
+1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles.
+1.1.6 Updated firmware version for MK2.5/S and MK3/S.
+1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend
+1.1.4 Added Prusament PC Blend filament profile.
+1.1.3 Added SLA material and filament profile
+1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET.
+1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS.
+1.1.1-beta Updated for PrusaSlicer 2.2.0-beta
+1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc
+1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
+# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
+# so they will see the print bed.
+max_slic3r_version = 2.2.0-alpha2
+min_slic3r_version = 2.2.0-alpha0
+1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
+1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
+min_slic3r_version = 2.1.1-beta0
+1.0.12 Updated firmware version.
+1.0.11 Updated firmware version.
+1.0.10 Updated firmware version for MK2.5/S and MK3/S.
+1.0.9 Updated firmware version for MK2.5/S and MK3/S.
+1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog.
+1.0.7 Updated layer height limits for MINI
+1.0.6 Added Prusa MINI profiles
+min_slic3r_version = 2.1.0-alpha0
+1.0.5 Added SLA materials
+1.0.4 Updated firmware version and 0.25mm nozzle profiles
+1.0.3 Added filament profiles
+1.0.2 Added SLA materials
+1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers.
+1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports.
+1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament.
+1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times
+1.0.0-alpha2 Printer model and nozzle diameter check
+1.0.0-alpha1 Added Prusament ASA profile
+1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX
+min_slic3r_version = 1.42.0-alpha6
+0.8.11 Updated firmware version.
+0.8.10 Updated firmware version.
+0.8.9 Updated firmware version for MK2.5/S and MK3/S.
+0.8.8 Updated firmware version for MK2.5/S and MK3/S.
+0.8.7 Updated firmware version
+0.8.6 Updated firmware version for MK2.5/S and MK3/S
+0.8.5 Updated SL1 printer and material settings
+0.8.4 Added Prusament ASA profile
+0.8.3 FW version and SL1 materials update
+0.8.2 FFF and SL1 settings update
+0.8.1 Output settings and SLA materials update
+0.8.0 Updated for the PrusaSlicer 2.0.0 final release
+0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S
+0.8.0-rc1 Updated SLA profiles
+0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release
+0.8.0-beta4 Updated SLA profiles
+0.8.0-beta3 Updated SLA profiles
+0.8.0-beta2 Updated SLA profiles
+0.8.0-beta1 Updated SLA profiles
+0.8.0-beta Updated SLA profiles
+0.8.0-alpha9 Updated SLA and FFF profiles
+0.8.0-alpha8 Updated SLA profiles
+0.8.0-alpha7 Updated SLA profiles
+0.8.0-alpha6 Updated SLA profiles
+min_slic3r_version = 1.42.0-alpha
+0.8.0-alpha Updated SLA profiles
+0.4.0-alpha4 Updated SLA profiles
+0.4.0-alpha3 Update of SLA profiles
+0.4.0-alpha2 First SLA profiles
+min_slic3r_version = 1.41.3-alpha
+0.4.12 Updated firmware version for MK2.5/S and MK3/S.
+0.4.11 Updated firmware version for MK2.5/S and MK3/S.
+0.4.10 Updated firmware version
+0.4.9 Updated firmware version for MK2.5/S and MK3/S
+0.4.8 MK2.5/3/S FW update
+0.4.7 MK2/S/MMU FW update
+0.4.6 Updated firmware versions for MK2.5/S and MK3/S
+0.4.5 Enabled remaining time support for MK2/S/MMU1
+0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.1 New MK2.5S and MK3S FW versions
+0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+min_slic3r_version = 1.41.1
+0.3.11 Updated firmware version for MK2.5/S and MK3/S.
+0.3.10 Updated firmware version
+0.3.9 Updated firmware version for MK2.5/S and MK3/S
+0.3.8 MK2.5/3/S FW update
+0.3.7 MK2/S/MMU FW update
+0.3.6 Updated firmware versions for MK2.5 and MK3
+0.3.5 New MK2.5 and MK3 FW versions
+0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.3.3 Prusament PETG released
+0.3.2 New MK2.5 and MK3 FW versions
+0.3.1 New MK2.5 and MK3 FW versions
+0.3.0 New MK2.5 and MK3 FW version
+min_slic3r_version = 1.41.0-alpha
+0.2.9 New MK2.5 and MK3 FW versions
+0.2.8 New MK2.5 and MK3 FW version
+min_slic3r_version = 1.41.1
+0.2.7 New MK2.5 and MK3 FW version
+0.2.6 Added MMU2 MK2.5 settings
+min_slic3r_version = 1.41.0-alpha
+0.2.5 Prusament is out - added prusament settings
+0.2.4 Added soluble support profiles for MMU2
+0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
+0.2.2 Edited MMU2 Single mode purge line
+0.2.1 Added PET and BVOH settings for MMU2
+0.2.0-beta5 Fixed MMU1 ramming parameters
+0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
+0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
+0.2.0-beta2 Edited first layer speed and wipe tower position
+0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
+0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
+0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references
+0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
+0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
+0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
+0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
+0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material
+0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
+0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
+min_slic3r_version = 1.40.0
+0.1.18 Updated firmware version
+0.1.17 Updated firmware version for MK2.5/S and MK3/S
+0.1.16 MK2.5/3/S FW update
+0.1.15 MK2/S/MMU FW update
+0.1.14 Updated firmware versions for MK2.5 and MK3
+0.1.13 New MK2.5 and MK3 FW versions
+0.1.12 New MK2.5 and MK3 FW versions
+0.1.11 fw version changed to 3.3.1
+0.1.10 MK3 jerk and acceleration update
+0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
+0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes
+0.1.7 Fixed errors in 0.25mm and 0.6mm profiles
+0.1.6 Split the MK2.5 profile from the MK2S
+min_slic3r_version = 1.40.0-beta
+0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles
+0.1.4 edited fw version, added z-raise after print
+min_slic3r_version = 1.40.0-alpha
+0.1.3 Fixed an incorrect position of the max_print_height parameter
+0.1.2 Wipe tower changes
+0.1.1 Minor print speed adjustments
+0.1.0 Initial
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 47a3883b4..7d4ab7ccb 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 1.4.0-alpha7
+config_version = 1.4.0-alpha8
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@@ -120,7 +120,7 @@ technology = SLA
family = SL1
bed_model = sl1_bed.stl
bed_texture = sl1.svg
-default_materials = Prusa Orange Tough @0.05
+default_materials = Prusa Orange Tough @0.05; Prusament Resin Tough Prusa Orange @0.05
[printer_model:SL1S]
name = Original Prusa SL1S SPEED
@@ -129,7 +129,7 @@ technology = SLA
family = SL1
bed_model = sl1s_bed.stl
bed_texture = sl1s.svg
-default_materials = Prusa Orange Tough @0.05 SL1S
+default_materials = Prusa Orange Tough @0.05 SL1S; Prusament Resin Tough Prusa Orange @0.05 SL1S
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@@ -181,7 +181,7 @@ max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0
min_skirt_length = 4
notes =
-overhangs = 0
+overhangs = 1
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
@@ -395,6 +395,7 @@ top_solid_min_thickness = 1.2
bottom_solid_min_thickness = 0.8
single_extruder_multi_material_priming = 0
thick_bridges = 1
+overhangs = 0
[print:*soluble_support*]
overhangs = 1
@@ -460,10 +461,10 @@ bridge_flow_ratio = 1
bridge_speed = 20
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
layer_height = 0.1
-perimeter_acceleration = 800
+perimeter_acceleration = 600
top_solid_layers = 9
support_material_contact_distance = 0.17
-raft_contact_distance = 0.17
+raft_contact_distance = 0.15
[print:*0.15mm*]
inherits = *common*
@@ -619,6 +620,7 @@ support_material_contact_distance = 0.1
raft_contact_distance = 0.2
top_solid_infill_speed = 40
thick_bridges = 1
+overhangs = 0
## MMU1 specific
[print:0.15mm OPTIMAL SOLUBLE FULL]
@@ -704,7 +706,7 @@ small_perimeter_speed = 15
solid_infill_speed = 40
top_solid_infill_speed = 30
support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
## MK2 - 0.6mm nozzle
@@ -772,6 +774,7 @@ single_extruder_multi_material_priming = 0
inherits = 0.35mm FAST
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
single_extruder_multi_material_priming = 0
+overhangs = 0
## MK2.5 - MMU2 specific
@@ -1013,7 +1016,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
fill_pattern = grid
fill_density = 20%
support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
## MK3 - 0.6mm nozzle
@@ -1028,7 +1031,7 @@ perimeter_speed = 45
solid_infill_speed = 70
top_solid_infill_speed = 45
support_material_contact_distance = 0.22
-raft_contact_distance = 0.22
+raft_contact_distance = 0.2
bridge_flow_ratio = 1
[print:0.20mm DETAIL @0.6 nozzle MK3]
@@ -1042,7 +1045,7 @@ perimeter_speed = 45
solid_infill_speed = 70
top_solid_infill_speed = 45
support_material_contact_distance = 0.22
-raft_contact_distance = 0.22
+raft_contact_distance = 0.2
bridge_flow_ratio = 1
[print:0.30mm QUALITY @0.6 nozzle MK3]
@@ -1311,7 +1314,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
fill_pattern = grid
fill_density = 20%
support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
# MINI - 0.6mm nozzle
@@ -4496,7 +4499,31 @@ initial_exposure_time = 35
material_type = Tough
material_vendor = Monocure
-## Prusa
+## Prusa Polymers 0.025
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.025]
+inherits = *common 0.025*
+exposure_time = 5
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.025]
+inherits = *common 0.025*
+exposure_time = 5
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.025]
+inherits = *common 0.025*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Prusa 0.025
+
[sla_material:Prusa Orange Tough @0.025]
inherits = *common 0.025*
exposure_time = 6
@@ -5185,7 +5212,30 @@ initial_exposure_time = 35
material_type = Tough
material_vendor = Zortrax
-## Prusa
+## Prusa Polymers 0.05
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.05]
+inherits = *common 0.05*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.05]
+inherits = *common 0.05*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.05]
+inherits = *common 0.05*
+exposure_time = 7
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Prusa 0.05
[sla_material:Prusa Beige Tough @0.05]
inherits = *common 0.05*
@@ -5447,7 +5497,30 @@ initial_exposure_time = 50
material_type = Tough
material_vendor = BlueCast
-## Prusa
+## Prusa Polymers 0.1
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.1]
+inherits = *common 0.1*
+exposure_time = 13
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.1]
+inherits = *common 0.1*
+exposure_time = 13
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.1]
+inherits = *common 0.1*
+exposure_time = 14
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Prusa 0.1
[sla_material:Prusa Orange Tough @0.1]
inherits = *common 0.1*
@@ -5530,6 +5603,31 @@ material_vendor = Made for Prusa
## 0.025 SL1S
+## Prusa Polymers 0.025
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 1.8
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 1.8
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.025
+
[sla_material:Prusa Orange Tough @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 1.8
@@ -5644,6 +5742,31 @@ material_vendor = Peopoly
## 0.05 SL1S
+## Prusa Polymers 0.05
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2.4
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.05
+
[sla_material:Prusa Orange Tough @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 2
@@ -5758,6 +5881,31 @@ material_vendor = Peopoly
## 0.1 SL1S
+## Prusa Polymers 0.1
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 2.6
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 2.6
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 3
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.1
+
[sla_material:Prusa Orange Tough @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 2.6
@@ -6573,7 +6721,7 @@ nozzle_diameter = 0.6
max_layer_height = 0.40
min_layer_height = 0.15
default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI
-retract_length = 3.5
+retract_length = 3.2
retract_before_travel = 1.5
[printer:Original Prusa MINI & MINI+ 0.8 nozzle]
diff --git a/resources/shaders/gouraud_light_instanced.vs b/resources/shaders/gouraud_light_instanced.vs
index 997b6a2bf..a42f8e9a4 100644
--- a/resources/shaders/gouraud_light_instanced.vs
+++ b/resources/shaders/gouraud_light_instanced.vs
@@ -34,9 +34,7 @@ void main()
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
- float width = 1.5 * i_scales.x;
- float height = 1.5 * i_scales.y;
- vec4 world_position = vec4(v_position * vec3(vec2(width), height) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
+ vec4 world_position = vec4(v_position * vec3(vec2(1.5 * i_scales.x), 1.5 * i_scales.y) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
diff --git a/sandboxes/aabb-evaluation/aabb-evaluation.cpp b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
index 9ec7451e5..1019ecf28 100644
--- a/sandboxes/aabb-evaluation/aabb-evaluation.cpp
+++ b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
@@ -212,8 +212,7 @@ int main(const int argc, const char *argv[])
return -1;
}
- mesh.repair();
- if (mesh.facets_count() == 0) {
+ if (mesh.empty()) {
std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
return -1;
}
diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp
index 392d90707..c8649888f 100644
--- a/sandboxes/meshboolean/MeshBoolean.cpp
+++ b/sandboxes/meshboolean/MeshBoolean.cpp
@@ -24,7 +24,6 @@ int main(const int argc, const char * argv[])
TriangleMesh input;
input.ReadSTLFile(argv[1]);
- input.repair();
Benchmark bench;
diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp
index f110b23c5..d8f1d3464 100644
--- a/sandboxes/opencsg/Engine.cpp
+++ b/sandboxes/opencsg/Engine.cpp
@@ -145,7 +145,7 @@ void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
int vertices_count = 0;
- for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
+ for (size_t i = 0; i < mesh.facets_count(); ++i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++j)
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
@@ -409,7 +409,6 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
- mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@@ -417,14 +416,12 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>();
mshinst.translate(-center);
- mshinst.require_shared_vertices();
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
m_csgsettings.get_convexity());
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
- holemesh.require_shared_vertices();
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
}
}
diff --git a/sandboxes/opencsg/ShaderCSGDisplay.cpp b/sandboxes/opencsg/ShaderCSGDisplay.cpp
index 8ceb234be..2413bad5b 100644
--- a/sandboxes/opencsg/ShaderCSGDisplay.cpp
+++ b/sandboxes/opencsg/ShaderCSGDisplay.cpp
@@ -43,7 +43,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
- mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@@ -51,15 +50,11 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>();
mshinst.translate(-center);
- mshinst.require_shared_vertices();
add_mesh(mshinst);
}
- for (const sla::DrainHole &holept : holedata) {
- TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
- holemesh.require_shared_vertices();
- add_mesh(holemesh);
- }
+ for (const sla::DrainHole &holept : holedata)
+ add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
}
repaint();
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 3490b8183..0da5e7380 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -397,7 +397,7 @@ int CLI::run(int argc, char **argv)
TriangleMesh mesh = model.mesh();
mesh.repair();
- TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
+ std::vector<TriangleMesh> meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
size_t i = 0;
for (TriangleMesh* m : meshes) {
Model out;
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
index e5491b1aa..8c3ab154a 100644
--- a/src/admesh/connect.cpp
+++ b/src/admesh/connect.cpp
@@ -239,6 +239,7 @@ private:
return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b;
}
+ // Connect edge_a with edge_b, update edge connection statistics.
static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)
{
// Facet a's neighbor is facet b
@@ -249,7 +250,7 @@ private:
stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */
- if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) {
+ if ((edge_a.which_edge < 3 && edge_b.which_edge < 3) || (edge_a.which_edge > 2 && edge_b.which_edge > 2)) {
// These facets are oriented in opposite directions, their normals are probably messed up.
stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3;
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3;
@@ -479,12 +480,13 @@ void stl_check_facets_exact(stl_file *stl)
void stl_check_facets_nearby(stl_file *stl, float tolerance)
{
- if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
- && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
- && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
+ assert(stl->stats.connected_facets_3_edge <= stl->stats.connected_facets_2_edge);
+ assert(stl->stats.connected_facets_2_edge <= stl->stats.connected_facets_1_edge);
+ assert(stl->stats.connected_facets_1_edge <= stl->stats.number_of_facets);
+
+ if (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)
// No need to check any further. All facets are connected.
return;
- }
HashTableEdges hash_table(stl->stats.number_of_facets);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
@@ -514,22 +516,12 @@ void stl_remove_unconnected_facets(stl_file *stl)
/* Update list of connected edges */
stl_neighbors &neighbors = stl->neighbors_start[facet_number];
// Update statistics on unconnected triangle edges.
- switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) {
- case 0: // Facet has 3 neighbors
- -- stl->stats.connected_facets_3_edge;
- -- stl->stats.connected_facets_2_edge;
- -- stl->stats.connected_facets_1_edge;
- break;
- case 1: // Facet has 2 neighbors
- -- stl->stats.connected_facets_2_edge;
- -- stl->stats.connected_facets_1_edge;
- break;
- case 2: // Facet has 1 neighbor
- -- stl->stats.connected_facets_1_edge;
- case 3: // Facet has 0 neighbors
- break;
- default:
- assert(false);
+ switch (neighbors.num_neighbors()) {
+ case 3: -- stl->stats.connected_facets_3_edge; // fall through
+ case 2: -- stl->stats.connected_facets_2_edge; // fall through
+ case 1: -- stl->stats.connected_facets_1_edge; // fall through
+ case 0: break;
+ default: assert(false);
}
if (facet_number < int(-- stl->stats.number_of_facets)) {
@@ -555,20 +547,14 @@ void stl_remove_unconnected_facets(stl_file *stl)
auto remove_degenerate = [stl, remove_facet](int facet)
{
- // Update statistics on face connectivity.
- auto stl_update_connects_remove_1 = [stl](int facet_num) {
- //FIXME when decreasing 3_edge, should I increase 2_edge etc?
- switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) {
- case 0: // Facet has 3 neighbors
- -- stl->stats.connected_facets_3_edge; break;
- case 1: // Facet has 2 neighbors
- -- stl->stats.connected_facets_2_edge; break;
- case 2: // Facet has 1 neighbor
- -- stl->stats.connected_facets_1_edge; break;
- case 3: // Facet has 0 neighbors
- break;
- default:
- assert(false);
+ // Update statistics on face connectivity after one edge was disconnected on the facet "facet_num".
+ auto update_connects_remove_1 = [stl](int facet_num) {
+ switch (stl->neighbors_start[facet_num].num_neighbors()) {
+ case 0: assert(false); break;
+ case 1: -- stl->stats.connected_facets_1_edge; break;
+ case 2: -- stl->stats.connected_facets_2_edge; break;
+ case 3: -- stl->stats.connected_facets_3_edge; break;
+ default: assert(false);
}
};
@@ -604,9 +590,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
// Update statistics on edge connectivity.
if ((neighbor[0] == -1) && (neighbor[1] != -1))
- stl_update_connects_remove_1(neighbor[1]);
+ update_connects_remove_1(neighbor[1]);
if ((neighbor[1] == -1) && (neighbor[0] != -1))
- stl_update_connects_remove_1(neighbor[0]);
+ update_connects_remove_1(neighbor[0]);
if (neighbor[0] >= 0) {
if (neighbor[1] >= 0) {
@@ -634,7 +620,7 @@ void stl_remove_unconnected_facets(stl_file *stl)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
}
if (neighbor[2] >= 0) {
- stl_update_connects_remove_1(neighbor[2]);
+ update_connects_remove_1(neighbor[2]);
stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
}
@@ -652,11 +638,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
++ i;
if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) {
- // remove completely unconnected facets
+ // There are some faces with no connected edge at all. Remove completely unconnected facets.
for (uint32_t i = 0; i < stl->stats.number_of_facets;)
- if (stl->neighbors_start[i].neighbor[0] == -1 &&
- stl->neighbors_start[i].neighbor[1] == -1 &&
- stl->neighbors_start[i].neighbor[2] == -1) {
+ if (stl->neighbors_start[i].num_neighbors() == 0) {
// This facet is completely unconnected. Remove it.
remove_facet(i);
assert(stl_validate(stl));
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 1ca75c95d..8c30a6ae5 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -79,8 +79,7 @@ struct stl_neighbors {
which_vertex_not[1] = -1;
which_vertex_not[2] = -1;
}
- int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); }
- int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
+ int num_neighbors() const { return 3 - ((this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
// Index of a neighbor facet.
int neighbor[3];
@@ -92,28 +91,44 @@ struct stl_stats {
stl_stats() { memset(&header, 0, 81); }
char header[81];
stl_type type = (stl_type)0;
+ // Should always match the number of facets stored inside stl_file::facet_start.
uint32_t number_of_facets = 0;
+ // Bounding box.
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float bounding_diameter = 0.f;
float shortest_edge = 0.f;
+ // After repair, the volume shall always be positive.
float volume = -1.f;
+ // Number of face edges connected to another face.
+ // Don't use this statistics after repair, use the connected_facets_1/2/3_edge instead!
int connected_edges = 0;
+ // Faces with >=1, >=2 and 3 edges connected to another face.
int connected_facets_1_edge = 0;
int connected_facets_2_edge = 0;
int connected_facets_3_edge = 0;
+ // Faces with 1, 2 and 3 open edges after exact chaining, but before repair.
int facets_w_1_bad_edge = 0;
int facets_w_2_bad_edge = 0;
int facets_w_3_bad_edge = 0;
+ // Number of faces read form an STL file.
int original_num_facets = 0;
+ // Number of edges connected one to another by snapping their end vertices.
int edges_fixed = 0;
+ // Number of faces removed because they were degenerated.
int degenerate_facets = 0;
+ // Total number of facets removed: Degenerate faces and unconnected faces.
int facets_removed = 0;
+ // Number of faces added by hole filling.
int facets_added = 0;
+ // Number of faces reversed because of negative volume or because one patch was connected to another patch with incompatible normals.
int facets_reversed = 0;
+ // Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
int backwards_edges = 0;
+ // Number of triangles, which were flipped during the fixing process.
int normals_fixed = 0;
+ // Number of connected triangle patches.
int number_of_parts = 0;
void clear() { *this = stl_stats(); }
@@ -135,13 +150,11 @@ struct stl_file {
std::vector<stl_facet> facet_start;
std::vector<stl_neighbors> neighbors_start;
// Statistics
- stl_stats stats;
+ stl_stats stats;
};
struct indexed_triangle_set
{
- indexed_triangle_set() {}
-
void clear() { indices.clear(); vertices.clear(); }
size_t memsize() const {
@@ -149,9 +162,7 @@ struct indexed_triangle_set
}
std::vector<stl_triangle_vertex_indices> indices;
- std::vector<stl_vertex> vertices;
- //FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
- //std::vector<stl_normal> normals
+ std::vector<stl_vertex> vertices;
bool empty() const { return indices.empty() || vertices.empty(); }
};
@@ -244,9 +255,15 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
stl_get_size(stl);
}
+template<typename V>
+inline void its_translate(indexed_triangle_set &its, const V v)
+{
+ for (stl_vertex &v_dst : its.vertices)
+ v_dst += v;
+}
template<typename T>
-extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
+inline void its_transform(indexed_triangle_set &its, T *trafo3x4)
{
for (stl_vertex &v_dst : its.vertices) {
stl_vertex v_src = v_dst;
diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp
index ddf377c78..26f5dc321 100644
--- a/src/admesh/stl_io.cpp
+++ b/src/admesh/stl_io.cpp
@@ -205,11 +205,12 @@ bool stl_write_quad_object(stl_file *stl, char *file)
fprintf(fp, "CQUAD\n");
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
- switch (stl->neighbors_start[i].num_neighbors_missing()) {
- case 0: color = connect_color; break;
- case 1: color = uncon_1_color; break;
- case 2: color = uncon_2_color; break;
- default: color = uncon_3_color;
+ switch (stl->neighbors_start[i].num_neighbors()) {
+ case 0:
+ default: color = uncon_3_color; break;
+ case 1: color = uncon_2_color; break;
+ case 2: color = uncon_1_color; break;
+ case 3: color = connect_color; break;
}
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 177d8d708..f3a1d5988 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -139,6 +139,9 @@ void AppConfig::set_defaults()
if (get("default_action_on_select_preset").empty())
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
+ if (get("default_action_on_new_project").empty())
+ set("default_action_on_new_project", "none"); // , "keep(transfer)", "discard" or "save"
+
if (get("color_mapinulation_panel").empty())
set("color_mapinulation_panel", "0");
diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp
index 76f675c70..938f54249 100644
--- a/src/libslic3r/BlacklistedLibraryCheck.cpp
+++ b/src/libslic3r/BlacklistedLibraryCheck.cpp
@@ -12,7 +12,7 @@ namespace Slic3r {
#ifdef WIN32
//only dll name with .dll suffix - currently case sensitive
-const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
+const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll", L"amhook.dll", L"AMHook.dll" });
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
{
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index f9bef903b..d42077b9c 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -78,8 +78,6 @@ add_library(libslic3r STATIC
Format/OBJ.hpp
Format/objparser.cpp
Format/objparser.hpp
- Format/PRUS.cpp
- Format/PRUS.hpp
Format/STL.cpp
Format/STL.hpp
Format/SL1.hpp
diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp
index 8a4dfc03c..e8f87a6e3 100644
--- a/src/libslic3r/ClipperUtils.cpp
+++ b/src/libslic3r/ClipperUtils.cpp
@@ -126,6 +126,45 @@ ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
return PolyTreeToExPolygons(std::move(polytree));
}
+#if 0
+// Global test.
+bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
+{
+ struct Helper {
+ static void collect_points_recursive(const ClipperLib::PolyNode &polynode, ClipperLib::Path &out) {
+ // For each hole of the current expolygon:
+ out.insert(out.end(), polynode.Contour.begin(), polynode.Contour.end());
+ for (int i = 0; i < polynode.ChildCount(); ++ i)
+ collect_points_recursive(*polynode.Childs[i], out);
+ }
+ };
+ ClipperLib::Path pts;
+ for (int i = 0; i < polytree.ChildCount(); ++ i)
+ Helper::collect_points_recursive(*polytree.Childs[i], pts);
+ return has_duplicate_points(std::move(pts));
+}
+#else
+// Local test inside each of the contours.
+bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
+{
+ struct Helper {
+ static bool has_duplicate_points_recursive(const ClipperLib::PolyNode &polynode) {
+ if (has_duplicate_points(polynode.Contour))
+ return true;
+ for (int i = 0; i < polynode.ChildCount(); ++ i)
+ if (has_duplicate_points_recursive(*polynode.Childs[i]))
+ return true;
+ return false;
+ }
+ };
+ ClipperLib::Path pts;
+ for (int i = 0; i < polytree.ChildCount(); ++ i)
+ if (Helper::has_duplicate_points_recursive(*polytree.Childs[i]))
+ return true;
+ return false;
+}
+#endif
+
// Offset outside by 10um, one by one.
template<typename PathsProvider>
static ClipperLib::Paths safety_offset(PathsProvider &&paths)
@@ -467,6 +506,8 @@ Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
+Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
+ { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
@@ -496,6 +537,8 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfac
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
+Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
+ { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
@@ -610,12 +653,18 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
+Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
+ { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonsProvider(clip)); }
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
+Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
+ { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
+Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
+ { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
@@ -637,7 +686,9 @@ Lines _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Pol
// convert Polylines to Lines
Lines retval;
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
- retval.emplace_back(polyline->operator Line());
+ if (polyline->size() >= 2)
+ //FIXME It may happen, that Clipper produced a polyline with more than 2 collinear points by clipping a single line with polygons. It is a very rare issue, but it happens, see GH #6933.
+ retval.push_back({ polyline->front(), polyline->back() });
return retval;
}
diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp
index c4e014a74..f49d922c1 100644
--- a/src/libslic3r/ClipperUtils.hpp
+++ b/src/libslic3r/ClipperUtils.hpp
@@ -303,6 +303,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygo
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
+Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@@ -312,6 +313,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surf
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
+Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
@@ -322,6 +324,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
}
// Safety offset is applied to the clipping polygons only.
+Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@@ -337,6 +340,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
+Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
+Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index ab58a43aa..2d4780d0e 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -740,7 +740,11 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
}
// Load the config keys from the given string.
-static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions)
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
+#else
+static inline size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
{
if (str == nullptr)
return 0;
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 6439e4632..dd121c90a 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -2015,6 +2015,10 @@ public:
// Set all the nullable values to nils.
void null_nullables();
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+
private:
// Set a configuration value from a string.
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp
index 506ba8cb6..bfe0479fe 100644
--- a/src/libslic3r/ExPolygon.cpp
+++ b/src/libslic3r/ExPolygon.cpp
@@ -92,7 +92,7 @@ bool ExPolygon::contains(const Line &line) const
bool ExPolygon::contains(const Polyline &polyline) const
{
- return diff_pl((Polylines)polyline, *this).empty();
+ return diff_pl(polyline, *this).empty();
}
bool ExPolygon::contains(const Polylines &polylines) const
@@ -114,10 +114,11 @@ bool ExPolygon::contains(const Polylines &polylines) const
bool ExPolygon::contains(const Point &point) const
{
- if (!this->contour.contains(point)) return false;
- for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
- if (it->contains(point)) return false;
- }
+ if (! this->contour.contains(point))
+ return false;
+ for (const Polygon &hole : this->holes)
+ if (hole.contains(point))
+ return false;
return true;
}
@@ -367,6 +368,57 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons)
return out;
}
+bool has_duplicate_points(const ExPolygon &expoly)
+{
+#if 1
+ // Check globally.
+ size_t cnt = expoly.contour.points.size();
+ for (const Polygon &hole : expoly.holes)
+ cnt += hole.points.size();
+ std::vector<Point> allpts;
+ allpts.reserve(cnt);
+ allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
+ for (const Polygon &hole : expoly.holes)
+ allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
+ return has_duplicate_points(std::move(allpts));
+#else
+ // Check per contour.
+ if (has_duplicate_points(expoly.contour))
+ return true;
+ for (const Polygon &hole : expoly.holes)
+ if (has_duplicate_points(hole))
+ return true;
+ return false;
+#endif
+}
+
+bool has_duplicate_points(const ExPolygons &expolys)
+{
+#if 1
+ // Check globally.
+ size_t cnt = 0;
+ for (const ExPolygon &expoly : expolys) {
+ cnt += expoly.contour.points.size();
+ for (const Polygon &hole : expoly.holes)
+ cnt += hole.points.size();
+ }
+ std::vector<Point> allpts;
+ allpts.reserve(cnt);
+ for (const ExPolygon &expoly : expolys) {
+ allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
+ for (const Polygon &hole : expoly.holes)
+ allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
+ }
+ return has_duplicate_points(std::move(allpts));
+#else
+ // Check per contour.
+ for (const ExPolygon &expoly : expolys)
+ if (has_duplicate_points(expoly))
+ return true;
+ return false;
+#endif
+}
+
bool remove_sticks(ExPolygon &poly)
{
return remove_sticks(poly.contour) || remove_sticks(poly.holes);
diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp
index 464310ac0..6f3884673 100644
--- a/src/libslic3r/ExPolygon.hpp
+++ b/src/libslic3r/ExPolygon.hpp
@@ -351,20 +351,24 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
return out;
}
-extern BoundingBox get_extents(const ExPolygon &expolygon);
-extern BoundingBox get_extents(const ExPolygons &expolygons);
-extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
-extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
-extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
+BoundingBox get_extents(const ExPolygon &expolygon);
+BoundingBox get_extents(const ExPolygons &expolygons);
+BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
+BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
+std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
-extern bool remove_sticks(ExPolygon &poly);
-extern void keep_largest_contour_only(ExPolygons &polygons);
+// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
+bool has_duplicate_points(const ExPolygon &expoly);
+bool has_duplicate_points(const ExPolygons &expolys);
+
+bool remove_sticks(ExPolygon &poly);
+void keep_largest_contour_only(ExPolygons &polygons);
inline double area(const ExPolygon &poly) { return poly.area(); }
inline double area(const ExPolygons &polys) { double s = 0.; for (auto &p : polys) s += p.area(); return s; }
// Removes all expolygons smaller than min_area and also removes all holes smaller than min_area
-extern bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
+bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp
index 3eabc6106..29b343db0 100644
--- a/src/libslic3r/Fill/FillAdaptive.cpp
+++ b/src/libslic3r/Fill/FillAdaptive.cpp
@@ -928,7 +928,9 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
#endif // NDEBUG
- } else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
+ } else if (pl.size() >= 2 &&
+ //FIXME Hoping that pl is really a line, trimmed by a polygon using ClipperUtils. Sometimes Clipper leaves some additional collinear points on the polyline, let's hope it is all right.
+ Line{ pl.front(), pl.back() }.distance_to_squared(pt) <= 1000 * 1000)
out = closest.front().second;
}
return out;
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 295c1413d..9f4015913 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -21,6 +21,7 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/spirit/include/karma.hpp>
+#include <boost/spirit/include/qi_int.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
@@ -32,6 +33,8 @@ namespace pt = boost::property_tree;
#include <Eigen/Dense>
#include "miniz_extension.hpp"
+#include <fast_float/fast_float.h>
+
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
// https://github.com/boostorg/spirit/pull/586
// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
@@ -172,14 +175,18 @@ std::string get_attribute_value_string(const char** attributes, unsigned int att
float get_attribute_value_float(const char** attributes, unsigned int attributes_size, const char* attribute_key)
{
- const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
- return (text != nullptr) ? (float)::atof(text) : 0.0f;
+ float value = 0.0f;
+ if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
+ fast_float::from_chars(text, text + strlen(text), value);
+ return value;
}
int get_attribute_value_int(const char** attributes, unsigned int attributes_size, const char* attribute_key)
{
- const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
- return (text != nullptr) ? ::atoi(text) : 0;
+ int value = 0;
+ if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
+ boost::spirit::qi::parse(text, text + strlen(text), boost::spirit::qi::int_, value);
+ return value;
}
bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
@@ -298,8 +305,8 @@ namespace Slic3r {
struct Geometry
{
- std::vector<float> vertices;
- std::vector<unsigned int> triangles;
+ std::vector<Vec3f> vertices;
+ std::vector<Vec3i> triangles;
std::vector<std::string> custom_supports;
std::vector<std::string> custom_seam;
std::vector<std::string> mmu_segmentation;
@@ -713,7 +720,7 @@ namespace Slic3r {
}
// use the geometry to create the volumes in the new model objects
- ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() / 3 - 1 });
+ ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
// for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry
@@ -786,7 +793,7 @@ namespace Slic3r {
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
- volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() / 3 - 1);
+ volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
// select as volumes
volumes_ptr = &volumes;
@@ -1552,9 +1559,10 @@ namespace Slic3r {
{
// appends the vertex coordinates
// missing values are set equal to ZERO
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
+ m_curr_object.geometry.vertices.emplace_back(
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
return true;
}
@@ -1588,9 +1596,10 @@ namespace Slic3r {
// appends the triangle's vertices indices
// missing values are set equal to ZERO
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
+ m_curr_object.geometry.triangles.emplace_back(
+ get_attribute_value_int(attributes, num_attributes, V1_ATTR),
+ get_attribute_value_int(attributes, num_attributes, V2_ATTR),
+ get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
@@ -1879,8 +1888,9 @@ namespace Slic3r {
return false;
}
- unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
+ unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
unsigned int renamed_volumes_count = 0;
+ int processed_vertices_max_id = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
@@ -1890,77 +1900,68 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- bool is_left_handed = false;
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
break;
}
}
// splits volume out of imported geometry
- TriangleMesh triangle_mesh;
- stl_file &stl = triangle_mesh.stl;
- unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = (uint32_t)triangles_count;
- stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- unsigned int src_start_id = volume_data.first_triangle_id * 3;
-
- for (unsigned int i = 0; i < triangles_count; ++i) {
- unsigned int ii = i * 3;
- stl_facet& facet = stl.facet_start[i];
- for (unsigned int v = 0; v < 3; ++v) {
- unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
- if (tri_id + 2 >= geometry.vertices.size()) {
- add_error("Malformed triangle mesh");
+ std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
+ const size_t triangles_count = faces.size();
+
+ int min_id = faces.front()[0];
+ int max_id = faces.front()[0];
+ for (const Vec3i& face : faces) {
+ for (const int tri_id : face) {
+ if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
+ add_error("Found invalid vertex id");
return false;
}
- facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
+ min_id = std::min(min_id, tri_id);
+ max_id = std::max(max_id, tri_id);
}
}
- stl_get_size(&stl);
- triangle_mesh.repair();
+ // rebase indices to the current vertices list
+ for (Vec3i& face : faces) {
+ for (int& tri_id : face) {
+ tri_id -= min_id;
+ }
+ }
+
+ processed_vertices_max_id = 1 + std::max(processed_vertices_max_id, max_id);
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- // PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
- // This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
- if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
- stl.stats.facets_reversed = 0;
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
+ std::vector<Vec3f> vertices(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
+ TriangleMesh triangle_mesh(std::move(vertices), std::move(faces));
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
- triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix());
+ triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
+ //FIXME do the mesh fixing?
}
}
+ if (triangle_mesh.volume() < 0)
+ triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
- volume->calculate_convex_hull();
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
- for (unsigned i=0; i<triangles_count; ++i) {
- size_t index = src_start_id/3 + i;
+ for (size_t i=0; i<triangles_count; ++i) {
+ size_t index = volume_data.first_triangle_id + i;
assert(index < geometry.custom_supports.size());
assert(index < geometry.custom_seam.size());
assert(index < geometry.mmu_segmentation.size());
@@ -2536,11 +2537,6 @@ namespace Slic3r {
if (volume == nullptr)
continue;
- if (!volume->mesh().repaired)
- throw Slic3r::FileIOError("store_3mf() requires repair()");
- if (!volume->mesh().has_shared_vertices())
- throw Slic3r::FileIOError("store_3mf() requires shared vertices");
-
volumes_offsets.insert({ volume, Offsets(vertices_count) });
const indexed_triangle_set &its = volume->mesh().its;
@@ -2581,10 +2577,7 @@ namespace Slic3r {
if (volume == nullptr)
continue;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed();
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
-
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end());
@@ -2599,7 +2592,6 @@ namespace Slic3r {
{
const Vec3i &idx = its.indices[i];
char *ptr = buf;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
@@ -2607,15 +2599,6 @@ namespace Slic3r {
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
-#else
- boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
- " v1=\"" << boost::spirit::int_ <<
- "\" v2=\"" << boost::spirit::int_ <<
- "\" v3=\"" << boost::spirit::int_ << "\"",
- idx[0] + volume_it->second.first_vertex_id,
- idx[1] + volume_it->second.first_vertex_id,
- idx[2] + volume_it->second.first_vertex_id);
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
*ptr = '\0';
output_buffer += buf;
}
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 35b3e0cf4..f0807df51 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -244,11 +244,11 @@ struct AMFParserContext
// Map from obect name to object idx & instances.
std::map<std::string, Object> m_object_instances_map;
// Vertices parsed for the current m_object.
- std::vector<float> m_object_vertices;
+ std::vector<Vec3f> m_object_vertices;
// Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume *m_volume { nullptr };
// Faces collected for the current m_volume.
- std::vector<int> m_volume_facets;
+ std::vector<Vec3i> m_volume_facets;
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform;
// Current material allocated for an amf/metadata subtree.
@@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX:
assert(m_object);
// Parse the vertex data
- m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
- m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
- m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
+ m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[2].c_str())));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Faces of the current volume:
case NODE_TYPE_TRIANGLE:
assert(m_object && m_volume);
- m_volume_facets.emplace_back(atoi(m_value[0].c_str()));
- m_volume_facets.emplace_back(atoi(m_value[1].c_str()));
- m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
+ m_volume_facets.emplace_back(atoi(m_value[0].c_str()), atoi(m_value[1].c_str()), atoi(m_value[2].c_str()));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@@ -621,44 +617,36 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME:
{
assert(m_object && m_volume);
- TriangleMesh mesh;
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
- stl.stats.original_num_facets = stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
- for (size_t i = 0; i < m_volume_facets.size();) {
- stl_facet &facet = stl.facet_start[i/3];
- for (unsigned int v = 0; v < 3; ++v)
- {
- unsigned int tri_id = m_volume_facets[i++] * 3;
- if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) {
+ // Verify validity of face indices.
+ for (Vec3i face : m_volume_facets)
+ for (unsigned int tri_id : face)
+ if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
this->stop("Malformed triangle mesh");
return;
}
- facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
- }
- }
- stl_get_size(&stl);
- mesh.repair();
- m_volume->set_mesh(std::move(mesh));
+
+ {
+ TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
+ if (triangle_mesh.volume() < 0)
+ triangle_mesh.flip_triangles();
+ m_volume->set_mesh(std::move(triangle_mesh));
+ }
+
// stores the volume matrix taken from the metadata, if present
- if (has_transform)
+ if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
- if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
- {
+
+ if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation();
- }
- else
+ } else
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
m_volume->calculate_convex_hull();
m_volume_facets.clear();
+ m_object_vertices.clear();
m_volume = nullptr;
break;
}
@@ -1187,10 +1175,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
int num_vertices = 0;
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
- if (! volume->mesh().repaired)
- throw Slic3r::FileIOError("store_amf() requires repair()");
- if (! volume->mesh().has_shared_vertices())
- throw Slic3r::FileIOError("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {
diff --git a/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp
index cb7eb4549..54c373ce3 100644
--- a/src/libslic3r/Format/OBJ.cpp
+++ b/src/libslic3r/Format/OBJ.cpp
@@ -19,7 +19,8 @@ namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr)
{
- if(meshptr == nullptr) return false;
+ if (meshptr == nullptr)
+ return false;
// Parse the OBJ file.
ObjParser::ObjData data;
@@ -31,84 +32,69 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
// Count the faces and verify, that all faces are triangular.
size_t num_faces = 0;
size_t num_quads = 0;
- for (size_t i = 0; i < data.vertices.size(); ) {
+ for (size_t i = 0; i < data.vertices.size(); ++ i) {
+ // Find the end of face.
size_t j = i;
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
- if (i == j)
- continue;
- size_t face_vertices = j - i;
- if (face_vertices != 3 && face_vertices != 4) {
- // Non-triangular and non-quad faces are not supported as of now.
- return false;
+ if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
+ if (num_face_vertices > 4) {
+ // Non-triangular and non-quad faces are not supported as of now.
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
+ return false;
+ } else if (num_face_vertices < 3) {
+ // Non-triangular and non-quad faces are not supported as of now.
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
+ return false;
+ }
+ if (num_face_vertices == 4)
+ ++ num_quads;
+ ++ num_faces;
+ i = j;
}
- if (face_vertices == 4)
- ++ num_quads;
- ++ num_faces;
- i = j + 1;
}
- // Convert ObjData into STL.
- TriangleMesh &mesh = *meshptr;
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = uint32_t(num_faces + num_quads);
- stl.stats.original_num_facets = int(num_faces + num_quads);
- // stl_allocate clears all the allocated data to zero, all normals are set to zeros as well.
- stl_allocate(&stl);
- size_t i_face = 0;
- for (size_t i = 0; i < data.vertices.size(); ++ i) {
+ // Convert ObjData into indexed triangle set.
+ indexed_triangle_set its;
+ size_t num_vertices = data.coordinates.size() / 4;
+ its.vertices.reserve(num_vertices);
+ its.indices.reserve(num_faces + num_quads);
+ for (size_t i = 0; i < num_vertices; ++ i) {
+ size_t j = i << 2;
+ its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
+ }
+ int indices[4];
+ for (size_t i = 0; i < data.vertices.size();)
if (data.vertices[i].coordIdx == -1)
- continue;
- stl_facet &facet = stl.facet_start[i_face ++];
- size_t num_normals = 0;
- stl_normal normal(stl_normal::Zero());
- for (unsigned int v = 0; v < 3; ++ v) {
- const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
- if (vertex.normalIdx != -1) {
- normal(0) += data.normals[vertex.normalIdx*3];
- normal(1) += data.normals[vertex.normalIdx*3+1];
- normal(2) += data.normals[vertex.normalIdx*3+2];
- ++ num_normals;
- }
- }
- // Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
- if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
- // This is a quad. Produce the other triangle.
- stl_facet &facet2 = stl.facet_start[i_face++];
- facet2.vertex[0] = facet.vertex[0];
- facet2.vertex[1] = facet.vertex[2];
- const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
- if (vertex.normalIdx != -1) {
- normal(0) += data.normals[vertex.normalIdx*3];
- normal(1) += data.normals[vertex.normalIdx*3+1];
- normal(2) += data.normals[vertex.normalIdx*3+2];
- ++ num_normals;
- }
- if (num_normals == 4) {
- // Normalize an average normal of a quad.
- float len = facet.normal.norm();
- if (len > EPSILON) {
- normal /= len;
- facet.normal = normal;
- facet2.normal = normal;
+ ++ i;
+ else {
+ int cnt = 0;
+ while (i < data.vertices.size())
+ if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
+ break;
+ } else {
+ assert(cnt < 4);
+ if (vertex.coordIdx < 0 || vertex.coordIdx >= its.vertices.size()) {
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
+ return false;
+ }
+ indices[cnt ++] = vertex.coordIdx;
}
+ if (cnt) {
+ assert(cnt == 3 || cnt == 4);
+ // Insert one or two faces (triangulate a quad).
+ its.indices.emplace_back(indices[0], indices[1], indices[2]);
+ if (cnt == 4)
+ its.indices.emplace_back(indices[0], indices[2], indices[3]);
}
- } else if (num_normals == 3) {
- // Normalize an average normal of a triangle.
- float len = facet.normal.norm();
- if (len > EPSILON)
- facet.normal = normal / len;
}
- }
- stl_get_size(&stl);
- mesh.repair();
- if (mesh.facets_count() == 0) {
+
+ *meshptr = TriangleMesh(std::move(its));
+ if (meshptr->empty()) {
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false;
}
-
+ if (meshptr->volume() < 0)
+ meshptr->flip_triangles();
return true;
}
diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp
deleted file mode 100644
index 586fbafb5..000000000
--- a/src/libslic3r/Format/PRUS.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-#include <string.h>
-#include <exception>
-
-#include <boost/algorithm/string.hpp>
-
-#include "miniz_extension.hpp"
-
-#include <Eigen/Geometry>
-
-#include "../libslic3r.h"
-#include "../Model.hpp"
-
-#include "PRUS.hpp"
-
-#if 0
-// Enable debugging and assert in this file.
-#define DEBUG
-#define _DEBUG
-#undef NDEBUG
-#endif
-
-#include <assert.h>
-
-namespace Slic3r
-{
-
-struct StlHeader
-{
- char comment[80];
- uint32_t nTriangles;
-};
-
-static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
-
-// Buffered line reader to a string buffer.
-class LineReader
-{
-public:
- LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {}
-
- const char* next_line() {
- // Skip empty lines.
- while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
- ++ m_pos;
- if (m_pos == m_len) {
- // End of file.
- return nullptr;
- }
- // The buffer is nonempty and it does not start with end of lines. Find the first end of line.
- int end = m_pos + 1;
- while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
- ++ end;
- char *ptr_out = m_buffer.data() + m_pos;
- m_pos = end + 1;
- m_buffer[end] = 0;
- return ptr_out;
- }
-
- int next_line_scanf(const char *format, ...)
- {
- const char *line = next_line();
- if (line == nullptr)
- return -1;
- int result;
- va_list arglist;
- va_start(arglist, format);
- result = vsscanf(line, format, arglist);
- va_end(arglist);
- return result;
- }
-
-private:
- std::vector<char> &m_buffer;
- int m_pos;
- int m_len;
-};
-
-static void extract_model_from_archive(
- // name of the model file
- const char *name,
- // path to the archive
- const char *path,
- // content of scene.xml
- const std::vector<char> &scene_xml_data,
- // loaded data of this STL
- std::vector<char> &data,
- // Model, to which the newly loaded objects will be added
- Model *model,
- // To map multiple STLs into a single model object for multi-material prints.
- std::map<int, ModelObject*> &group_to_model_object)
-{
- // Find the model entry in the XML data.
- char model_name_tag[1024];
- sprintf(model_name_tag, "<model name=\"%s\">", name);
- const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
- const char *zero_tag = "<zero>";
- const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
- Vec3d instance_rotation = Vec3d::Zero();
- Vec3d instance_scaling_factor = Vec3d::Ones();
- Vec3d instance_offset = Vec3d::Zero();
- bool trafo_set = false;
- unsigned int group_id = (unsigned int)-1;
- unsigned int extruder_id = (unsigned int)-1;
- ModelObject *model_object = nullptr;
- if (model_xml != nullptr) {
- model_xml += strlen(model_name_tag);
- const char *position_tag = "<position>";
- const char *position_xml = strstr(model_xml, position_tag);
- const char *rotation_tag = "<rotation>";
- const char *rotation_xml = strstr(model_xml, rotation_tag);
- const char *scale_tag = "<scale>";
- const char *scale_xml = strstr(model_xml, scale_tag);
- float position[3], rotation[3], scale[3], zero[3];
- if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
- sscanf(position_xml+strlen(position_tag),
- "[%f, %f, %f]", position, position+1, position+2) == 3 &&
- sscanf(rotation_xml+strlen(rotation_tag),
- "[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
- sscanf(scale_xml+strlen(scale_tag),
- "[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
- sscanf(zero_xml+strlen(zero_tag),
- "[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
- instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
- instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
- instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
- trafo_set = true;
- }
- const char *group_tag = "<group>";
- const char *group_xml = strstr(model_xml, group_tag);
- const char *extruder_tag = "<extruder>";
- const char *extruder_xml = strstr(model_xml, extruder_tag);
- if (group_xml != nullptr) {
- int group = atoi(group_xml + strlen(group_tag));
- if (group > 0) {
- group_id = group;
- auto it = group_to_model_object.find(group_id);
- if (it != group_to_model_object.end())
- model_object = it->second;
- }
- }
- if (extruder_xml != nullptr) {
- int e = atoi(extruder_xml + strlen(extruder_tag));
- if (e > 0)
- extruder_id = e;
- }
- }
- if (! trafo_set)
- throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
-
- // Extract the STL.
- StlHeader header;
- TriangleMesh mesh;
- bool mesh_valid = false;
- bool stl_ascii = false;
- if (data.size() > sizeof(StlHeader)) {
- memcpy((char*)&header, data.data(), sizeof(StlHeader));
- if (strncmp(header.comment, "solid ", 6) == 0)
- stl_ascii = true;
- else {
- // Header has been extracted. Now read the faces.
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = header.nTriangles;
- stl.stats.original_num_facets = header.nTriangles;
- stl_allocate(&stl);
- if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
- memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
- if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
- // The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
- unsigned char *data = (unsigned char*)stl.facet_start.data();
- for (size_t i = header.nTriangles - 1; i > 0; -- i)
- memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
- }
- // All the faces have been read.
- stl_get_size(&stl);
- mesh.repair();
- if (std::abs(stl.stats.min(2)) < EPSILON)
- stl.stats.min(2) = 0.;
- // Add a mesh to a model.
- if (mesh.facets_count() > 0)
- mesh_valid = true;
- }
- }
- } else
- stl_ascii = true;
-
- if (stl_ascii) {
- // Try to parse ASCII STL.
- char normal_buf[3][32];
- stl_facet facet;
- std::vector<stl_facet> facets;
- LineReader line_reader(data);
- std::string solid_name;
- facet.extra[0] = facet.extra[1] = 0;
- for (;;) {
- const char *line = line_reader.next_line();
- if (line == nullptr)
- // End of file.
- break;
- if (strncmp(line, "solid", 5) == 0) {
- // Opening the "solid" block.
- if (! solid_name.empty()) {
- // Error, solid block is already open.
- facets.clear();
- break;
- }
- solid_name = line + 5;
- if (solid_name.empty())
- solid_name = "unknown";
- continue;
- }
- if (strncmp(line, "endsolid", 8) == 0) {
- // Closing the "solid" block.
- if (solid_name.empty()) {
- // Error, no solid block is open.
- facets.clear();
- break;
- }
- solid_name.clear();
- continue;
- }
- // Line has to start with the word solid.
- int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
- assert(res_normal == 3);
- int res_outer_loop = line_reader.next_line_scanf(" outer loop");
- assert(res_outer_loop == 0);
- int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
- assert(res_vertex1 == 3);
- int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
- assert(res_vertex2 == 3);
- int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
- assert(res_vertex3 == 3);
- int res_endloop = line_reader.next_line_scanf(" endloop");
- assert(res_endloop == 0);
- int res_endfacet = line_reader.next_line_scanf(" endfacet");
- if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
- // perror("Something is syntactically very wrong with this ASCII STL!");
- facets.clear();
- break;
- }
- // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
- if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
- sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
- sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
- // Normal was mangled. Maybe denormals or "not a number" were stored?
- // Just reset the normal and silently ignore it.
- facet.normal = stl_normal::Zero();
- }
- facets.emplace_back(facet);
- }
- if (! facets.empty() && solid_name.empty()) {
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = (uint32_t)facets.size();
- stl.stats.original_num_facets = (int)facets.size();
- stl_allocate(&stl);
- memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
- stl_get_size(&stl);
- mesh.repair();
- // Add a mesh to a model.
- if (mesh.facets_count() > 0)
- mesh_valid = true;
- }
- }
-
- if (! mesh_valid)
- throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
-
- // Add this mesh to the model.
- ModelVolume *volume = nullptr;
- if (model_object == nullptr) {
- // This is a first mesh of a group. Create a new object & volume.
- model_object = model->add_object(name, path, std::move(mesh));
- volume = model_object->volumes.front();
- ModelInstance *instance = model_object->add_instance();
- instance->set_rotation(instance_rotation);
- instance->set_scaling_factor(instance_scaling_factor);
- instance->set_offset(instance_offset);
- if (group_id != (unsigned int)(-1))
- group_to_model_object[group_id] = model_object;
- } else {
- // This is not the 1st mesh of a group. Add it to the ModelObject.
- volume = model_object->add_volume(std::move(mesh));
- volume->name = name;
- }
- // Set the extruder to the volume.
- if (extruder_id != (unsigned int)-1)
- volume->config.set("extruder", int(extruder_id));
-}
-
-// Load a PrusaControl project file into a provided model.
-bool load_prus(const char *path, Model *model)
-{
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- size_t n_models_initial = model->objects.size();
- mz_bool res = MZ_FALSE;
- try {
- if (!open_zip_reader(&archive, path))
- throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
- std::vector<char> scene_xml_data;
- // For grouping multiple STLs into a single ModelObject for multi-material prints.
- std::map<int, ModelObject*> group_to_model_object;
- mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
- for (mz_uint i = 0; i < num_entries; ++ i) {
- mz_zip_archive_file_stat stat;
- if (! mz_zip_reader_file_stat(&archive, i, &stat))
- continue;
- std::vector<char> buffer;
- buffer.assign((size_t)stat.m_uncomp_size, 0);
- res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == MZ_FALSE)
- throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
- if (strcmp(stat.m_filename, "scene.xml") == 0) {
- if (! scene_xml_data.empty())
- throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
- scene_xml_data = std::move(buffer);
- } else if (boost::iends_with(stat.m_filename, ".stl")) {
- // May throw std::exception
- extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object);
- }
- }
- } catch (std::exception &ex) {
- close_zip_reader(&archive);
- throw ex;
- }
-
- close_zip_reader(&archive);
- return model->objects.size() > n_models_initial;
-}
-
-}; // namespace Slic3r
diff --git a/src/libslic3r/Format/PRUS.hpp b/src/libslic3r/Format/PRUS.hpp
deleted file mode 100644
index be5c5c61a..000000000
--- a/src/libslic3r/Format/PRUS.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#define slic3r_Format_PRUS_hpp_
-
-namespace Slic3r {
-
-class TriangleMesh;
-class Model;
-
-// Load a PrusaControl project file into a provided model.
-extern bool load_prus(const char *path, Model *model);
-
-}; // namespace Slic3r
diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp
index e2ef801f2..4ddc584ba 100644
--- a/src/libslic3r/Format/SL1.cpp
+++ b/src/libslic3r/Format/SL1.cpp
@@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
- throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
+ throw MissingProfileError("Invalid SL1 / SL1S file");
RasterParams rstp;
@@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
if (!opt_layerh || !opt_init_layerh)
- throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
+ throw MissingProfileError("Invalid SL1 / SL1S file");
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
}
@@ -293,24 +293,34 @@ ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrint
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
}
+// If the profile is missing from the archive (older PS versions did not have
+// it), profile_out's initial value will be used as fallback. profile_out will be empty on
+// function return if the archive did not contain any profile.
ConfigSubstitutions import_sla_archive(
const std::string & zipfname,
Vec2i windowsize,
indexed_triangle_set & out,
- DynamicPrintConfig & profile,
+ DynamicPrintConfig & profile_out,
std::function<bool(int)> progr)
{
// Ensure minimum window size for marching squares
windowsize.x() = std::max(2, windowsize.x());
windowsize.y() = std::max(2, windowsize.y());
- ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
- ConfigSubstitutions config_substitutions = profile.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
+ 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);
- RasterParams rstp = get_raster_params(profile);
+ // 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.
+ profile_use = profile_in.empty() ? profile_out : profile_in;
+ profile_out = profile_in;
+
+ RasterParams rstp = get_raster_params(profile_use);
rstp.win = {windowsize.y(), windowsize.x()};
- SliceParams slicp = get_slice_params(profile);
+ SliceParams slicp = get_slice_params(profile_use);
std::vector<ExPolygons> slices =
extract_slices_from_sla_archive(arch, rstp, progr);
diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp
index 2c7e1edc1..7f4356b12 100644
--- a/src/libslic3r/Format/SL1.hpp
+++ b/src/libslic3r/Format/SL1.hpp
@@ -8,7 +8,7 @@
namespace Slic3r {
-class SL1Archive: public SLAPrinter {
+class SL1Archive: public SLAArchive {
SLAPrinterConfig m_cfg;
protected:
@@ -57,6 +57,8 @@ inline ConfigSubstitutions import_sla_archive(
return import_sla_archive(zipfname, windowsize, out, profile, progr);
}
+class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
+
} // namespace Slic3r::sla
#endif // ARCHIVETRAITS_HPP
diff --git a/src/libslic3r/Format/STL.cpp b/src/libslic3r/Format/STL.cpp
index 932906fe0..2f2c9ec7f 100644
--- a/src/libslic3r/Format/STL.cpp
+++ b/src/libslic3r/Format/STL.cpp
@@ -21,8 +21,7 @@ bool load_stl(const char *path, Model *model, const char *object_name_in)
// die "Failed to open $file\n" if !-e $path;
return false;
}
- mesh.repair();
- if (mesh.facets_count() == 0) {
+ if (mesh.empty()) {
// die "This STL file couldn't be read because it's empty.\n"
return false;
}
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index cebc9136f..ff0a7c027 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -784,7 +784,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
}
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
- m_processor.finalize();
+ // Post-process the G-code to update time stamps.
+ m_processor.finalize(true);
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
if (result != nullptr) {
@@ -1842,11 +1843,7 @@ namespace ProcessLayer
assert(m600_extruder_before_layer >= 0);
// Color Change or Tool Change as Color Change.
// add tag for processor
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n";
-#else
- gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer
// && !MMU1
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index d16cfb9cf..2397a17ff 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -2,6 +2,7 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/LocalesUtils.hpp"
+#include "libslic3r/format.hpp"
#include "GCodeProcessor.hpp"
#include <boost/log/trivial.hpp>
@@ -27,9 +28,7 @@ static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
static const float INCHES_TO_MM = 25.4f;
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
-#if ENABLE_RETRACT_ACCELERATION
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
-#endif // ENABLE_RETRACT_ACCELERATION
static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
static const size_t MIN_EXTRUDERS_COUNT = 5;
@@ -184,10 +183,8 @@ void GCodeProcessor::TimeMachine::reset()
enabled = false;
acceleration = 0.0f;
max_acceleration = 0.0f;
-#if ENABLE_RETRACT_ACCELERATION
retract_acceleration = 0.0f;
max_retract_acceleration = 0.0f;
-#endif // ENABLE_RETRACT_ACCELERATION
travel_acceleration = 0.0f;
max_travel_acceleration = 0.0f;
extrude_factor_override_percentage = 1.0f;
@@ -347,18 +344,6 @@ void GCodeProcessor::TimeProcessor::reset()
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
-struct FilePtr {
- FilePtr(FILE *f) : f(f) {}
- ~FilePtr() { this->close(); }
- void close() {
- if (this->f) {
- ::fclose(this->f);
- this->f = nullptr;
- }
- }
- FILE* f = nullptr;
-};
-
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
{
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
@@ -740,9 +725,7 @@ void GCodeProcessor::Result::reset() {
extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
time = 0;
}
#else
@@ -756,18 +739,19 @@ void GCodeProcessor::Result::reset() {
extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
#endif // ENABLE_GCODE_VIEWER_STATISTICS
const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = {
- { EProducer::PrusaSlicer, "PrusaSlicer" },
- { EProducer::Slic3rPE, "Slic3r Prusa Edition" },
- { EProducer::Slic3r, "Slic3r" },
+ { EProducer::PrusaSlicer, "generated by PrusaSlicer" },
+ { EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
+ { EProducer::Slic3r, "generated by Slic3r" },
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ { EProducer::SuperSlicer, "generated by SuperSlicer" },
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
{ EProducer::Cura, "Cura_SteamEngine" },
- { EProducer::Simplify3D, "Simplify3D" },
+ { EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
{ EProducer::CraftWare, "CraftWare" },
{ EProducer::ideaMaker, "ideaMaker" },
{ EProducer::KissSlicer, "KISSlicer" }
@@ -849,26 +833,16 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.extruders_count = extruders_count;
m_extruder_offsets.resize(extruders_count);
- for (size_t i = 0; i < extruders_count; ++i) {
- Vec2f offset = config.extruder_offset.get_at(i).cast<float>();
- m_extruder_offsets[i] = { offset(0), offset(1), 0.0f };
- }
-
m_extruder_colors.resize(extruders_count);
- for (size_t i = 0; i < extruders_count; ++i) {
- m_extruder_colors[i] = static_cast<unsigned char>(i);
- }
-
+ m_result.filament_diameters.resize(extruders_count);
+ m_result.filament_densities.resize(extruders_count);
m_extruder_temps.resize(extruders_count);
- m_result.filament_diameters.resize(config.filament_diameter.values.size());
- for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) {
- m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
- }
-
- m_result.filament_densities.resize(config.filament_density.values.size());
- for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
- m_result.filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
+ for (size_t i = 0; i < extruders_count; ++ i) {
+ m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f);
+ m_extruder_colors[i] = static_cast<unsigned char>(i);
+ m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
+ m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
}
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
@@ -895,11 +869,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
-#if ENABLE_RETRACT_ACCELERATION
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
-#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -1117,11 +1089,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
-#if ENABLE_RETRACT_ACCELERATION
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
-#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -1200,9 +1170,7 @@ void GCodeProcessor::reset()
m_result.id = ++s_result_id;
m_use_volumetric_e = false;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_last_default_color_id = 0;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.reset();
@@ -1215,6 +1183,18 @@ void GCodeProcessor::reset()
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
}
+static inline const char* skip_whitespaces(const char *begin, const char *end) {
+ for (; begin != end && (*begin == ' ' || *begin == '\t'); ++ begin);
+ return begin;
+}
+
+static inline const char* remove_eols(const char *begin, const char *end) {
+ for (; begin != end && (*(end - 1) == '\r' || *(end - 1) == '\n'); -- end);
+ return end;
+}
+
+// Load a G-code into a stand-alone G-code viewer.
+// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
{
CNumericLocalesSetter locales_setter;
@@ -1226,14 +1206,16 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
// pre-processing
// parse the gcode file to detect its producer
{
- m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
- const std::string_view cmd = line.cmd();
- if (cmd.empty()) {
- const std::string_view comment = line.comment();
- if (comment.length() > 1 && detect_producer(comment))
+ m_parser.parse_file_raw(filename, [this](GCodeReader& reader, const char *begin, const char *end) {
+ begin = skip_whitespaces(begin, end);
+ if (begin != end && *begin == ';') {
+ // Comment.
+ begin = skip_whitespaces(++ begin, end);
+ end = remove_eols(begin, end);
+ if (begin != end && detect_producer(std::string_view(begin, end - begin)))
m_parser.quit_parsing();
}
- });
+ });
m_parser.reset();
// if the gcode was produced by PrusaSlicer,
@@ -1249,6 +1231,10 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
}
else if (m_producer == EProducer::Simplify3D)
apply_config_simplify3d(filename);
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ else if (m_producer == EProducer::SuperSlicer)
+ apply_config_superslicer(filename);
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
}
// process gcode
@@ -1267,7 +1253,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
this->process_gcode_line(line, true);
});
- this->finalize();
+ // Don't post-process the G-code to update time stamps.
+ this->finalize(false);
}
void GCodeProcessor::initialize(const std::string& filename)
@@ -1293,7 +1280,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
});
}
-void GCodeProcessor::finalize()
+void GCodeProcessor::finalize(bool post_process)
{
// update width/height of wipe moves
for (MoveVertex& move : m_result.moves) {
@@ -1323,7 +1310,8 @@ void GCodeProcessor::finalize()
m_width_compare.output();
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
- m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
+ if (post_process)
+ m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
#if ENABLE_GCODE_VIEWER_STATISTICS
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -1380,6 +1368,41 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
return ret;
}
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ConfigSubstitutions load_from_superslicer_gcode_file(const std::string& filename, DynamicPrintConfig& config, ForwardCompatibilitySubstitutionRule compatibility_rule)
+{
+ // for reference, see: ConfigBase::load_from_gcode_file()
+
+ boost::nowide::ifstream ifs(filename);
+
+ auto header_end_pos = ifs.tellg();
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+ size_t key_value_pairs = 0;
+
+ ifs.seekg(0, ifs.end);
+ auto file_length = ifs.tellg();
+ auto data_length = std::min<std::fstream::pos_type>(65535, file_length - header_end_pos);
+ ifs.seekg(file_length - data_length, ifs.beg);
+ std::vector<char> data(size_t(data_length) + 1, 0);
+ ifs.read(data.data(), data_length);
+ ifs.close();
+ key_value_pairs = ConfigBase::load_from_gcode_string_legacy(config, data.data(), substitutions_ctxt);
+
+ if (key_value_pairs < 80)
+ throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs));
+
+ return std::move(substitutions_ctxt.substitutions);
+}
+
+void GCodeProcessor::apply_config_superslicer(const std::string& filename)
+{
+ DynamicPrintConfig config;
+ config.apply(FullPrintConfig::defaults());
+ load_from_superslicer_gcode_file(filename, config, ForwardCompatibilitySubstitutionRule::EnableSilent);
+ apply_config(config);
+}
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
{
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
@@ -1398,9 +1421,11 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
};
BedSize bed_size;
+ bool producer_detected = false;
+
+ m_parser.parse_file_raw(filename, [this, &bed_size, &producer_detected](GCodeReader& reader, const char* begin, const char* end) {
- m_parser.parse_file(filename, [this, &bed_size](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
- auto extract_double = [](const std::string& cmt, const std::string& key, double& out) {
+ auto extract_double = [](const std::string_view cmt, const std::string& key, double& out) {
size_t pos = cmt.find(key);
if (pos != cmt.npos) {
pos = cmt.find(',', pos);
@@ -1412,12 +1437,12 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
return false;
};
- auto extract_floats = [](const std::string& cmt, const std::string& key, std::vector<float>& out) {
+ auto extract_floats = [](const std::string_view cmt, const std::string& key, std::vector<float>& out) {
size_t pos = cmt.find(key);
if (pos != cmt.npos) {
pos = cmt.find(',', pos);
if (pos != cmt.npos) {
- std::string data_str = cmt.substr(pos + 1);
+ const std::string_view data_str = cmt.substr(pos + 1);
std::vector<std::string> values_str;
boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
for (const std::string& s : values_str) {
@@ -1428,28 +1453,39 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
}
return false;
};
-
- const std::string& comment = line.raw();
- if (comment.length() > 2 && comment.front() == ';') {
- if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
- extract_double(comment, "strokeXoverride", bed_size.x);
- else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
- extract_double(comment, "strokeYoverride", bed_size.y);
- else if (comment.find("filamentDiameters") != comment.npos) {
- m_result.filament_diameters.clear();
- extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
- }
- else if (comment.find("filamentDensities") != comment.npos) {
- m_result.filament_densities.clear();
- extract_floats(comment, "filamentDensities", m_result.filament_densities);
- }
- else if (comment.find("extruderDiameter") != comment.npos) {
- std::vector<float> extruder_diameters;
- extract_floats(comment, "extruderDiameter", extruder_diameters);
- m_result.extruders_count = extruder_diameters.size();
+
+ begin = skip_whitespaces(begin, end);
+ end = remove_eols(begin, end);
+ if (begin != end)
+ if (*begin == ';') {
+ // Comment.
+ begin = skip_whitespaces(++ begin, end);
+ if (begin != end) {
+ std::string_view comment(begin, end - begin);
+ if (producer_detected) {
+ if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
+ extract_double(comment, "strokeXoverride", bed_size.x);
+ else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
+ extract_double(comment, "strokeYoverride", bed_size.y);
+ else if (comment.find("filamentDiameters") != comment.npos) {
+ m_result.filament_diameters.clear();
+ extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
+ } else if (comment.find("filamentDensities") != comment.npos) {
+ m_result.filament_densities.clear();
+ extract_floats(comment, "filamentDensities", m_result.filament_densities);
+ } else if (comment.find("extruderDiameter") != comment.npos) {
+ std::vector<float> extruder_diameters;
+ extract_floats(comment, "extruderDiameter", extruder_diameters);
+ m_result.extruders_count = extruder_diameters.size();
+ }
+ } else if (boost::starts_with(comment, "G-Code generated by Simplify3D(R)"))
+ producer_detected = true;
+ }
+ } else {
+ // Some non-empty G-code line detected, stop parsing config comments.
+ reader.quit_parsing();
}
- }
- });
+ });
if (m_result.extruders_count == 0)
m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
@@ -1741,7 +1777,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// color change tag
if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
unsigned char extruder_id = 0;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
static std::vector<std::string> Default_Colors = {
"#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish
"#1C8891", // { 0.110f, 0.533f, 0.569f },
@@ -1790,16 +1825,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
if (m_last_default_color_id == Default_Colors.size())
m_last_default_color_id = 0;
}
-#else
- if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
- int eid;
- if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) {
- BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
- return;
- }
- extruder_id = static_cast<unsigned char>(eid);
- }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (extruder_id < m_extruder_colors.size())
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
@@ -1810,7 +1835,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
if (m_extruder_id == extruder_id) {
m_cp_color.current = m_extruder_colors[extruder_id];
store_move_vertex(EMoveType::Color_change);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
@@ -1818,27 +1842,19 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
process_custom_gcode_time(CustomGCode::ColorChange);
process_filaments(CustomGCode::ColorChange);
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
-#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
- process_custom_gcode_time(CustomGCode::ColorChange);
- process_filaments(CustomGCode::ColorChange);
-#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-
return;
}
// pause print tag
if (comment == reserved_tag(ETags::Pause_Print)) {
store_move_vertex(EMoveType::Pause_Print);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.set();
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
process_custom_gcode_time(CustomGCode::PausePrint);
return;
}
@@ -1846,13 +1862,11 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// custom code tag
if (comment == reserved_tag(ETags::Custom_Code)) {
store_move_vertex(EMoveType::Custom_GCode);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.set();
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
return;
}
@@ -1878,6 +1892,9 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
{
case EProducer::Slic3rPE:
case EProducer::Slic3r:
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ case EProducer::SuperSlicer:
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
case EProducer::Cura: { return process_cura_tags(comment); }
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
@@ -2707,15 +2724,15 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line)
std::string_view cmd = line.cmd();
std::string new_line_raw = { cmd.data(), cmd.size() };
bool found = false;
- if (line.has_x()) {
+ if (line.has('X')) {
new_line_raw += " X0";
found = true;
}
- if (line.has_y()) {
+ if (line.has('Y')) {
new_line_raw += " Y0";
found = true;
}
- if (line.has_z()) {
+ if (line.has('Z')) {
new_line_raw += " Z0";
found = true;
}
@@ -2851,16 +2868,16 @@ void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md
// Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082
- if (line.has_x())
+ if (line.has('X'))
m_origin[X] = 0.0f;
- if (line.has_y())
+ if (line.has('Y'))
m_origin[Y] = 0.0f;
- if (line.has_z())
+ if (line.has('Z'))
m_origin[Z] = 0.0f;
- if (line.has_e())
+ if (line.has('E'))
m_origin[E] = 0.0f;
}
@@ -2943,22 +2960,14 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('T', value))
-#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
-#else
- set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
-#endif // ENABLE_RETRACT_ACCELERATION
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('R', value))
-#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
-#else
- set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
-#endif // ENABLE_RETRACT_ACCELERATION
if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
@@ -3029,7 +3038,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
// https://github.com/repetier/Repetier-Firmware/blob/master/src/ArduinoAVR/Repetier/Printer.cpp
// void Printer::GoToMemoryPosition(bool x, bool y, bool z, bool e, float feed)
- bool has_xyz = !(line.has_x() || line.has_y() || line.has_z());
+ bool has_xyz = !(line.has('X') || line.has('Y') || line.has('Z'));
float p = FLT_MAX;
for (unsigned char a = X; a <= Z; ++a) {
@@ -3216,20 +3225,12 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
}
}
-#if ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
}
-#else
-float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
-{
- return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
-}
-#endif // ENABLE_RETRACT_ACCELERATION
-#if ENABLE_RETRACT_ACCELERATION
void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{
size_t id = static_cast<size_t>(mode);
@@ -3239,7 +3240,6 @@ void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMod
std::min(value, m_time_processor.machines[id].max_retract_acceleration);
}
}
-#endif // ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 040f7432a..fce888233 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -242,11 +242,9 @@ namespace Slic3r {
float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2
-#if ENABLE_RETRACT_ACCELERATION
float retract_acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_retract_acceleration; // mm/s^2
-#endif // ENABLE_RETRACT_ACCELERATION
float travel_acceleration; // mm/s^2
// hard limit for the travel acceleration, to which the firmware will clamp.
float max_travel_acceleration; // mm/s^2
@@ -359,9 +357,7 @@ namespace Slic3r {
std::vector<float> filament_diameters;
std::vector<float> filament_densities;
PrintEstimatedStatistics print_statistics;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 };
@@ -536,9 +532,7 @@ namespace Slic3r {
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
OptionsZCorrector m_options_z_corrector;
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
size_t m_last_default_color_id;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -549,6 +543,9 @@ namespace Slic3r {
PrusaSlicer,
Slic3rPE,
Slic3r,
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ SuperSlicer,
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
Cura,
Simplify3D,
CraftWare,
@@ -585,14 +582,14 @@ namespace Slic3r {
const Result& get_result() const { return m_result; }
Result&& extract_result() { return std::move(m_result); }
- // Process the gcode contained in the file with the given filename
+ // Load a G-code into a stand-alone G-code viewer.
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
// Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
void initialize(const std::string& filename);
void process_buffer(const std::string& buffer);
- void finalize();
+ void finalize(bool post_process);
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
@@ -605,6 +602,9 @@ namespace Slic3r {
private:
void apply_config(const DynamicPrintConfig& config);
void apply_config_simplify3d(const std::string& filename);
+#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
+ void apply_config_superslicer(const std::string& filename);
+#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
// Process tags embedded into comments
@@ -724,9 +724,7 @@ namespace Slic3r {
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
-#if ENABLE_RETRACT_ACCELERATION
void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
-#endif // ENABLE_RETRACT_ACCELERATION
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index 61ab10f22..7b106463a 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -2,9 +2,11 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/nowide/fstream.hpp>
+#include <boost/nowide/cstdio.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
+#include "Utils.hpp"
#include "LocalesUtils.hpp"
@@ -126,44 +128,92 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
}
}
-bool GCodeReader::parse_file(const std::string &file, callback_t callback)
+template<typename ParseLineCallback, typename LineEndCallback>
+bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
{
- boost::nowide::ifstream f(file);
- f.sync_with_stdio(false);
+ FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
+
+ // Read the input stream 64kB at a time, extract lines and process them.
std::vector<char> buffer(65536 * 10, 0);
- std::string line;
+ // Line buffer.
+ std::string gcode_line;
+ size_t file_pos = 0;
m_parsing = true;
- GCodeLine gline;
- while (m_parsing && ! f.eof()) {
- f.read(buffer.data(), buffer.size());
- if (! f.eof() && ! f.good())
- // Reading the input file failed.
+ for (;;) {
+ size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
+ if (::ferror(in.f))
return false;
+ bool eof = cnt_read == 0;
auto it = buffer.begin();
- auto it_bufend = buffer.begin() + f.gcount();
- while (it != it_bufend) {
- bool eol = false;
+ auto it_bufend = buffer.begin() + cnt_read;
+ while (it != it_bufend || (eof && ! gcode_line.empty())) {
+ // Find end of line.
+ bool eol = false;
auto it_end = it;
- for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
- eol |= f.eof() && it_end == it_bufend;
+ for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end)
+ if (*it_end == '\n')
+ line_end_callback((it_end - buffer.begin()) + 1);
+ // End of line is indicated also if end of file was reached.
+ eol |= eof && it_end == it_bufend;
if (eol) {
- gline.reset();
- if (line.empty())
- this->parse_line(&(*it), &(*it_end), gline, callback);
+ if (gcode_line.empty())
+ parse_line_callback(&(*it), &(*it_end));
else {
- line.insert(line.end(), it, it_end);
- this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
- line.clear();
+ gcode_line.insert(gcode_line.end(), it, it_end);
+ parse_line_callback(gcode_line.c_str(), gcode_line.c_str() + gcode_line.size());
+ gcode_line.clear();
}
+ if (! m_parsing)
+ // The callback wishes to exit.
+ return true;
} else
- line.insert(line.end(), it, it_end);
- // Skip all the empty lines.
- for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
+ gcode_line.insert(gcode_line.end(), it, it_end);
+ // Skip EOL.
+ it = it_end;
+ if (it != it_bufend && *it == '\r')
+ ++ it;
+ if (it != it_bufend && *it == '\n') {
+ line_end_callback((it - buffer.begin()) + 1);
+ ++ it;
+ }
}
+ if (eof)
+ break;
+ file_pos += cnt_read;
}
return true;
}
+template<typename ParseLineCallback, typename LineEndCallback>
+bool GCodeReader::parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
+{
+ GCodeLine gline;
+ return this->parse_file_raw_internal(filename,
+ [this, &gline, parse_line_callback](const char *begin, const char *end) {
+ gline.reset();
+ this->parse_line(begin, end, gline, parse_line_callback);
+ },
+ line_end_callback);
+}
+
+bool GCodeReader::parse_file(const std::string &file, callback_t callback)
+{
+ return this->parse_file_internal(file, callback, [](size_t){});
+}
+
+bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector<size_t> &lines_ends)
+{
+ lines_ends.clear();
+ return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); });
+}
+
+bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback)
+{
+ return this->parse_file_raw_internal(filename,
+ [this, line_callback](const char *begin, const char *end) { line_callback(*this, begin, end); },
+ [](size_t){});
+}
+
bool GCodeReader::GCodeLine::has(char axis) const
{
const char *c = m_raw.c_str();
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 15376c0fc..0ab268139 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -76,6 +76,7 @@ public:
};
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
+ typedef std::function<void(GCodeReader&, const char*, const char*)> raw_line_callback_t;
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
void reset() { memset(m_position, 0, sizeof(m_position)); }
@@ -114,6 +115,13 @@ public:
// Returns false if reading the file failed.
bool parse_file(const std::string &file, callback_t callback);
+ // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code
+ // as an overlay in the 3D scene.
+ bool parse_file(const std::string &file, callback_t callback, std::vector<size_t> &lines_ends);
+ // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed.
+ bool parse_file_raw(const std::string &file, raw_line_callback_t callback);
+
+ // To be called by the callback to stop parsing.
void quit_parsing() { m_parsing = false; }
float& x() { return m_position[X]; }
@@ -132,6 +140,11 @@ public:
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
private:
+ template<typename ParseLineCallback, typename LineEndCallback>
+ bool parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
+ template<typename ParseLineCallback, typename LineEndCallback>
+ bool parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
+
const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command);
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
@@ -154,6 +167,7 @@ private:
char m_extrusion_axis;
float m_position[NUM_AXES];
bool m_verbose;
+ // To be set by the callback to stop parsing.
bool m_parsing{ false };
};
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index eb45e95c1..f57e25406 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -1,7 +1,6 @@
#include "GCodeWriter.hpp"
#include "CustomGCode.hpp"
#include <algorithm>
-#include <charconv>
#include <iomanip>
#include <iostream>
#include <map>
@@ -11,15 +10,8 @@
#include <boost/spirit/include/karma.hpp>
#endif
-#define XYZF_EXPORT_DIGITS 3
-#define E_EXPORT_DIGITS 5
-
#define FLAVOR_IS(val) this->config.gcode_flavor == val
#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
-#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
-#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
-#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
-#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
namespace Slic3r {
@@ -261,115 +253,12 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
return gcode.str();
}
-class G1Writer {
-private:
- static constexpr const size_t buflen = 256;
- char buf[buflen];
- char *buf_end;
- std::to_chars_result ptr_err;
-
-public:
- G1Writer() {
- this->buf[0] = 'G';
- this->buf[1] = '1';
- this->buf_end = this->buf + buflen;
- this->ptr_err.ptr = this->buf + 2;
- }
-
- void emit_axis(const char axis, const double v, size_t digits) {
- assert(digits <= 6);
- static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
- *ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
-
- char *base_ptr = this->ptr_err.ptr;
- auto v_int = int64_t(std::round(v * pow_10[digits]));
- // Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
- // That is a little bit slower than std::to_chars but not much.
-#ifdef __APPLE__
- boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
-#else
- // this->buf_end minus 1 because we need space for adding the extra decimal point.
- this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
-#endif
- size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
- if (writen_digits < digits) {
- // Number is smaller than 10^digits, so that we will pad it with zeros.
- size_t remaining_digits = digits - writen_digits;
- // Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
- for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
- *to_ptr = *from_ptr;
-
- memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
- this->ptr_err.ptr += remaining_digits;
- }
-
- // Move all newly inserted chars by one to allocate space for a decimal point.
- for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
- *to_ptr = *from_ptr;
-
- *(this->ptr_err.ptr - digits) = '.';
- for (size_t i = 0; i < digits; ++i) {
- if (*this->ptr_err.ptr != '0')
- break;
- this->ptr_err.ptr--;
- }
- if (*this->ptr_err.ptr == '.')
- this->ptr_err.ptr--;
- if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
- *(++this->ptr_err.ptr) = '0';
- this->ptr_err.ptr++;
- }
-
- void emit_xy(const Vec2d &point) {
- this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
- this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
- }
-
- void emit_xyz(const Vec3d &point) {
- this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
- this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
- this->emit_z(point.z());
- }
-
- void emit_z(const double z) {
- this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
- }
-
- void emit_e(const std::string &axis, double v) {
- if (! axis.empty()) {
- // not gcfNoExtrusion
- this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
- }
- }
-
- void emit_f(double speed) {
- this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
- }
-
- void emit_string(const std::string &s) {
- strncpy(ptr_err.ptr, s.c_str(), s.size());
- ptr_err.ptr += s.size();
- }
-
- void emit_comment(bool allow_comments, const std::string &comment) {
- if (allow_comments && ! comment.empty()) {
- *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
- this->emit_string(comment);
- }
- }
-
- std::string string() {
- *ptr_err.ptr ++ = '\n';
- return std::string(this->buf, ptr_err.ptr - buf);
- }
-};
-
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
{
assert(F > 0.);
assert(F < 100000.);
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_f(F);
w.emit_comment(this->config.gcode_comments, comment);
w.emit_string(cooling_marker);
@@ -381,7 +270,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
m_pos(0) = point(0);
m_pos(1) = point(1);
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_xy(point);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@@ -414,7 +303,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
m_lifted = 0;
m_pos = point;
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_xyz(point);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@@ -448,7 +337,7 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
if (speed == 0.)
speed = this->config.travel_speed.value;
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_z(z);
w.emit_f(speed * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@@ -473,7 +362,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
m_pos(1) = point(1);
m_extruder->extrude(dE);
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_xy(point);
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment);
@@ -486,7 +375,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
m_lifted = 0;
m_extruder->extrude(dE);
- G1Writer w;
+ GCodeG1Formatter w;
w.emit_xyz(point);
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment);
@@ -517,12 +406,11 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe)
std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment)
{
- std::ostringstream gcode;
-
/* If firmware retraction is enabled, we use a fake value of 1
since we ignore the actual configured retract_length which
might be 0, in which case the retraction logic gets skipped. */
- if (this->config.use_firmware_retraction) length = 1;
+ if (this->config.use_firmware_retraction)
+ length = 1;
// If we use volumetric E values we turn lengths into volumes */
if (this->config.use_volumetric_e) {
@@ -532,52 +420,48 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
restart_extra = restart_extra * area;
}
- double dE = m_extruder->retract(length, restart_extra);
- if (dE != 0) {
+
+ std::string gcode;
+ if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
if (this->config.use_firmware_retraction) {
- if (FLAVOR_IS(gcfMachinekit))
- gcode << "G22 ; retract\n";
- else
- gcode << "G10 ; retract\n";
+ gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
} else if (! m_extrusion_axis.empty()) {
- gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
- << " F" << XYZF_NUM(m_extruder->retract_speed() * 60.);
- COMMENT(comment);
- gcode << "\n";
+ GCodeG1Formatter w;
+ w.emit_e(m_extrusion_axis, m_extruder->E());
+ w.emit_f(m_extruder->retract_speed() * 60.);
+ w.emit_comment(this->config.gcode_comments, comment);
+ gcode = w.string();
}
}
if (FLAVOR_IS(gcfMakerWare))
- gcode << "M103 ; extruder off\n";
-
- return gcode.str();
+ gcode += "M103 ; extruder off\n";
+
+ return gcode;
}
std::string GCodeWriter::unretract()
{
- std::ostringstream gcode;
+ std::string gcode;
if (FLAVOR_IS(gcfMakerWare))
- gcode << "M101 ; extruder on\n";
+ gcode = "M101 ; extruder on\n";
- double dE = m_extruder->unretract();
- if (dE != 0) {
+ if (double dE = m_extruder->unretract(); dE != 0) {
if (this->config.use_firmware_retraction) {
- if (FLAVOR_IS(gcfMachinekit))
- gcode << "G23 ; unretract\n";
- else
- gcode << "G11 ; unretract\n";
- gcode << this->reset_e();
+ gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
+ gcode += this->reset_e();
} else if (! m_extrusion_axis.empty()) {
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
- gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
- << " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.);
- if (this->config.gcode_comments) gcode << " ; unretract";
- gcode << "\n";
+ GCodeG1Formatter w;
+ w.emit_e(m_extrusion_axis, m_extruder->E());
+ w.emit_f(m_extruder->deretract_speed() * 60.);
+ w.emit_comment(this->config.gcode_comments, " ; unretract");
+ gcode += w.string();
}
}
- return gcode.str();
+ return gcode;
}
/* If this method is called more than once before calling unlift(),
@@ -649,4 +533,49 @@ std::string GCodeWriter::set_fan(unsigned int speed) const
return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed);
}
+
+void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) {
+ assert(digits <= 6);
+ static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
+ *ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
+
+ char *base_ptr = this->ptr_err.ptr;
+ auto v_int = int64_t(std::round(v * pow_10[digits]));
+ // Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
+ // That is a little bit slower than std::to_chars but not much.
+#ifdef __APPLE__
+ boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
+#else
+ // this->buf_end minus 1 because we need space for adding the extra decimal point.
+ this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
+#endif
+ size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
+ if (writen_digits < digits) {
+ // Number is smaller than 10^digits, so that we will pad it with zeros.
+ size_t remaining_digits = digits - writen_digits;
+ // Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
+ for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
+ *to_ptr = *from_ptr;
+
+ memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
+ this->ptr_err.ptr += remaining_digits;
+ }
+
+ // Move all newly inserted chars by one to allocate space for a decimal point.
+ for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
+ *to_ptr = *from_ptr;
+
+ *(this->ptr_err.ptr - digits) = '.';
+ for (size_t i = 0; i < digits; ++i) {
+ if (*this->ptr_err.ptr != '0')
+ break;
+ this->ptr_err.ptr--;
+ }
+ if (*this->ptr_err.ptr == '.')
+ this->ptr_err.ptr--;
+ if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
+ *(++this->ptr_err.ptr) = '0';
+ this->ptr_err.ptr++;
}
+
+} // namespace Slic3r
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index dd602ca80..6e2c08d3b 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -3,6 +3,7 @@
#include "libslic3r.h"
#include <string>
+#include <charconv>
#include "Extruder.hpp"
#include "Point.hpp"
#include "PrintConfig.hpp"
@@ -93,6 +94,84 @@ private:
std::string _retract(double length, double restart_extra, const std::string &comment);
};
+class GCodeFormatter {
+public:
+ GCodeFormatter() {
+ this->buf_end = buf + buflen;
+ this->ptr_err.ptr = this->buf;
+ }
+
+ GCodeFormatter(const GCodeFormatter&) = delete;
+ GCodeFormatter& operator=(const GCodeFormatter&) = delete;
+
+ static constexpr const int XYZF_EXPORT_DIGITS = 3;
+ static constexpr const int E_EXPORT_DIGITS = 5;
+
+ void emit_axis(const char axis, const double v, size_t digits);
+
+ void emit_xy(const Vec2d &point) {
+ this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+ this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_xyz(const Vec3d &point) {
+ this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+ this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+ this->emit_z(point.z());
+ }
+
+ void emit_z(const double z) {
+ this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_e(const std::string &axis, double v) {
+ if (! axis.empty()) {
+ // not gcfNoExtrusion
+ this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
+ }
+ }
+
+ void emit_f(double speed) {
+ this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_string(const std::string &s) {
+ strncpy(ptr_err.ptr, s.c_str(), s.size());
+ ptr_err.ptr += s.size();
+ }
+
+ void emit_comment(bool allow_comments, const std::string &comment) {
+ if (allow_comments && ! comment.empty()) {
+ *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
+ this->emit_string(comment);
+ }
+ }
+
+ std::string string() {
+ *ptr_err.ptr ++ = '\n';
+ return std::string(this->buf, ptr_err.ptr - buf);
+ }
+
+protected:
+ static constexpr const size_t buflen = 256;
+ char buf[buflen];
+ char* buf_end;
+ std::to_chars_result ptr_err;
+};
+
+class GCodeG1Formatter : public GCodeFormatter {
+public:
+ GCodeG1Formatter() {
+ this->buf[0] = 'G';
+ this->buf[1] = '1';
+ this->buf_end = buf + buflen;
+ this->ptr_err.ptr = this->buf + 2;
+ }
+
+ GCodeG1Formatter(const GCodeG1Formatter&) = delete;
+ GCodeG1Formatter& operator=(const GCodeG1Formatter&) = delete;
+};
+
} /* namespace Slic3r */
#endif /* slic3r_GCodeWriter_hpp_ */
diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp
index 64ab700ed..d32107233 100644
--- a/src/libslic3r/LocalesUtils.cpp
+++ b/src/libslic3r/LocalesUtils.cpp
@@ -1,7 +1,13 @@
#include "LocalesUtils.hpp"
+#ifdef _WIN32
+ #include <charconv>
+#endif
#include <stdexcept>
+#include <fast_float/fast_float.h>
+
+
namespace Slic3r {
@@ -11,14 +17,14 @@ CNumericLocalesSetter::CNumericLocalesSetter()
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
std::setlocale(LC_NUMERIC, "C");
-#elif __linux__
+#elif __APPLE__
m_original_locale = uselocale((locale_t)0);
- m_new_locale = duplocale(m_original_locale);
- m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
+ m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
uselocale(m_new_locale);
-#else // APPLE
+#else // linux / BSD
m_original_locale = uselocale((locale_t)0);
- m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
+ m_new_locale = duplocale(m_original_locale);
+ m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
uselocale(m_new_locale);
#endif
}
@@ -45,28 +51,37 @@ bool is_decimal_separator_point()
}
-double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/)
+double string_to_double_decimal_point(const std::string_view str, size_t* pos /* = nullptr*/)
{
double out;
- std::istringstream stream(str);
- if (! (stream >> out))
- throw std::invalid_argument("string_to_double_decimal_point conversion failed.");
- if (pos) {
- if (stream.eof())
- *pos = str.size();
- else
- *pos = stream.tellg();
- }
+ size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data();
+ if (pos)
+ *pos = p;
return out;
}
std::string float_to_string_decimal_point(double value, int precision/* = -1*/)
{
+ // Our Windows build server fully supports C++17 std::to_chars. Let's use it.
+ // Other platforms are behind, fall back to slow stringstreams for now.
+#ifdef _WIN32
+ constexpr size_t SIZE = 20;
+ char out[SIZE] = "";
+ std::to_chars_result res;
+ if (precision >=0)
+ res = std::to_chars(out, out+SIZE, value, std::chars_format::fixed, precision);
+ else
+ res = std::to_chars(out, out+SIZE, value, std::chars_format::general, 6);
+ if (res.ec == std::errc::value_too_large)
+ throw std::invalid_argument("float_to_string_decimal_point conversion failed.");
+ return std::string(out, res.ptr - out);
+#else
std::stringstream buf;
if (precision >= 0)
buf << std::fixed << std::setprecision(precision);
buf << value;
return buf.str();
+#endif
}
diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp
index 113cfa04b..f63c3572f 100644
--- a/src/libslic3r/LocalesUtils.hpp
+++ b/src/libslic3r/LocalesUtils.hpp
@@ -5,6 +5,7 @@
#include <clocale>
#include <iomanip>
#include <cassert>
+#include <string_view>
#ifdef __APPLE__
#include <xlocale.h>
@@ -40,7 +41,7 @@ bool is_decimal_separator_point();
// (We use user C locales and "C" C++ locales in most of the code.)
std::string float_to_string_decimal_point(double value, int precision = -1);
//std::string float_to_string_decimal_point(float value, int precision = -1);
-double string_to_double_decimal_point(const std::string& str, size_t* pos = nullptr);
+double string_to_double_decimal_point(const std::string_view str, size_t* pos = nullptr);
} // namespace Slic3r
diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp
index a59165946..25250e234 100644
--- a/src/libslic3r/MeshBoolean.cpp
+++ b/src/libslic3r/MeshBoolean.cpp
@@ -29,18 +29,17 @@ TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
{
auto &VC = emesh.first; auto &FC = emesh.second;
- Pointf3s points(size_t(VC.rows()));
- std::vector<Vec3i> facets(size_t(FC.rows()));
+ indexed_triangle_set its;
+ its.vertices.reserve(size_t(VC.rows()));
+ its.indices.reserve(size_t(FC.rows()));
for (Eigen::Index i = 0; i < VC.rows(); ++i)
- points[size_t(i)] = VC.row(i);
+ its.vertices.emplace_back(VC.row(i).cast<float>());
for (Eigen::Index i = 0; i < FC.rows(); ++i)
- facets[size_t(i)] = FC.row(i);
+ its.indices.emplace_back(FC.row(i));
- TriangleMesh out{points, facets};
- out.require_shared_vertices();
- return out;
+ return TriangleMesh { std::move(its) };
}
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh)
@@ -131,28 +130,27 @@ void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
}
-inline Vec3d to_vec3d(const _EpicMesh::Point &v)
+inline Vec3f to_vec3f(const _EpicMesh::Point& v)
{
- return {v.x(), v.y(), v.z()};
+ return { float(v.x()), float(v.y()), float(v.z()) };
}
-inline Vec3d to_vec3d(const _EpecMesh::Point &v)
+inline Vec3f to_vec3f(const _EpecMesh::Point& v)
{
CGAL::Cartesian_converter<EpecKernel, EpicKernel> cvt;
auto iv = cvt(v);
- return {iv.x(), iv.y(), iv.z()};
+ return { float(iv.x()), float(iv.y()), float(iv.z()) };
}
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{
- Pointf3s points;
- std::vector<Vec3i> facets;
- points.reserve(cgalmesh.num_vertices());
- facets.reserve(cgalmesh.num_faces());
+ indexed_triangle_set its;
+ its.vertices.reserve(cgalmesh.num_vertices());
+ its.indices.reserve(cgalmesh.num_faces());
for (auto &vi : cgalmesh.vertices()) {
auto &v = cgalmesh.point(vi); // Don't ask...
- points.emplace_back(to_vec3d(v));
+ its.vertices.emplace_back(to_vec3f(v));
}
for (auto &face : cgalmesh.faces()) {
@@ -166,12 +164,10 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
}
if (i == 3)
- facets.emplace_back(facet);
+ its.indices.emplace_back(facet);
}
- TriangleMesh out{points, facets};
- out.repair();
- return out;
+ return TriangleMesh(std::move(its));
}
std::unique_ptr<CGALMesh, CGALMeshDeleter>
diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp
index 7233ac40d..c9df648fb 100644
--- a/src/libslic3r/MeshSplitImpl.hpp
+++ b/src/libslic3r/MeshSplitImpl.hpp
@@ -28,65 +28,84 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
}
};
-// Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
-// and return them.
+// Discover connected patches of facets one by one.
template<class NeighborIndex>
-std::vector<size_t> its_find_unvisited_neighbors(
- const indexed_triangle_set &its,
- const NeighborIndex & neighbor_index,
- std::vector<char> & visited)
-{
- using stack_el = size_t;
-
- auto facestack = reserve_vector<stack_el>(its.indices.size());
- auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); };
- auto pop = [&facestack] () -> stack_el {
- stack_el ret = facestack.back();
- facestack.pop_back();
- return ret;
- };
-
- // find the next unvisited facet and push the index
- auto facet = std::find(visited.begin(), visited.end(), false);
- std::vector<size_t> ret;
-
- if (facet != visited.end()) {
- ret.reserve(its.indices.size());
- auto idx = size_t(facet - visited.begin());
- push(idx);
- ret.emplace_back(idx);
- visited[idx] = true;
+struct NeighborVisitor {
+ NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
+ its(its), neighbor_index(neighbor_index) {
+ m_visited.assign(its.indices.size(), false);
+ m_facestack.reserve(its.indices.size());
+ }
+ NeighborVisitor(const indexed_triangle_set &its, NeighborIndex &&aneighbor_index) :
+ its(its), neighbor_index(m_neighbor_index_data), m_neighbor_index_data(std::move(aneighbor_index)) {
+ m_visited.assign(its.indices.size(), false);
+ m_facestack.reserve(its.indices.size());
}
- while (!facestack.empty()) {
- size_t facet_idx = pop();
- const auto &neighbors = neighbor_index[facet_idx];
- for (auto neighbor_idx : neighbors) {
- if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) {
- visited[size_t(neighbor_idx)] = true;
- push(stack_el(neighbor_idx));
- ret.emplace_back(size_t(neighbor_idx));
+ template<typename Visitor>
+ void visit(Visitor visitor)
+ {
+ // find the next unvisited facet and push the index
+ auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
+ m_seed = facet - m_visited.begin();
+
+ if (facet != m_visited.end()) {
+ // Skip this element in the next round.
+ auto idx = m_seed ++;
+ if (! visitor(idx))
+ return;
+ this->push(idx);
+ m_visited[idx] = true;
+ while (! m_facestack.empty()) {
+ size_t facet_idx = this->pop();
+ for (auto neighbor_idx : neighbor_index[facet_idx]) {
+ assert(neighbor_idx < int(m_visited.size()));
+ if (neighbor_idx >= 0 && !m_visited[neighbor_idx]) {
+ if (! visitor(size_t(neighbor_idx)))
+ return;
+ m_visited[neighbor_idx] = true;
+ this->push(stack_el(neighbor_idx));
+ }
+ }
}
}
}
- return ret;
-}
+ const indexed_triangle_set &its;
+ const NeighborIndex &neighbor_index;
+
+private:
+ // If initialized with &&neighbor_index, take the ownership of the data.
+ const NeighborIndex m_neighbor_index_data;
+
+ std::vector<char> m_visited;
+
+ using stack_el = size_t;
+ std::vector<stack_el> m_facestack;
+ void push(const stack_el &s) { m_facestack.emplace_back(s); }
+ stack_el pop() { stack_el ret = m_facestack.back(); m_facestack.pop_back(); return ret; }
+
+ // Last face visited.
+ size_t m_seed { 0 };
+};
} // namespace meshsplit_detail
+// Funky wrapper for timinig of its_split() using various neighbor index creating methods, see sandboxes/its_neighbor_index/main.cpp
template<class IndexT> struct ItsNeighborsWrapper
{
using Index = IndexT;
- const indexed_triangle_set *its;
- IndexT index;
+ const indexed_triangle_set &its;
+ const IndexT &index_ref;
+ const IndexT index;
- ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx)
- : its{&m}, index{std::move(idx)}
- {}
+ // Keeping a reference to index, the caller is responsible for keeping the index alive.
+ ItsNeighborsWrapper(const indexed_triangle_set &its, const IndexT &index) : its{its}, index_ref{index} {}
+ // Taking ownership of the index.
+ ItsNeighborsWrapper(const indexed_triangle_set &its, IndexT &&aindex) : its{its}, index_ref{index}, index(std::move(aindex)) {}
- const auto& get_its() const noexcept { return *its; }
- const auto& get_index() const noexcept { return index; }
+ const auto& get_its() const noexcept { return its; }
+ const auto& get_index() const noexcept { return index_ref; }
};
// Splits a mesh into multiple meshes when possible.
@@ -97,20 +116,19 @@ void its_split(const Its &m, OutputIt out_it)
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
- std::vector<char> visited(its.indices.size(), false);
-
struct VertexConv {
size_t part_id = std::numeric_limits<size_t>::max();
size_t vertex_image;
};
std::vector<VertexConv> vidx_conv(its.vertices.size());
- const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
-
+ meshsplit_detail::NeighborVisitor visitor(its, meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
+
+ std::vector<size_t> facets;
for (size_t part_id = 0;; ++part_id) {
- std::vector<size_t> facets =
- its_find_unvisited_neighbors(its, neighbor_index, visited);
-
+ // Collect all faces of the next patch.
+ facets.clear();
+ visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
if (facets.empty())
break;
@@ -150,17 +168,34 @@ std::vector<indexed_triangle_set> its_split(const Its &its)
return ret;
}
-template<class Its> bool its_is_splittable(const Its &m)
+template<class Its>
+bool its_is_splittable(const Its &m)
{
- using namespace meshsplit_detail;
- const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
- const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
-
- std::vector<char> visited(its.indices.size(), false);
- its_find_unvisited_neighbors(its, neighbor_index, visited);
- auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited);
+ meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
+ bool has_some = false;
+ bool has_some2 = false;
+ // Traverse the 1st patch fully.
+ visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
+ if (has_some)
+ // Just check whether there is any face of the 2nd patch.
+ visitor.visit([&has_some2](size_t idx) { has_some2 = true; return false; });
+ return has_some && has_some2;
+}
- return !faces.empty();
+template<class Its>
+size_t its_number_of_patches(const Its &m)
+{
+ meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
+ size_t num_patches = 0;
+ for (;;) {
+ bool has_some = false;
+ // Traverse the 1st patch fully.
+ visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
+ if (! has_some)
+ break;
+ ++ num_patches;
+ }
+ return num_patches;
}
template<class ExPolicy>
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index fb6ab5635..b42dfb6a4 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -9,7 +9,6 @@
#include "Format/AMF.hpp"
#include "Format/OBJ.hpp"
-#include "Format/PRUS.hpp"
#include "Format/STL.hpp"
#include "Format/3mf.hpp"
@@ -118,8 +117,6 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
else if (boost::algorithm::iends_with(input_file, ".3mf"))
//FIXME options & LoadAttribute::CheckVersion ?
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
- else if (boost::algorithm::iends_with(input_file, ".prusa"))
- result = load_prus(input_file.c_str(), &model);
else
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
@@ -478,10 +475,10 @@ bool Model::looks_like_imperial_units() const
void Model::convert_from_imperial_units(bool only_small_volumes)
{
- static constexpr const double in_to_mm = 25.4;
+ static constexpr const float in_to_mm = 25.4f;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) {
- obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
+ obj->scale_mesh_after_creation(in_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_meters);
v->source.is_converted_from_inches = true;
@@ -508,7 +505,7 @@ void Model::convert_from_meters(bool only_small_volumes)
static constexpr const double m_to_mm = 1000;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) {
- obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
+ obj->scale_mesh_after_creation(m_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_inches);
v->source.is_converted_from_meters = true;
@@ -1065,11 +1062,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
+void ModelObject::scale_mesh_after_creation(const float scale)
{
for (ModelVolume *v : this->volumes) {
- v->scale_geometry_after_creation(versor);
- v->set_offset(versor.cwiseProduct(v->get_offset()));
+ v->scale_geometry_after_creation(scale);
+ v->set_offset(Vec3d(scale, scale, scale).cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
}
@@ -1080,9 +1077,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
ModelObject* new_object = new_clone(*this);
- double koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4 : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787 :
- conv_type == ConversionType::CONV_FROM_METER ? 1000 : conv_type == ConversionType::CONV_TO_METER ? 0.001 : 1;
- const Vec3d versor = Vec3d(koef, koef, koef);
+ float koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4f : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787f :
+ conv_type == ConversionType::CONV_FROM_METER ? 1000.f : conv_type == ConversionType::CONV_TO_METER ? 0.001f : 1.f;
new_object->set_model(nullptr);
new_object->sla_support_points.clear();
@@ -1095,7 +1091,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
for (ModelVolume* volume : volumes) {
if (!volume->mesh().empty()) {
TriangleMesh mesh(volume->mesh());
- mesh.require_shared_vertices();
ModelVolume* vol = new_object->add_volume(mesh);
vol->name = volume->name;
@@ -1121,8 +1116,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
- vol->scale_geometry_after_creation(versor);
- vol->set_offset(versor.cwiseProduct(volume->get_offset()));
+ vol->scale_geometry_after_creation(koef);
+ vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH)
vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
@@ -1154,7 +1149,7 @@ size_t ModelObject::facets_count() const
size_t num = 0;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
- num += v->mesh().stl.stats.number_of_facets;
+ num += v->mesh().facets_count();
return num;
}
@@ -1167,14 +1162,6 @@ size_t ModelObject::parts_count() const
return num;
}
-bool ModelObject::needed_repair() const
-{
- for (const ModelVolume *v : this->volumes)
- if (v->is_model_part() && v->mesh().needed_repair())
- return true;
- return false;
-}
-
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
@@ -1256,21 +1243,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
- mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
- if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
+ if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
- upper_mesh.repair();
- upper_mesh.reset_repair_stats();
- }
- if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
+ if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
- lower_mesh.repair();
- lower_mesh.reset_repair_stats();
- }
}
- if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper_mesh.facets_count() > 0) {
+ if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@@ -1279,7 +1259,7 @@ 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());
}
- if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower_mesh.facets_count() > 0) {
+ if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@@ -1349,24 +1329,22 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (volume->type() != ModelVolumeType::MODEL_PART)
continue;
- TriangleMeshPtrs meshptrs = volume->mesh().split();
+ std::vector<TriangleMesh> meshes = volume->mesh().split();
size_t counter = 1;
- for (TriangleMesh* mesh : meshptrs) {
-
+ for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied
- if (mesh->facets_count() < 3) continue;
-
- mesh->repair();
+ if (mesh.facets_count() < 3)
+ continue;
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
- if (meshptrs.size() == 1) {
+ if (meshes.size() == 1) {
new_object->name = volume->name;
// Don't copy the config's ID.
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
}
else {
- new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
+ new_object->name = this->name + (meshes.size() > 1 ? "_" + std::to_string(counter++) : "");
// Don't copy the config's ID.
new_object->config.assign_config(this->config);
}
@@ -1375,7 +1353,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
new_object->instances.reserve(this->instances.size());
for (const ModelInstance* model_instance : this->instances)
new_object->add_instance(*model_instance);
- ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
+ ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
for (ModelInstance* model_instance : new_object->instances)
{
@@ -1387,7 +1365,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// reset the source to disable reload from disk
new_vol->source = ModelVolume::Source();
new_objects->emplace_back(new_object);
- delete mesh;
}
}
}
@@ -1405,7 +1382,6 @@ void ModelObject::merge()
for (ModelVolume* volume : volumes)
if (!volume->mesh().empty())
mesh.merge(volume->mesh());
- mesh.repair();
this->clear_volumes();
ModelVolume* vol = this->add_volume(mesh);
@@ -1508,9 +1484,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
const Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
- for (const stl_facet &facet : hull.stl.facet_start)
+ for (const stl_triangle_vertex_indices& facet : hull.its.indices)
for (int i = 0; i < 3; ++ i)
- min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
+ min_z = std::min(min_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
}
return min_z + inst->get_offset(Z);
@@ -1529,9 +1505,9 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
const Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
- for (const stl_facet& facet : hull.stl.facet_start)
+ for (const stl_triangle_vertex_indices& facet : hull.its.indices)
for (int i = 0; i < 3; ++i)
- max_z = std::max(max_z, (mv * facet.vertex[i].cast<double>()).z());
+ max_z = std::max(max_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
}
return max_z + inst->get_offset(Z);
@@ -1572,7 +1548,6 @@ void ModelObject::print_info() const
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
- mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size();
cout << "size_x = " << size(0) << endl;
@@ -1584,27 +1559,26 @@ void ModelObject::print_info() const
cout << "max_x = " << bb.max(0) << endl;
cout << "max_y = " << bb.max(1) << endl;
cout << "max_z = " << bb.max(2) << endl;
- cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl;
- cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
+ cout << "number_of_facets = " << mesh.facets_count() << endl;
+
+ cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
+ if (! mesh.stats().manifold())
+ cout << "open_edges = " << mesh.stats().open_edges << endl;
- mesh.repair(); // this calculates number_of_parts
- if (mesh.needed_repair()) {
- mesh.repair();
- if (mesh.stl.stats.degenerate_facets > 0)
- cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl;
- if (mesh.stl.stats.edges_fixed > 0)
- cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl;
- if (mesh.stl.stats.facets_removed > 0)
- cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl;
- if (mesh.stl.stats.facets_added > 0)
- cout << "facets_added = " << mesh.stl.stats.facets_added << endl;
- if (mesh.stl.stats.facets_reversed > 0)
- cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl;
- if (mesh.stl.stats.backwards_edges > 0)
- cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl;
- }
- cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl;
- cout << "volume = " << mesh.volume() << endl;
+ if (mesh.stats().repaired()) {
+ if (mesh.stats().degenerate_facets > 0)
+ cout << "degenerate_facets = " << mesh.stats().degenerate_facets << endl;
+ if (mesh.stats().edges_fixed > 0)
+ cout << "edges_fixed = " << mesh.stats().edges_fixed << endl;
+ if (mesh.stats().facets_removed > 0)
+ cout << "facets_removed = " << mesh.stats().facets_removed << endl;
+ if (mesh.stats().facets_reversed > 0)
+ cout << "facets_reversed = " << mesh.stats().facets_reversed << endl;
+ if (mesh.stats().backwards_edges > 0)
+ cout << "backwards_edges = " << mesh.stats().backwards_edges << endl;
+ }
+ cout << "number_of_parts = " << mesh.stats().number_of_parts << endl;
+ cout << "volume = " << mesh.volume() << endl;
}
std::string ModelObject::get_export_filename() const
@@ -1627,24 +1601,24 @@ std::string ModelObject::get_export_filename() const
return ret;
}
-stl_stats ModelObject::get_object_stl_stats() const
+TriangleMeshStats ModelObject::get_object_stl_stats() const
{
if (this->volumes.size() == 1)
- return this->volumes[0]->mesh().stl.stats;
+ return this->volumes[0]->mesh().stats();
- stl_stats full_stats;
+ TriangleMeshStats full_stats;
full_stats.volume = 0.f;
// fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes)
{
- const stl_stats& stats = volume->mesh().stl.stats;
+ const TriangleMeshStats& stats = volume->mesh().stats();
// initialize full_stats (for repaired errors)
+ full_stats.open_edges += stats.open_edges;
full_stats.degenerate_facets += stats.degenerate_facets;
full_stats.edges_fixed += stats.edges_fixed;
full_stats.facets_removed += stats.facets_removed;
- full_stats.facets_added += stats.facets_added;
full_stats.facets_reversed += stats.facets_reversed;
full_stats.backwards_edges += stats.backwards_edges;
@@ -1663,10 +1637,10 @@ int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count();
- const stl_stats& stats = get_object_stl_stats();
+ const TriangleMeshStats& stats = get_object_stl_stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
- stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+ stats.facets_reversed + stats.backwards_edges;
}
void ModelVolume::set_material_id(t_model_material_id material_id)
@@ -1730,14 +1704,15 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
void ModelVolume::calculate_convex_hull()
{
m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
+ assert(m_convex_hull.get());
}
int ModelVolume::get_mesh_errors_count() const
{
- const stl_stats& stats = this->mesh().stl.stats;
+ const TriangleMeshStats &stats = this->mesh().stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
- stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+ stats.facets_reversed + stats.backwards_edges;
}
const TriangleMesh& ModelVolume::get_convex_hull() const
@@ -1785,11 +1760,9 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders)
{
- TriangleMeshPtrs meshptrs = this->mesh().split();
- if (meshptrs.size() <= 1) {
- delete meshptrs.front();
+ std::vector<TriangleMesh> meshes = this->mesh().split();
+ if (meshes.size() <= 1)
return 1;
- }
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
@@ -1798,15 +1771,14 @@ size_t ModelVolume::split(unsigned int max_extruders)
unsigned int extruder_counter = 0;
Vec3d offset = this->get_offset();
- for (TriangleMesh *mesh : meshptrs) {
- mesh->repair();
- if (mesh->empty())
+ for (TriangleMesh &mesh : meshes) {
+ if (mesh.empty())
// Repair may have removed unconnected triangles, thus emptying the mesh.
continue;
if (idx == 0)
{
- this->set_mesh(std::move(*mesh));
+ this->set_mesh(std::move(mesh));
this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id();
@@ -1814,7 +1786,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->source = ModelVolume::Source();
}
else
- this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
+ this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(mesh)));
this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
this->object->volumes[ivolume]->center_geometry_after_creation();
@@ -1822,7 +1794,6 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
this->object->volumes[ivolume]->m_is_splittable = 0;
- delete mesh;
++ idx;
}
@@ -1891,7 +1862,7 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
+void ModelVolume::scale_geometry_after_creation(const Vec3f& versor)
{
const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
@@ -1924,8 +1895,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
void ModelVolume::convert_from_imperial_units()
{
assert(! this->source.is_converted_from_meters);
- double in_to_mm = 25.4;
- this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
+ this->scale_geometry_after_creation(25.4f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_inches = true;
}
@@ -1933,8 +1903,7 @@ void ModelVolume::convert_from_imperial_units()
void ModelVolume::convert_from_meters()
{
assert(! this->source.is_converted_from_inches);
- double m_to_mm = 1000;
- this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
+ this->scale_geometry_after_creation(1000.f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_meters = true;
}
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index ba3156139..ea1d0ed17 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -346,13 +346,12 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_mesh_after_creation(const Vec3d& versor);
+ void scale_mesh_after_creation(const float scale);
void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector<int> volume_idxs);
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
- bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
void merge();
@@ -376,7 +375,7 @@ public:
std::string get_export_filename() const;
// Get full stl statistics for all object's meshes
- stl_stats get_object_stl_stats() const;
+ TriangleMeshStats get_object_stl_stats() const;
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
@@ -620,6 +619,8 @@ public:
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
+ void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
+ void set_mesh(indexed_triangle_set &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
@@ -670,7 +671,8 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_geometry_after_creation(const Vec3d& versor);
+ void scale_geometry_after_creation(const Vec3f &versor);
+ void scale_geometry_after_creation(const float scale) { this->scale_geometry_after_creation(Vec3f(scale, scale, scale)); }
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
@@ -774,7 +776,7 @@ private:
assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id());
- if (mesh.stl.stats.number_of_facets > 1)
+ if (mesh.facets_count() > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
@@ -830,7 +832,7 @@ private:
assert(this->config.id() == other.config.id());
this->set_material_id(other.material_id());
this->config.set_new_unique_id();
- if (mesh.stl.stats.number_of_facets > 1)
+ if (mesh.facets_count() > 1)
calculate_convex_hull();
assert(this->config.id().valid());
assert(this->config.id() != other.config.id());
diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp
index 46b47832a..935348279 100644
--- a/src/libslic3r/MultiPoint.hpp
+++ b/src/libslic3r/MultiPoint.hpp
@@ -33,7 +33,9 @@ public:
void rotate(double angle, const Point &center);
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
- const Point& first_point() const { return this->points.front(); }
+ const Point& front() const { return this->points.front(); }
+ const Point& back() const { return this->points.back(); }
+ const Point& first_point() const { return this->front(); }
virtual const Point& last_point() const = 0;
virtual Lines lines() const = 0;
size_t size() const { return points.size(); }
diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp
index 7f351d259..b1f3a74bb 100644
--- a/src/libslic3r/Point.cpp
+++ b/src/libslic3r/Point.cpp
@@ -179,6 +179,15 @@ Point Point::projection_onto(const Line &line) const
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
}
+bool has_duplicate_points(std::vector<Point> &&pts)
+{
+ std::sort(pts.begin(), pts.end());
+ for (size_t i = 1; i < pts.size(); ++ i)
+ if (pts[i - 1] == pts[i])
+ return true;
+ return false;
+}
+
BoundingBox get_extents(const Points &pts)
{
return BoundingBox(pts);
diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp
index 20726270d..499ab073d 100644
--- a/src/libslic3r/Point.hpp
+++ b/src/libslic3r/Point.hpp
@@ -211,8 +211,34 @@ inline Point lerp(const Point &a, const Point &b, double t)
return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
}
-extern BoundingBox get_extents(const Points &pts);
-extern BoundingBox get_extents(const std::vector<Points> &pts);
+BoundingBox get_extents(const Points &pts);
+BoundingBox get_extents(const std::vector<Points> &pts);
+
+// Test for duplicate points in a vector of points.
+// The points are copied, sorted and checked for duplicates globally.
+bool has_duplicate_points(std::vector<Point> &&pts);
+inline bool has_duplicate_points(const std::vector<Point> &pts)
+{
+ std::vector<Point> cpy = pts;
+ return has_duplicate_points(std::move(cpy));
+}
+
+// Test for duplicate points in a vector of points.
+// Only successive points are checked for equality.
+inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
+{
+ for (size_t i = 1; i < pts.size(); ++ i)
+ if (pts[i - 1] == pts[i])
+ return true;
+ return false;
+}
+
+// Test for duplicate points in a vector of points.
+// Only successive points are checked for equality. Additionally, first and last points are compared for equality.
+inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts)
+{
+ return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
+}
namespace int128 {
// Exact orientation predicate,
@@ -418,7 +444,7 @@ template<class Tout = double,
class = FloatingOnly<Tout>>
inline constexpr Tout unscaled(const Tin &v) noexcept
{
- return Tout(v * Tout(SCALING_FACTOR));
+ return Tout(v) * Tout(SCALING_FACTOR);
}
// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
@@ -432,7 +458,7 @@ template<class Tout = double,
inline constexpr Eigen::Matrix<Tout, N, EigenArgs...>
unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept
{
- return v.template cast<Tout>() * SCALING_FACTOR;
+ return v.template cast<Tout>() * Tout(SCALING_FACTOR);
}
// Align a coordinate to a grid. The coordinate may be negative,
diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp
index 5b180afca..42a6cc209 100644
--- a/src/libslic3r/Polygon.cpp
+++ b/src/libslic3r/Polygon.cpp
@@ -334,6 +334,27 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
return out;
}
+bool has_duplicate_points(const Polygons &polys)
+{
+#if 1
+ // Check globally.
+ size_t cnt = 0;
+ for (const Polygon &poly : polys)
+ cnt += poly.points.size();
+ std::vector<Point> allpts;
+ allpts.reserve(cnt);
+ for (const Polygon &poly : polys)
+ allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
+ return has_duplicate_points(std::move(allpts));
+#else
+ // Check per contour.
+ for (const Polygon &poly : polys)
+ if (has_duplicate_points(poly))
+ return true;
+ return false;
+#endif
+}
+
static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
{
Point v1 = p2 - p1;
diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp
index 333f1e6b1..29800a31d 100644
--- a/src/libslic3r/Polygon.hpp
+++ b/src/libslic3r/Polygon.hpp
@@ -18,11 +18,6 @@ using ConstPolygonPtrs = std::vector<const Polygon*>;
class Polygon : public MultiPoint
{
public:
- explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
- explicit operator Polyline() const { return this->split_at_first_point(); }
- Point& operator[](Points::size_type idx) { return this->points[idx]; }
- const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
-
Polygon() = default;
virtual ~Polygon() = default;
explicit Polygon(const Points &points) : MultiPoint(points) {}
@@ -39,6 +34,9 @@ public:
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
+ Point& operator[](Points::size_type idx) { return this->points[idx]; }
+ const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
+
// last point == first point for polygons
const Point& last_point() const override { return this->points.front(); }
@@ -80,11 +78,16 @@ public:
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
-extern BoundingBox get_extents(const Polygon &poly);
-extern BoundingBox get_extents(const Polygons &polygons);
-extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
-extern BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
-extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
+BoundingBox get_extents(const Polygon &poly);
+BoundingBox get_extents(const Polygons &polygons);
+BoundingBox get_extents_rotated(const Polygon &poly, double angle);
+BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
+std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
+
+// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
+inline bool has_duplicate_points(Polygon &&poly) { return has_duplicate_points(std::move(poly.points)); }
+inline bool has_duplicate_points(const Polygon &poly) { return has_duplicate_points(poly.points); }
+bool has_duplicate_points(const Polygons &polys);
inline double total_length(const Polygons &polylines) {
double total = 0;
@@ -104,19 +107,19 @@ inline double area(const Polygons &polys)
}
// Remove sticks (tentacles with zero area) from the polygon.
-extern bool remove_sticks(Polygon &poly);
-extern bool remove_sticks(Polygons &polys);
+bool remove_sticks(Polygon &poly);
+bool remove_sticks(Polygons &polys);
// Remove polygons with less than 3 edges.
-extern bool remove_degenerate(Polygons &polys);
-extern bool remove_small(Polygons &polys, double min_area);
-extern void remove_collinear(Polygon &poly);
-extern void remove_collinear(Polygons &polys);
+bool remove_degenerate(Polygons &polys);
+bool remove_small(Polygons &polys, double min_area);
+void remove_collinear(Polygon &poly);
+void remove_collinear(Polygons &polys);
// Append a vector of polygons at the end of another vector of polygons.
-inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
+inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
-inline void polygons_append(Polygons &dst, Polygons &&src)
+inline void polygons_append(Polygons &dst, Polygons &&src)
{
if (dst.empty()) {
dst = std::move(src);
diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp
index 68c40fc20..1255d5473 100644
--- a/src/libslic3r/Polyline.cpp
+++ b/src/libslic3r/Polyline.cpp
@@ -10,20 +10,6 @@
namespace Slic3r {
-Polyline::operator Polylines() const
-{
- Polylines polylines;
- polylines.push_back(*this);
- return polylines;
-}
-
-Polyline::operator Line() const
-{
- if (this->points.size() > 2)
- throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line");
- return Line(this->points.front(), this->points.back());
-}
-
const Point& Polyline::leftmost_point() const
{
const Point *p = &this->points.front();
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index 758fc38cd..31e0b88d3 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -59,13 +59,11 @@ public:
src.points.clear();
}
}
-
- explicit operator Polylines() const;
- explicit operator Line() const;
+
const Point& last_point() const override { return this->points.back(); }
-
const Point& leftmost_point() const;
Lines lines() const override;
+
void clip_end(double distance);
void clip_start(double distance);
void extend_end(double distance);
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index f5c8235ed..09cd38468 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -1097,14 +1097,6 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
return m_idx_selected;
}
-// Save the preset under a new name. If the name is different from the old one,
-// a new preset is stored into the list of presets.
-// All presets are marked as not modified and the new preset is activated.
-//void PresetCollection::save_current_preset(const std::string &new_name);
-
-// Delete the current preset, activate the first visible preset.
-//void PresetCollection::delete_current_preset();
-
// Update a dirty flag of the current preset
// Return true if the dirty flag changed.
bool PresetCollection::update_dirty()
diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp
index c22599e38..e5e49fb47 100644
--- a/src/libslic3r/PresetBundle.hpp
+++ b/src/libslic3r/PresetBundle.hpp
@@ -143,7 +143,7 @@ public:
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const;
- // Save current preset of a required type under a new name. If the name is different from the old one,
+ // Save current preset of a provided type under a new name. If the name is different from the old one,
// Unselected option would be reverted to the beginning values
void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options);
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 06052a62f..b33e8bda2 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -397,7 +397,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
convex_hull.translate(instance.shift - print_object->center_offset());
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
for (size_t i = 0; i < convex_hulls_other.size(); ++i) {
- if (!intersection((Polygons)convex_hulls_other[i], (Polygons)convex_hull).empty()) {
+ if (! intersection(convex_hulls_other[i], convex_hull).empty()) {
if (polygons == nullptr)
return false;
else {
@@ -434,6 +434,8 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
}
+
+
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
std::string Print::validate(std::string* warning) const
{
@@ -526,42 +528,11 @@ std::string Print::validate(std::string* warning) const
}
if (has_custom_layering) {
- const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
if (idx_object == tallest_object_idx)
continue;
- const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
-
- // The comparison of the profiles is not just about element-wise equality, some layers may not be
- // explicitely included. Always remember z and height of last reference layer that in the vector
- // and compare to that. In case some layers are in the vectors multiple times, only the last entry is
- // taken into account and compared.
- size_t i = 0; // index into tested profile
- size_t j = 0; // index into reference profile
- coordf_t ref_z = -1.;
- coordf_t next_ref_z = layer_height_profile_tallest[0];
- coordf_t ref_height = -1.;
- while (i < layer_height_profile.size()) {
- coordf_t this_z = layer_height_profile[i];
- // find the last entry with this z
- while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
- i += 2;
-
- coordf_t this_height = layer_height_profile[i+1];
- if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
- ref_z = next_ref_z;
- do { // one layer can be in the vector several times
- ref_height = layer_height_profile_tallest[j+1];
- if (j+2 >= layer_height_profile_tallest.size())
- break;
- j += 2;
- next_ref_z = layer_height_profile_tallest[j];
- } while (ref_z == next_ref_z);
- }
- if (std::abs(this_height - ref_height) > EPSILON)
- return L("The Wipe tower is only supported if all objects have the same variable layer height");
- i += 2;
- }
+ if (layer_height_profiles[idx_object] != layer_height_profiles[tallest_object_idx])
+ return L("The Wipe tower is only supported if all objects have the same variable layer height");
}
}
}
@@ -812,7 +783,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
// Slicing process, running at a background thread.
void Print::process()
{
- name_tbb_thread_pool_threads();
+ name_tbb_thread_pool_threads_set_locale();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp
index 10960c535..da26f905c 100644
--- a/src/libslic3r/PrintApply.cpp
+++ b/src/libslic3r/PrintApply.cpp
@@ -1300,8 +1300,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders,
painting_extruders,
*print_object_regions,
- [&print_object, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
- update_apply_status(print_object.invalidate_state_by_config_options(old_config, new_config, diff_keys));
+ [&print_object, it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
+ for (auto it = it_print_object; it != it_print_object_end; ++it)
+ if ((*it)->m_shared_regions != nullptr)
+ update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
})) {
// Regions are valid, just keep them.
} else {
diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp
index c4b6b7cce..7de74035b 100644
--- a/src/libslic3r/PrintBase.cpp
+++ b/src/libslic3r/PrintBase.cpp
@@ -60,6 +60,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
DynamicConfig cfg;
if (config_override != nullptr)
cfg = *config_override;
+ cfg.set_key_value("version", new ConfigOptionString(std::string(SLIC3R_VERSION)));
PlaceholderParser::update_timestamp(cfg);
this->update_object_placeholders(cfg, default_ext);
if (! filename_base.empty()) {
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index d9a4f2670..74ef27bd7 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1447,7 +1447,7 @@ void PrintObject::bridge_over_infill()
Polygons to_bridge_pp = internal_solid;
// iterate through lower layers spanned by bridge_flow
- double bottom_z = layer->print_z - bridge_flow.height();
+ double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i];
diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp
index 1d61be85b..7db0de626 100644
--- a/src/libslic3r/PrintObjectSlice.cpp
+++ b/src/libslic3r/PrintObjectSlice.cpp
@@ -39,23 +39,6 @@ LayerPtrs new_layers(
return out;
}
-//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
-// This function will go away once we get rid of admesh from ModelVolume.
-static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
-{
- assert(mesh.repaired && mesh.has_shared_vertices());
- if (mesh.stl.stats.number_of_facets > 0) {
- assert(mesh.repaired && mesh.has_shared_vertices());
- auto nr_degenerated = mesh.stl.stats.degenerate_facets;
- stl_check_facets_exact(&mesh.stl);
- if (nr_degenerated != mesh.stl.stats.degenerate_facets)
- // stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation.
- stl_generate_shared_vertices(&mesh.stl, mesh.its);
- } else
- mesh.its.clear();
- return std::move(mesh.its);
-}
-
// Slice single triangle mesh.
static std::vector<ExPolygons> slice_volume(
const ModelVolume &volume,
@@ -65,7 +48,7 @@ static std::vector<ExPolygons> slice_volume(
{
std::vector<ExPolygons> layers;
if (! zs.empty()) {
- indexed_triangle_set its = get_mesh_its_fix_mesh_connectivity(volume.mesh());
+ indexed_triangle_set its = volume.mesh().its;
if (its.indices.size() > 0) {
MeshSlicingParamsEx params2 { params };
params2.trafo = params2.trafo * volume.get_matrix();
diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp
index 55dae9430..9cac7f63b 100644
--- a/src/libslic3r/SLA/Hollowing.cpp
+++ b/src/libslic3r/SLA/Hollowing.cpp
@@ -286,8 +286,6 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
if (mesh.empty()) return;
- mesh.require_shared_vertices();
-
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size())
@@ -316,7 +314,6 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
remove_inside_triangles(mesh, interior);
mesh.merge(TriangleMesh{interior.mesh});
- mesh.require_shared_vertices();
}
// Get the distance of p to the interior's zero iso_surface. Interior should
@@ -557,8 +554,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
new_faces = {};
mesh = TriangleMesh{mesh.its};
- mesh.repaired = true;
- mesh.require_shared_vertices();
+ //FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles?
}
}} // namespace Slic3r::sla
diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp
index 8d995b59e..1e5de5158 100644
--- a/src/libslic3r/SLA/Pad.cpp
+++ b/src/libslic3r/SLA/Pad.cpp
@@ -370,7 +370,7 @@ bool add_cavity(indexed_triangle_set &pad,
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; }
- ExPolygons pdiff = diff_ex((Polygons)top_poly, (Polygons)middle_base.contour);
+ ExPolygons pdiff = diff_ex(top_poly, middle_base.contour);
if (pdiff.size() != 1) { logerr(); return false; }
diff --git a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
index 20804193e..3ad7d62b1 100644
--- a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
+++ b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
@@ -33,7 +33,6 @@ inline void reproject_points_and_holes(ModelObject *object)
if (!object || (!has_holes && !has_sppoints)) return;
TriangleMesh rmsh = object->raw_mesh();
- rmsh.require_shared_vertices();
IndexedMesh emesh{rmsh};
if (has_sppoints)
diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp
index 5486741f2..196646dc9 100644
--- a/src/libslic3r/SLA/Rotfinder.cpp
+++ b/src/libslic3r/SLA/Rotfinder.cpp
@@ -205,7 +205,6 @@ inline bool is_on_floor(const SLAPrintObjectConfig &cfg)
std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max_count)
{
TriangleMesh chull = mesh.convex_hull_3d();
- chull.require_shared_vertices();
double chull2d_area = chull.convex_hull().area();
double area_threshold = chull2d_area / (scaled<double>(1e3) * scaled(1.));
@@ -299,7 +298,6 @@ struct RotfinderBoilerplate {
static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
{
TriangleMesh mesh = mo.raw_mesh();
- mesh.require_shared_vertices();
ModelInstance *mi = mo.instances[0];
auto rotation = Vec3d::Zero();
@@ -437,7 +435,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo,
RotfinderBoilerplate<1000> bp{mo, params};
TriangleMesh chull = bp.mesh.convex_hull_3d();
- chull.require_shared_vertices();
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 0b9dde122..004f7d555 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -670,7 +670,7 @@ std::string SLAPrint::validate(std::string*) const
return "";
}
-void SLAPrint::set_printer(SLAPrinter *arch)
+void SLAPrint::set_printer(SLAArchive *arch)
{
invalidate_step(slapsRasterize);
m_printer = arch;
@@ -693,7 +693,7 @@ void SLAPrint::process()
if (m_objects.empty())
return;
- name_tbb_thread_pool_threads();
+ name_tbb_thread_pool_threads_set_locale();
// Assumption: at this point the print objects should be populated only with
// the model objects we have to process and the instances are also filtered
@@ -896,7 +896,6 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
obj = m_model_object->raw_mesh();
if (!obj.empty()) {
obj.transform(m_trafo);
- obj.require_shared_vertices();
}
})
{}
@@ -1048,15 +1047,15 @@ Vec3d SLAPrint::relative_correction() const
Vec3d corr(1., 1., 1.);
if(printer_config().relative_correction.values.size() >= 2) {
- corr(X) = printer_config().relative_correction.values[0];
- corr(Y) = printer_config().relative_correction.values[0];
- corr(Z) = printer_config().relative_correction.values.back();
+ corr.x() = printer_config().relative_correction.values[0];
+ corr.y() = corr.x();
+ corr.z() = printer_config().relative_correction.values[1];
}
if(material_config().material_correction.values.size() >= 2) {
- corr(X) *= material_config().material_correction.values[0];
- corr(Y) *= material_config().material_correction.values[0];
- corr(Z) *= material_config().material_correction.values.back();
+ corr.x() *= material_config().material_correction.values[0];
+ corr.y() = corr.x();
+ corr.z() *= material_config().material_correction.values[1];
}
return corr;
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 0cd80f20b..deaabbe19 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -323,7 +323,6 @@ private:
{
support_tree_ptr = sla::SupportTree::create(*this, ctl);
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
- tree_mesh.require_shared_vertices();
return support_tree_ptr;
}
@@ -388,7 +387,7 @@ struct SLAPrintStatistics
}
};
-class SLAPrinter {
+class SLAArchive {
protected:
std::vector<sla::EncodedRaster> m_layers;
@@ -396,7 +395,7 @@ protected:
virtual sla::RasterEncoder get_encoder() const = 0;
public:
- virtual ~SLAPrinter() = default;
+ virtual ~SLAArchive() = default;
virtual void apply(const SLAPrinterConfig &cfg) = 0;
@@ -527,7 +526,7 @@ public:
// TODO: use this structure for the preview in the future.
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
- void set_printer(SLAPrinter *archiver);
+ void set_printer(SLAArchive *archiver);
private:
@@ -549,7 +548,7 @@ private:
std::vector<PrintLayer> m_printer_input;
// The archive object which collects the raster images after slicing
- SLAPrinter *m_printer = nullptr;
+ SLAArchive *m_printer = nullptr;
// Estimated print time, material consumed.
SLAPrintStatistics m_print_statistics;
diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index 4b377d9f1..adec5735a 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -526,7 +526,6 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
}
auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels;
- assert(mesh.has_shared_vertices());
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
sla::Interior *interior = po.m_hollowing_data ?
diff --git a/src/libslic3r/SimplifyMesh.hpp b/src/libslic3r/SimplifyMesh.hpp
index fb3e73d04..23eb343d1 100644
--- a/src/libslic3r/SimplifyMesh.hpp
+++ b/src/libslic3r/SimplifyMesh.hpp
@@ -14,10 +14,8 @@ void simplify_mesh(indexed_triangle_set &);
template<class...Args> void simplify_mesh(TriangleMesh &m, Args &&...a)
{
- m.require_shared_vertices();
simplify_mesh(m.its, std::forward<Args>(a)...);
- m = TriangleMesh{m.its};
- m.require_shared_vertices();
+ m = TriangleMesh{ std::move(m.its) };
}
} // namespace Slic3r
diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp
index 9b9b48f88..30ff67bba 100644
--- a/src/libslic3r/SlicingAdaptive.cpp
+++ b/src/libslic3r/SlicingAdaptive.cpp
@@ -35,13 +35,6 @@ legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as Pru
namespace Slic3r
{
-static inline std::pair<float, float> face_z_span(const stl_facet &f)
-{
- return std::pair<float, float>(
- std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)),
- std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)));
-}
-
// By Florens Waserfall aka @platch:
// This constant essentially describes the volumetric error at the surface which is induced
// by stacking "elliptic" extrusion threads. It is empirically determined by
@@ -88,10 +81,15 @@ void SlicingAdaptive::prepare(const ModelObject &object)
mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
// 1) Collect faces from mesh.
- m_faces.reserve(mesh.stl.stats.number_of_facets);
- for (const stl_facet &face : mesh.stl.facet_start) {
- Vec3f n = face.normal.normalized();
- m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
+ m_faces.reserve(mesh.facets_count());
+ for (stl_triangle_vertex_indices face : mesh.its.indices) {
+ stl_vertex vertex[3] = { mesh.its.vertices[face[0]], mesh.its.vertices[face[1]], mesh.its.vertices[face[2]] };
+ stl_vertex n = face_normal_normalized(vertex);
+ std::pair<float, float> face_z_span {
+ std::min(std::min(vertex[0].z(), vertex[1].z()), vertex[2].z()),
+ std::max(std::max(vertex[0].z(), vertex[1].z()), vertex[2].z())
+ };
+ m_faces.emplace_back(FaceZ({ face_z_span, std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
}
// 2) Sort faces lexicographically by their Z span.
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 1b66bcc53..280936418 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -721,9 +721,9 @@ public:
#ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls.
- int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
- m_pixel_size = scale_(m_support_spacing / oversampling);
- assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
+ int extrusion_width_scaled = scale_(params.extrusion_width);
+ int oversampling = std::clamp(int(scale_(m_support_spacing) / (extrusion_width_scaled + 100)), 1, 8);
+ m_pixel_size = std::max<double>(extrusion_width_scaled + 21, scale_(m_support_spacing / oversampling));
// Add one empty column / row boundaries.
m_bbox.offset(m_pixel_size);
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
@@ -1599,7 +1599,8 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*> new_contact_layer(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
- const SlicingParameters &slicing_params,
+ const SlicingParameters &slicing_params,
+ const coordf_t support_layer_height_min,
const Layer &layer,
std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage,
tbb::spin_mutex &layer_storage_mutex)
@@ -1629,7 +1630,8 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
// Don't want to print a layer below the first layer height as it may not stick well.
//FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
// and it may actually make sense to do it with a thinner layer than the first layer height.
- if (print_z < slicing_params.first_print_layer_height - EPSILON) {
+ const coordf_t min_print_z = slicing_params.raft_layers() > 1 ? slicing_params.raft_interface_top_z + support_layer_height_min + EPSILON : slicing_params.first_print_layer_height - EPSILON;
+ if (print_z < min_print_z) {
// This contact layer is below the first layer height, therefore not printable. Don't support this surface.
return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr);
} else if (print_z < slicing_params.first_print_layer_height + EPSILON) {
@@ -1650,7 +1652,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
bridging_height += region->region().bridging_height_avg(print_config);
bridging_height /= coordf_t(layer.regions().size());
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
- if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
+ if (bridging_print_z >= min_print_z) {
// Not below the first layer height means this layer is printable.
if (print_z < slicing_params.first_print_layer_height + EPSILON) {
// Align the layer with the 1st layer height.
@@ -1664,8 +1666,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
if (bridging_print_z == slicing_params.first_print_layer_height) {
bridging_layer->bottom_z = 0;
bridging_layer->height = slicing_params.first_print_layer_height;
- }
- else {
+ } else {
// Don't know the height yet.
bridging_layer->bottom_z = bridging_print_z;
bridging_layer->height = 0;
@@ -1917,7 +1918,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) {
- auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, layer, layer_storage, layer_storage_mutex);
+ auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
if (new_layer) {
fill_contact_layer(*new_layer, layer_id, m_slicing_params,
*m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons,
@@ -2600,8 +2601,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
// No top contacts -> no intermediate layers will be produced.
return;
- // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
-
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, intermediate_layers.size()),
@@ -2696,6 +2695,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
layer_intermediate.layer_type = sltBase;
#if 0
+ // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
// Fillet the base polygons and trim them again with the top, interface and contact layers.
$base->{$i} = diff(
offset2(
@@ -3338,7 +3338,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
const Point *seg_current_pt = nullptr;
coordf_t seg_current_t = 0.;
- if (! intersection_pl((Polylines)contour.split_at_first_point(), overhang_with_margin).empty()) {
+ if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) {
// The contour is below the overhang at least to some extent.
//FIXME ideally one would place the circles below the overhang only.
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
@@ -3784,7 +3784,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath;
- InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
+ InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density < 1.05 ? ipRectilinear : ipSupportBase);
std::vector<float> angles;
angles.push_back(base_angle);
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index dfaf0f3c6..24cc260ad 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -41,16 +41,8 @@
//====================
#define ENABLE_2_4_0_ALPHA1 1
-// Enable delayed rendering of transparent volumes
-#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1)
-// Enable the fix of importing color print view from gcode files into GCodeViewer
-#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1)
-// Enable drawing contours, at cut level, for sinking volumes
-#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1)
// Enable implementation of retract acceleration in gcode processor
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
-// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
-#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
// Enable rendering seams (and other options) in preview using models
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
@@ -65,9 +57,22 @@
// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
// Enable fixing the z position of color change, pause print and custom gcode markers in preview
-#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2)
+#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
+// Enable replacing a missing file during reload from disk command
+#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
+// Enable fixing the synchronization of seams with the horizontal slider in preview
+#define ENABLE_FIX_SEAMS_SYNCH (1 && ENABLE_2_4_0_ALPHA2)
+
+
+//====================
+// 2.4.0.alpha3 techs
+//====================
+#define ENABLE_2_4_0_ALPHA3 1
+
+// Enable fixing loading of gcode files generated with SuperSlicer in GCodeViewer
+#define ENABLE_FIX_SUPERSLICER_GCODE_IMPORT (1 && ENABLE_2_4_0_ALPHA3)
// Enable coloring of toolpaths in preview by layer time
-#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_ALPHA2)
+#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_ALPHA3)
#endif // _prusaslicer_technologies_h_
diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp
index 25c29d273..106da4a78 100644
--- a/src/libslic3r/Thread.cpp
+++ b/src/libslic3r/Thread.cpp
@@ -188,7 +188,8 @@ std::optional<std::string> get_current_thread_name()
#endif // _WIN32
// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID.
-void name_tbb_thread_pool_threads()
+// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
+void name_tbb_thread_pool_threads_set_locale()
{
static bool initialized = false;
if (initialized)
@@ -233,6 +234,21 @@ void name_tbb_thread_pool_threads()
std::ostringstream name;
name << "slic3r_tbb_" << range.begin();
set_current_thread_name(name.str().c_str());
+ // Set locales of the worker thread to "C".
+#ifdef _WIN32
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ std::setlocale(LC_ALL, "C");
+#else
+ // We are leaking some memory here, because the newlocale() produced memory will never be released.
+ // This is not a problem though, as there will be a maximum one worker thread created per physical thread.
+ uselocale(newlocale(
+#ifdef __APPLE__
+ LC_ALL_MASK
+#else // some Unix / Linux / BSD
+ LC_ALL
+#endif
+ , "C", nullptr));
+#endif
}
});
}
diff --git a/src/libslic3r/Thread.hpp b/src/libslic3r/Thread.hpp
index a86123796..9afe13b42 100644
--- a/src/libslic3r/Thread.hpp
+++ b/src/libslic3r/Thread.hpp
@@ -33,7 +33,8 @@ std::optional<std::string> get_current_thread_name();
// To be called somewhere before the TBB threads are spinned for the first time, to
// give them names recognizible in the debugger.
-void name_tbb_thread_pool_threads();
+// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
+void name_tbb_thread_pool_threads_set_locale();
template<class Fn>
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index fa8f8bce6..39815bd64 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -7,6 +7,7 @@
#include "Point.hpp"
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
+#include "Utils.hpp"
#include <libqhullcpp/Qhull.h>
#include <libqhullcpp/QhullFacetList.h>
@@ -21,6 +22,8 @@
#include <type_traits>
#include <boost/log/trivial.hpp>
+#include <boost/nowide/cstdio.hpp>
+#include <boost/predef/other/endian.h>
#include <Eigen/Core>
#include <Eigen/Dense>
@@ -29,74 +32,51 @@
namespace Slic3r {
-TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets) : repaired(false)
+static void update_bounding_box(const indexed_triangle_set &its, TriangleMeshStats &out)
{
- stl_file &stl = this->stl;
- stl.stats.type = inmemory;
+ BoundingBoxf3 bbox = Slic3r::bounding_box(its);
+ out.min = bbox.min.cast<float>();
+ out.max = bbox.max.cast<float>();
+ out.size = out.max - out.min;
+}
- // count facets and allocate memory
- stl.stats.number_of_facets = (uint32_t)facets.size();
- stl.stats.original_num_facets = stl.stats.number_of_facets;
- stl_allocate(&stl);
+static void fill_initial_stats(const indexed_triangle_set &its, TriangleMeshStats &out)
+{
+ out.number_of_facets = its.indices.size();
+ out.volume = its_volume(its);
+ update_bounding_box(its, out);
- for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
- stl_facet facet;
- facet.vertex[0] = points[facets[i](0)].cast<float>();
- facet.vertex[1] = points[facets[i](1)].cast<float>();
- facet.vertex[2] = points[facets[i](2)].cast<float>();
- facet.extra[0] = 0;
- facet.extra[1] = 0;
+ const std::vector<Vec3i> face_neighbors = its_face_neighbors(its);
+ out.number_of_parts = its_number_of_patches(its, face_neighbors);
+ out.open_edges = its_num_open_edges(face_neighbors);
+}
- stl_normal normal;
- stl_calculate_normal(normal, &facet);
- stl_normalize_vector(normal);
- facet.normal = normal;
+TriangleMesh::TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces) : its { faces, vertices }
+{
+ fill_initial_stats(this->its, m_stats);
+}
- stl.facet_start[i] = facet;
- }
- stl_get_size(&stl);
+TriangleMesh::TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces) : its { std::move(faces), std::move(vertices) }
+{
+ fill_initial_stats(this->its, m_stats);
}
-TriangleMesh::TriangleMesh(const indexed_triangle_set &M) : repaired(false)
+TriangleMesh::TriangleMesh(const indexed_triangle_set &its) : its(its)
{
- stl.stats.type = inmemory;
-
- // count facets and allocate memory
- stl.stats.number_of_facets = uint32_t(M.indices.size());
- stl.stats.original_num_facets = int(stl.stats.number_of_facets);
- stl_allocate(&stl);
-
- for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
- stl_facet facet;
- facet.vertex[0] = M.vertices[size_t(M.indices[i](0))];
- facet.vertex[1] = M.vertices[size_t(M.indices[i](1))];
- facet.vertex[2] = M.vertices[size_t(M.indices[i](2))];
- facet.extra[0] = 0;
- facet.extra[1] = 0;
-
- stl_normal normal;
- stl_calculate_normal(normal, &facet);
- stl_normalize_vector(normal);
- facet.normal = normal;
-
- stl.facet_start[i] = facet;
- }
-
- stl_get_size(&stl);
+ fill_initial_stats(this->its, m_stats);
+}
+
+TriangleMesh::TriangleMesh(indexed_triangle_set &&its) : its(std::move(its))
+{
+ fill_initial_stats(this->its, m_stats);
}
// #define SLIC3R_TRACE_REPAIR
-void TriangleMesh::repair(bool update_shared_vertices)
+static void trianglemesh_repair_on_import(stl_file &stl)
{
- if (this->repaired) {
- if (update_shared_vertices)
- this->require_shared_vertices();
- return;
- }
-
// admesh fails when repairing empty meshes
- if (this->stl.stats.number_of_facets == 0)
+ if (stl.stats.number_of_facets == 0)
return;
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
@@ -105,9 +85,9 @@ void TriangleMesh::repair(bool update_shared_vertices)
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
#endif /* SLIC3R_TRACE_REPAIR */
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
stl_check_facets_exact(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
@@ -117,9 +97,11 @@ void TriangleMesh::repair(bool update_shared_vertices)
float tolerance = (float)stl.stats.shortest_edge;
float increment = (float)stl.stats.bounding_diameter / 10000.0f;
int iterations = 2;
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < int(stl.stats.number_of_facets)) {
+ // Not a manifold, some triangles have unconnected edges.
+ for (int i = 0; i < iterations; ++ i) {
+ if (stl.stats.connected_facets_3_edge < int(stl.stats.number_of_facets)) {
+ // Still not a manifold, some triangles have unconnected edges.
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
@@ -133,7 +115,7 @@ void TriangleMesh::repair(bool update_shared_vertices)
}
}
}
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// remove_unconnected
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
@@ -141,7 +123,7 @@ void TriangleMesh::repair(bool update_shared_vertices)
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
#endif /* SLIC3R_TRACE_REPAIR */
stl_remove_unconnected_facets(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
}
// fill_holes
@@ -162,93 +144,82 @@ void TriangleMesh::repair(bool update_shared_vertices)
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
#endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_directions(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// normal_values
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
#endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_values(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// always calculate the volume and reverse all normals if volume is negative
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
#endif /* SLIC3R_TRACE_REPAIR */
+ // If the volume is negative, all the facets are flipped and added to stats.facets_reversed.
stl_calculate_volume(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// neighbors
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
#endif /* SLIC3R_TRACE_REPAIR */
stl_verify_neighbors(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
- this->repaired = true;
+ //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+ if (auto nr_degenerated = stl.stats.degenerate_facets; stl.stats.number_of_facets > 0 && nr_degenerated > 0)
+ stl_check_facets_exact(&stl);
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
-
- // This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
- // and it is risky to generate such a structure once the meshes are shared. Do it now.
- this->its.clear();
- if (update_shared_vertices)
- this->require_shared_vertices();
}
-float TriangleMesh::volume()
-{
- if (this->stl.stats.volume == -1)
- stl_calculate_volume(&this->stl);
- return this->stl.stats.volume;
+bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
+{
+ stl_file stl;
+ if (! stl_open(&stl, input_file))
+ return false;
+ if (repair)
+ trianglemesh_repair_on_import(stl);
+
+ m_stats.number_of_facets = stl.stats.number_of_facets;
+ m_stats.min = stl.stats.min;
+ m_stats.max = stl.stats.max;
+ m_stats.size = stl.stats.size;
+ m_stats.volume = stl.stats.volume;
+
+ auto facets_w_1_bad_edge = stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge;
+ auto facets_w_2_bad_edge = stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge;
+ auto facets_w_3_bad_edge = stl.stats.number_of_facets - stl.stats.connected_facets_1_edge;
+ m_stats.open_edges = stl.stats.backwards_edges + facets_w_1_bad_edge + facets_w_2_bad_edge * 2 + facets_w_3_bad_edge * 3;
+
+ m_stats.edges_fixed = stl.stats.edges_fixed;
+ m_stats.degenerate_facets = stl.stats.degenerate_facets;
+ m_stats.facets_removed = stl.stats.facets_removed;
+ m_stats.facets_reversed = stl.stats.facets_reversed;
+ m_stats.backwards_edges = stl.stats.backwards_edges;
+ m_stats.number_of_parts = stl.stats.number_of_parts;
+
+ stl_generate_shared_vertices(&stl, this->its);
+ return true;
}
-void TriangleMesh::check_topology()
-{
- // checking exact
- stl_check_facets_exact(&stl);
- stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
- stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
- stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
-
- // checking nearby
- //int last_edges_fixed = 0;
- float tolerance = stl.stats.shortest_edge;
- float increment = stl.stats.bounding_diameter / 10000.0;
- int iterations = 2;
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
- stl_check_facets_nearby(&stl, tolerance);
- //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
- //last_edges_fixed = stl.stats.edges_fixed;
- tolerance += increment;
- } else {
- break;
- }
- }
- }
+bool TriangleMesh::write_ascii(const char* output_file)
+{
+ return its_write_stl_ascii(output_file, "", this->its);
}
-void TriangleMesh::reset_repair_stats() {
- this->stl.stats.degenerate_facets = 0;
- this->stl.stats.edges_fixed = 0;
- this->stl.stats.facets_removed = 0;
- this->stl.stats.facets_added = 0;
- this->stl.stats.facets_reversed = 0;
- this->stl.stats.backwards_edges = 0;
- this->stl.stats.normals_fixed = 0;
+bool TriangleMesh::write_binary(const char* output_file)
+{
+ return its_write_stl_binary(output_file, "", this->its);
}
-bool TriangleMesh::needed_repair() const
+float TriangleMesh::volume()
{
- return this->stl.stats.degenerate_facets > 0
- || this->stl.stats.edges_fixed > 0
- || this->stl.stats.facets_removed > 0
- || this->stl.stats.facets_added > 0
- || this->stl.stats.facets_reversed > 0
- || this->stl.stats.backwards_edges > 0;
+ if (m_stats.volume == -1)
+ m_stats.volume = its_volume(this->its);
+ return m_stats.volume;
}
void TriangleMesh::WriteOBJFile(const char* output_file) const
@@ -258,133 +229,138 @@ void TriangleMesh::WriteOBJFile(const char* output_file) const
void TriangleMesh::scale(float factor)
{
- stl_scale(&(this->stl), factor);
- for (stl_vertex& v : this->its.vertices)
- v *= factor;
+ this->scale(Vec3f(factor, factor, factor));
}
-void TriangleMesh::scale(const Vec3d &versor)
+void TriangleMesh::scale(const Vec3f &versor)
{
- stl_scale_versor(&this->stl, versor.cast<float>());
- for (stl_vertex& v : this->its.vertices) {
- v.x() *= versor.x();
- v.y() *= versor.y();
- v.z() *= versor.z();
+ // Scale extents.
+ auto s = versor.array();
+ m_stats.min.array() *= s;
+ m_stats.max.array() *= s;
+ // Scale size.
+ m_stats.size.array() *= s;
+ // Scale volume.
+ if (m_stats.volume > 0.0)
+ m_stats.volume *= s(0) * s(1) * s(2);
+ if (versor.x() == versor.y() && versor.x() == versor.z()) {
+ float s = versor.x();
+ for (stl_vertex &v : this->its.vertices)
+ v *= s;
+ } else {
+ for (stl_vertex &v : this->its.vertices) {
+ v.x() *= versor.x();
+ v.y() *= versor.y();
+ v.z() *= versor.z();
+ }
}
}
-void TriangleMesh::translate(float x, float y, float z)
+void TriangleMesh::translate(const Vec3f &displacement)
{
- if (x == 0.f && y == 0.f && z == 0.f)
- return;
- stl_translate_relative(&(this->stl), x, y, z);
- stl_vertex shift(x, y, z);
- for (stl_vertex& v : this->its.vertices)
- v += shift;
+ if (displacement.x() != 0.f || displacement.y() != 0.f || displacement.z() != 0.f) {
+ for (stl_vertex& v : this->its.vertices)
+ v += displacement;
+ m_stats.min += displacement;
+ m_stats.max += displacement;
+ }
}
-void TriangleMesh::translate(const Vec3f &displacement)
+void TriangleMesh::translate(float x, float y, float z)
{
- translate(displacement(0), displacement(1), displacement(2));
+ this->translate(Vec3f(x, y, z));
}
void TriangleMesh::rotate(float angle, const Axis &axis)
{
- if (angle == 0.f)
- return;
-
- // admesh uses degrees
- angle = Slic3r::Geometry::rad2deg(angle);
-
- if (axis == X) {
- stl_rotate_x(&this->stl, angle);
- its_rotate_x(this->its, angle);
- } else if (axis == Y) {
- stl_rotate_y(&this->stl, angle);
- its_rotate_y(this->its, angle);
- } else if (axis == Z) {
- stl_rotate_z(&this->stl, angle);
- its_rotate_z(this->its, angle);
+ if (angle != 0.f) {
+ angle = Slic3r::Geometry::rad2deg(angle);
+ switch (axis) {
+ case X: its_rotate_x(this->its, angle); break;
+ case Y: its_rotate_y(this->its, angle); break;
+ case Z: its_rotate_z(this->its, angle); break;
+ default: assert(false); return;
+ }
+ update_bounding_box(this->its, this->m_stats);
}
}
void TriangleMesh::rotate(float angle, const Vec3d& axis)
{
- if (angle == 0.f)
- return;
-
- Vec3d axis_norm = axis.normalized();
- Transform3d m = Transform3d::Identity();
- m.rotate(Eigen::AngleAxisd(angle, axis_norm));
- stl_transform(&stl, m);
- its_transform(its, m);
+ if (angle != 0.f) {
+ Vec3d axis_norm = axis.normalized();
+ Transform3d m = Transform3d::Identity();
+ m.rotate(Eigen::AngleAxisd(angle, axis_norm));
+ its_transform(its, m);
+ update_bounding_box(this->its, this->m_stats);
+ }
}
-void TriangleMesh::mirror(const Axis &axis)
+void TriangleMesh::mirror(const Axis axis)
{
- if (axis == X) {
- stl_mirror_yz(&this->stl);
- for (stl_vertex &v : this->its.vertices)
- v(0) *= -1.0;
- } else if (axis == Y) {
- stl_mirror_xz(&this->stl);
- for (stl_vertex &v : this->its.vertices)
- v(1) *= -1.0;
- } else if (axis == Z) {
- stl_mirror_xy(&this->stl);
+ switch (axis) {
+ case X:
+ for (stl_vertex &v : its.vertices)
+ v.x() *= -1.f;
+ break;
+ case Y:
+ for (stl_vertex& v : this->its.vertices)
+ v.y() *= -1.0;
+ break;
+ case Z:
for (stl_vertex &v : this->its.vertices)
- v(2) *= -1.0;
- }
+ v.z() *= -1.0;
+ break;
+ default:
+ assert(false);
+ return;
+ };
+ its_flip_triangles(this->its);
+ int iaxis = int(axis);
+ std::swap(m_stats.min[iaxis], m_stats.max[iaxis]);
+ m_stats.min[iaxis] *= -1.0;
+ m_stats.max[iaxis] *= -1.0;
}
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
{
- stl_transform(&stl, t);
its_transform(its, t);
- if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
- // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
- // As for the assert: the repair function would fix the normals, reversing would
- // break them again. The caller should provide a mesh that does not need repair.
- // The repair call is left here so things don't break more than they were.
- assert(this->repaired);
- this->repair(false);
- stl_reverse_all_facets(&stl);
- this->its.clear();
- this->require_shared_vertices();
- }
+ if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
+ its_flip_triangles(its);
+ else
+ m_stats.volume = - m_stats.volume;
+ update_bounding_box(this->its, this->m_stats);
}
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
{
- stl_transform(&stl, m);
its_transform(its, m);
- if (fix_left_handed && m.determinant() < 0.) {
- // See comments in function above.
- assert(this->repaired);
- this->repair(false);
- stl_reverse_all_facets(&stl);
- this->its.clear();
- this->require_shared_vertices();
- }
+ if (fix_left_handed && m.determinant() < 0.)
+ its_flip_triangles(its);
+ else
+ m_stats.volume = - m_stats.volume;
+ update_bounding_box(this->its, this->m_stats);
+}
+
+void TriangleMesh::flip_triangles()
+{
+ its_flip_triangles(its);
+ m_stats.volume = - m_stats.volume;
}
void TriangleMesh::align_to_origin()
{
- this->translate(
- - this->stl.stats.min(0),
- - this->stl.stats.min(1),
- - this->stl.stats.min(2));
+ this->translate(- m_stats.min(0), - m_stats.min(1), - m_stats.min(2));
}
void TriangleMesh::rotate(double angle, Point* center)
{
- if (angle == 0.)
- return;
- Vec2f c = center->cast<float>();
- this->translate(-c(0), -c(1), 0);
- stl_rotate_z(&this->stl, (float)angle);
- its_rotate_z(this->its, (float)angle);
- this->translate(c(0), c(1), 0);
+ if (angle != 0.) {
+ Vec2f c = center->cast<float>();
+ this->translate(-c(0), -c(1), 0);
+ its_rotate_z(this->its, (float)angle);
+ this->translate(c(0), c(1), 0);
+ }
}
/**
@@ -392,145 +368,36 @@ void TriangleMesh::rotate(double angle, Point* center)
*/
bool TriangleMesh::is_splittable() const
{
- std::vector<unsigned char> visited;
- find_unvisited_neighbors(visited);
-
- // Try finding an unvisited facet. If there are none, the mesh is not splittable.
- auto it = std::find(visited.begin(), visited.end(), false);
- return it != visited.end();
+ return its_is_splittable(this->its);
}
-/**
- * Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
- * and return them.
- *
- * @param facet_visited A reference to a vector of booleans. Contains whether or not a
- * facet with the same index has been visited.
- * @return A deque with all newly visited facets.
- */
-std::deque<uint32_t> TriangleMesh::find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const
-{
- // Make sure we're not operating on a broken mesh.
- if (!this->repaired)
- throw Slic3r::RuntimeError("find_unvisited_neighbors() requires repair()");
-
- // If the visited list is empty, populate it with false for every facet.
- if (facet_visited.empty())
- facet_visited = std::vector<unsigned char>(this->stl.stats.number_of_facets, false);
-
- // Find the first unvisited facet.
- std::queue<uint32_t> facet_queue;
- std::deque<uint32_t> facets;
- auto facet = std::find(facet_visited.begin(), facet_visited.end(), false);
- if (facet != facet_visited.end()) {
- uint32_t idx = uint32_t(facet - facet_visited.begin());
- facet_queue.push(idx);
- facet_visited[idx] = true;
- facets.emplace_back(idx);
- }
-
- // Traverse all reachable neighbors and mark them as visited.
- while (! facet_queue.empty()) {
- uint32_t facet_idx = facet_queue.front();
- facet_queue.pop();
- for (int neighbor_idx : this->stl.neighbors_start[facet_idx].neighbor)
- if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) {
- facet_queue.push(uint32_t(neighbor_idx));
- facet_visited[neighbor_idx] = true;
- facets.emplace_back(uint32_t(neighbor_idx));
- }
- }
-
- return facets;
-}
-
-/**
- * Splits a mesh into multiple meshes when possible.
- *
- * @return A TriangleMeshPtrs with the newly created meshes.
- */
-TriangleMeshPtrs TriangleMesh::split() const
+std::vector<TriangleMesh> TriangleMesh::split() const
{
- struct MeshAdder {
- TriangleMeshPtrs &meshes;
- MeshAdder(TriangleMeshPtrs &ptrs): meshes{ptrs} {}
- void operator=(const indexed_triangle_set &its)
- {
- meshes.emplace_back(new TriangleMesh(its));
- }
- };
+ std::vector<indexed_triangle_set> itss = its_split(this->its);
+ std::vector<TriangleMesh> out;
+ out.reserve(itss.size());
+ for (indexed_triangle_set &m : itss) {
+ // The TriangleMesh constructor shall fill in the mesh statistics including volume.
+ out.emplace_back(std::move(m));
+ if (TriangleMesh &triangle_mesh = out.back(); triangle_mesh.volume() < 0)
+ // Some source mesh parts may be incorrectly oriented. Correct them.
+ triangle_mesh.flip_triangles();
- TriangleMeshPtrs meshes;
- if (has_shared_vertices()) {
- its_split(its, MeshAdder{meshes});
- } else {
- // Loop while we have remaining facets.
- std::vector<unsigned char> facet_visited;
- for (;;) {
- std::deque<uint32_t> facets = find_unvisited_neighbors(facet_visited);
- if (facets.empty())
- break;
-
- // Create a new mesh for the part that was just split off.
- TriangleMesh* mesh = new TriangleMesh;
- meshes.emplace_back(mesh);
- mesh->stl.stats.type = inmemory;
- mesh->stl.stats.number_of_facets = (uint32_t)facets.size();
- mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
- stl_allocate(&mesh->stl);
-
- // Assign the facets to the new mesh.
- bool first = true;
- for (auto facet = facets.begin(); facet != facets.end(); ++ facet) {
- mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
- stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
- }
- }
}
-
- return meshes;
+ return out;
}
void TriangleMesh::merge(const TriangleMesh &mesh)
{
- // reset stats and metadata
- int number_of_facets = this->stl.stats.number_of_facets;
- this->its.clear();
- this->repaired = false;
-
- // update facet count and allocate more memory
- this->stl.stats.number_of_facets = number_of_facets + mesh.stl.stats.number_of_facets;
- this->stl.stats.original_num_facets = this->stl.stats.number_of_facets;
- stl_reallocate(&this->stl);
-
- // copy facets
- for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
- this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
-
- // update size
- stl_get_size(&this->stl);
+ its_merge(this->its, mesh.its);
+ m_stats = m_stats.merge(mesh.m_stats);
}
// Calculate projection of the mesh into the XY plane, in scaled coordinates.
//FIXME This could be extremely slow! Use it for tiny meshes only!
ExPolygons TriangleMesh::horizontal_projection() const
{
- ClipperLib::Paths paths;
- Polygon p;
- p.points.assign(3, Point());
- auto delta = scaled<float>(0.01);
- std::vector<float> deltas { delta, delta, delta };
- paths.reserve(this->stl.stats.number_of_facets);
- for (const stl_facet &facet : this->stl.facet_start) {
- p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
- p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
- p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
- p.make_counter_clockwise();
- paths.emplace_back(mittered_offset_path_scaled(p.points, deltas, 3.));
- }
-
- // the offset factor was tuned using groovemount.stl
- return ClipperPaths_to_Slic3rExPolygons(paths);
+ return union_ex(project_mesh(this->its, Transform3d::Identity(), []() {}));
}
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
@@ -549,24 +416,16 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
{
BoundingBoxf3 bb;
bb.defined = true;
- bb.min = this->stl.stats.min.cast<double>();
- bb.max = this->stl.stats.max.cast<double>();
+ bb.min = m_stats.min.cast<double>();
+ bb.max = m_stats.max.cast<double>();
return bb;
}
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
{
BoundingBoxf3 bbox;
- if (this->its.vertices.empty()) {
- // Using the STL faces.
- for (const stl_facet &facet : this->stl.facet_start)
- for (size_t j = 0; j < 3; ++ j)
- bbox.merge(trafo * facet.vertex[j].cast<double>());
- } else {
- // Using the shared vertices should be a bit quicker than using the STL faces.
- for (const stl_vertex &v : this->its.vertices)
- bbox.merge(trafo * v.cast<double>());
- }
+ for (const stl_vertex &v : this->its.vertices)
+ bbox.merge(trafo * v.cast<double>());
return bbox;
}
@@ -578,26 +437,16 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
std::vector<realT> src_vertices;
try
{
- if (this->has_shared_vertices()) {
#if REALfloat
- qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
+ qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
#else
- src_vertices.reserve(this->its.vertices.size() * 3);
- // We will now fill the vector with input points for computation:
- for (const stl_vertex &v : this->its.vertices)
- for (int i = 0; i < 3; ++ i)
- src_vertices.emplace_back(v(i));
- qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
+ src_vertices.reserve(this->its.vertices.size() * 3);
+ // We will now fill the vector with input points for computation:
+ for (const stl_vertex &v : this->its.vertices)
+ for (int i = 0; i < 3; ++ i)
+ src_vertices.emplace_back(v(i));
+ qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
#endif
- } else {
- src_vertices.reserve(this->stl.facet_start.size() * 9);
- // We will now fill the vector with input points for computation:
- for (const stl_facet &f : this->stl.facet_start)
- for (int i = 0; i < 3; ++ i)
- for (int j = 0; j < 3; ++ j)
- src_vertices.emplace_back(f.vertex[i](j));
- qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
- }
}
catch (...)
{
@@ -606,84 +455,77 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
}
// Let's collect results:
- Pointf3s dst_vertices;
- std::vector<Vec3i> facets;
- auto facet_list = qhull.facetList().toStdVector();
- for (const orgQhull::QhullFacet& facet : facet_list)
- { // iterate through facets
- orgQhull::QhullVertexSet vertices = facet.vertices();
- for (int i = 0; i < 3; ++i)
- { // iterate through facet's vertices
-
- orgQhull::QhullPoint p = vertices[i].point();
- const auto* coords = p.coordinates();
- dst_vertices.emplace_back(coords[0], coords[1], coords[2]);
+ std::vector<Vec3f> dst_vertices;
+ std::vector<Vec3i> dst_facets;
+ // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
+ std::vector<int> map_dst_vertices;
+#ifndef NDEBUG
+ Vec3f centroid = Vec3f::Zero();
+ for (auto pt : this->its.vertices)
+ centroid += pt;
+ centroid /= float(this->its.vertices.size());
+#endif // NDEBUG
+ for (const orgQhull::QhullFacet facet : qhull.facetList()) {
+ // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
+ Vec3i indices;
+ int cnt = 0;
+ for (const orgQhull::QhullVertex vertex : facet.vertices()) {
+ int id = vertex.id();
+ assert(id >= 0);
+ if (id >= int(map_dst_vertices.size()))
+ map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
+ if (int i = map_dst_vertices[id]; i == -1) {
+ // Allocate a new vertex.
+ i = int(dst_vertices.size());
+ map_dst_vertices[id] = i;
+ orgQhull::QhullPoint pt(vertex.point());
+ dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
+ indices[cnt] = i;
+ } else {
+ // Reuse existing vertex.
+ indices[cnt] = i;
+ }
+ if (cnt ++ == 3)
+ break;
+ }
+ assert(cnt == 3);
+ if (cnt == 3) {
+ // QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
+ // Calculate face normal based on the order of vertices.
+ Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
+ auto *n2 = facet.getBaseT()->normal;
+ auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
+#ifndef NDEBUG
+ Vec3f n3 = (dst_vertices[indices(0)] - centroid);
+ auto d3 = n.dot(n3);
+ assert((d < 0.f) == (d3 < 0.f));
+#endif // NDEBUG
+ // Get the face normal from QHull.
+ if (d < 0.f)
+ // Fix face orientation.
+ std::swap(indices[1], indices[2]);
+ dst_facets.emplace_back(indices);
}
- unsigned int size = (unsigned int)dst_vertices.size();
- facets.emplace_back(size - 3, size - 2, size - 1);
}
- TriangleMesh output_mesh(dst_vertices, facets);
- output_mesh.repair();
- return output_mesh;
+ TriangleMesh mesh{ std::move(dst_vertices), std::move(dst_facets) };
+ assert(mesh.stats().manifold());
+ return mesh;
}
std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z) const
{
// convert doubles to floats
std::vector<float> z_f(z.begin(), z.end());
- assert(this->has_shared_vertices());
return slice_mesh_ex(this->its, z_f, 0.0004f);
}
-void TriangleMesh::require_shared_vertices()
-{
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
- assert(stl_validate(&this->stl));
- if (! this->repaired)
- this->repair();
- if (this->its.vertices.empty()) {
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
- stl_generate_shared_vertices(&this->stl, this->its);
- }
- assert(stl_validate(&this->stl, this->its));
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
-}
-
size_t TriangleMesh::memsize() const
{
- size_t memsize = 8 + this->stl.memsize() + this->its.memsize();
+ size_t memsize = 8 + this->its.memsize() + sizeof(this->m_stats);
return memsize;
}
-// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
-size_t TriangleMesh::release_optional()
-{
- size_t memsize_released = sizeof(stl_neighbors) * this->stl.neighbors_start.size() + this->its.memsize();
- // The indexed triangle set may be recalculated using the stl_generate_shared_vertices() function.
- this->its.clear();
- // The neighbors structure may be recalculated using the stl_check_facets_exact() function.
- this->stl.neighbors_start.clear();
- return memsize_released;
-}
-
-// Restore optional data possibly released by release_optional().
-void TriangleMesh::restore_optional()
-{
- if (! this->stl.facet_start.empty()) {
- // Save the old stats before calling stl_check_faces_exact, as it may modify the statistics.
- stl_stats stats = this->stl.stats;
- if (this->stl.neighbors_start.empty()) {
- stl_reallocate(&this->stl);
- stl_check_facets_exact(&this->stl);
- }
- if (this->its.vertices.empty())
- stl_generate_shared_vertices(&this->stl, this->its);
- // Restore the old statistics.
- this->stl.stats = stats;
- }
-}
-
// Create a mapping from triangle edge into face.
struct EdgeToFace {
// Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
@@ -1059,21 +901,32 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor
indexed_triangle_set its_make_cube(double xd, double yd, double zd)
{
auto x = float(xd), y = float(yd), z = float(zd);
- indexed_triangle_set mesh;
- mesh.vertices = {{x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0},
- {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z}};
- mesh.indices = {{0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7},
- {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2},
- {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5}};
-
- return mesh;
+ return {
+ { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7},
+ {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2},
+ {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} },
+ { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0},
+ {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} }
+ };
}
-TriangleMesh make_cube(double x, double y, double z)
+indexed_triangle_set its_make_prism(float width, float length, float height)
{
- TriangleMesh mesh(its_make_cube(x, y, z));
- mesh.repair();
- return mesh;
+ // We need two upward facing triangles
+ float x = width / 2.f, y = length / 2.f;
+ return {
+ {
+ {0, 1, 2}, // side 1
+ {4, 3, 5}, // side 2
+ {1, 4, 2}, {2, 4, 5}, // roof 1
+ {0, 2, 5}, {0, 5, 3}, // roof 2
+ {3, 4, 1}, {3, 1, 0} // bottom
+ },
+ {
+ {-x, -y, 0.f}, {x, -y, 0.f}, {0.f, -y, height},
+ {-x, y, 0.f}, {x, y, 0.f}, {0.f, y, height},
+ }
+ };
}
// Generate the mesh for a cylinder and return it, using
@@ -1121,23 +974,17 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa)
return mesh;
}
-TriangleMesh make_cylinder(double r, double h, double fa)
+indexed_triangle_set its_make_cone(double r, double h, double fa)
{
- TriangleMesh mesh{its_make_cylinder(r, h, fa)};
- mesh.repair();
-
- return mesh;
-}
+ indexed_triangle_set mesh;
+ auto& vertices = mesh.vertices;
+ auto& facets = mesh.indices;
+ vertices.reserve(3 + 2 * size_t(2 * PI / fa));
+ // base center and top vertex
+ vertices.emplace_back(Vec3f::Zero());
+ vertices.emplace_back(Vec3f(0., 0., h));
-TriangleMesh make_cone(double r, double h, double fa)
-{
- Pointf3s vertices;
- std::vector<Vec3i> facets;
- vertices.reserve(3+size_t(2*PI/fa));
- vertices.reserve(3+2*size_t(2*PI/fa));
-
- vertices = { Vec3d::Zero(), Vec3d(0., 0., h) }; // base center and top vertex
size_t i = 0;
for (double angle=0; angle<2*PI; angle+=fa) {
vertices.emplace_back(r*std::cos(angle), r*std::sin(angle), 0.);
@@ -1150,11 +997,27 @@ TriangleMesh make_cone(double r, double h, double fa)
facets.emplace_back(0, 2, i+1); // close the shape
facets.emplace_back(1, i+1, 2);
- TriangleMesh mesh(std::move(vertices), std::move(facets));
- mesh.repair();
return mesh;
}
+indexed_triangle_set its_make_pyramid(float base, float height)
+{
+ float a = base / 2.f;
+ return {
+ {
+ {0, 1, 2},
+ {0, 2, 3},
+ {0, 1, 4},
+ {1, 2, 4},
+ {2, 3, 4},
+ {3, 0, 4}
+ },
+ {
+ {-a, -a, 0}, {a, -a, 0}, {a, a, 0},
+ {-a, a, 0}, {0.f, 0.f, height}
+ }
+ };
+}
// Generates mesh for a sphere centered about the origin, using the generated angle
// to determine the granularity.
@@ -1214,12 +1077,10 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh;
}
-TriangleMesh make_sphere(double radius, double fa)
+void its_reverse_all_facets(indexed_triangle_set &its)
{
- TriangleMesh mesh(its_make_sphere(radius, fa));
- mesh.repair();
-
- return mesh;
+ for (stl_triangle_vertex_indices &face : its.indices)
+ std::swap(face[0], face[1]);
}
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
@@ -1295,10 +1156,40 @@ std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its)
return its_split<>(its);
}
+// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
+bool its_number_of_patches(const indexed_triangle_set &its)
+{
+ return its_number_of_patches<>(its);
+}
+bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors)
+{
+ return its_number_of_patches<>(ItsNeighborsWrapper{ its, face_neighbors });
+}
+
+// Same as its_number_of_patches(its) > 1, but faster.
bool its_is_splittable(const indexed_triangle_set &its)
{
return its_is_splittable<>(its);
}
+bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors)
+{
+ return its_is_splittable<>(ItsNeighborsWrapper{ its, face_neighbors });
+}
+
+size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors)
+{
+ size_t num_open_edges = 0;
+ for (Vec3i neighbors : face_neighbors)
+ for (int n : neighbors)
+ if (n < 0)
+ ++ num_open_edges;
+ return num_open_edges;
+}
+
+size_t its_num_open_edges(const indexed_triangle_set &its)
+{
+ return its_num_open_edges(its_face_neighbors(its));
+}
void VertexFaceIndex::create(const indexed_triangle_set &its)
{
@@ -1335,4 +1226,88 @@ std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its)
return create_face_neighbors_index(ex_tbb, its);
}
+std::vector<Vec3f> its_face_normals(const indexed_triangle_set &its)
+{
+ std::vector<Vec3f> normals;
+ normals.reserve(its.indices.size());
+ for (stl_triangle_vertex_indices face : its.indices)
+ normals.push_back(its_face_normal(its, face));
+ return normals;
+}
+
+#if BOOST_ENDIAN_LITTLE_BYTE
+static inline void big_endian_reverse_quads(char*, size_t) {}
+#else // BOOST_ENDIAN_LITTLE_BYTE
+static inline void big_endian_reverse_quads(char *buf, size_t cnt)
+{
+ for (size_t i = 0; i < cnt; i += 4) {
+ std::swap(buf[i], buf[i+3]);
+ std::swap(buf[i+1], buf[i+2]);
+ }
+}
+#endif // BOOST_ENDIAN_LITTLE_BYTE
+
+bool its_write_stl_ascii(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices)
+{
+ FILE *fp = boost::nowide::fopen(file, "w");
+ if (fp == nullptr) {
+ BOOST_LOG_TRIVIAL(error) << "its_write_stl_ascii: Couldn't open " << file << " for writing";
+ return false;
+ }
+
+ fprintf(fp, "solid %s\n", label);
+
+ for (const stl_triangle_vertex_indices face : indices) {
+ Vec3f vertex[3] = { vertices[face(0)], vertices[face(1)], vertices[face(2)] };
+ Vec3f normal = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized();
+ fprintf(fp, " facet normal % .8E % .8E % .8E\n", normal(0), normal(1), normal(2));
+ fprintf(fp, " outer loop\n");
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[0](0), vertex[0](1), vertex[0](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[1](0), vertex[1](1), vertex[1](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[2](0), vertex[2](1), vertex[2](2));
+ fprintf(fp, " endloop\n");
+ fprintf(fp, " endfacet\n");
+ }
+
+ fprintf(fp, "endsolid %s\n", label);
+ fclose(fp);
+ return true;
+}
+
+bool its_write_stl_binary(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices)
+{
+ FILE *fp = boost::nowide::fopen(file, "wb");
+ if (fp == nullptr) {
+ BOOST_LOG_TRIVIAL(error) << "its_write_stl_binary: Couldn't open " << file << " for writing";
+ return false;
+ }
+
+ {
+ static constexpr const int header_size = 80;
+ std::vector<char> header(header_size, 0);
+ if (int header_len = std::min((label == nullptr) ? 0 : int(strlen(label)), header_size); header_len > 0)
+ ::memcpy(header.data(), label, header_len);
+ ::fwrite(header.data(), header_size, 1, fp);
+ }
+
+ uint32_t nfaces = indices.size();
+ big_endian_reverse_quads(reinterpret_cast<char*>(&nfaces), 4);
+ ::fwrite(&nfaces, 4, 1, fp);
+
+ stl_facet f;
+ f.extra[0] = 0;
+ f.extra[1] = 0;
+ for (const stl_triangle_vertex_indices face : indices) {
+ f.vertex[0] = vertices[face(0)];
+ f.vertex[1] = vertices[face(1)];
+ f.vertex[2] = vertices[face(2)];
+ f.normal = (f.vertex[1] - f.vertex[0]).cross(f.vertex[2] - f.vertex[1]).normalized();
+ big_endian_reverse_quads(reinterpret_cast<char*>(&f), 48);
+ fwrite(&f, 50, 1, fp);
+ }
+
+ fclose(fp);
+ return true;
+}
+
} // namespace Slic3r
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index c463af5a2..d46fb4a8b 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -15,25 +15,79 @@ namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
-typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
+
+struct TriangleMeshStats {
+ // Mesh metrics.
+ uint32_t number_of_facets = 0;
+ stl_vertex max = stl_vertex::Zero();
+ stl_vertex min = stl_vertex::Zero();
+ stl_vertex size = stl_vertex::Zero();
+ float volume = -1.f;
+ int number_of_parts = 0;
+
+ // Mesh errors, remaining.
+ int open_edges = 0;
+
+ // Mesh errors, fixed.
+ // How many edges were united by merging their end points with some other end points in epsilon neighborhood?
+ int edges_fixed = 0;
+ // How many degenerate faces were removed?
+ int degenerate_facets = 0;
+ // How many faces were removed during fixing? Includes degenerate_faces and disconnected faces.
+ int facets_removed = 0;
+ // New faces could only be created with stl_fill_holes() and we ditched stl_fill_holes(), because mostly it does more harm than good.
+ //int facets_added = 0;
+ // How many facets were revesed? Faces are reversed by admesh while it connects patches of triangles togeter and a flipped triangle is encountered.
+ // Also the facets are reversed when a negative volume is corrected by flipping all facets.
+ int facets_reversed = 0;
+ // Edges shared by two triangles, oriented incorrectly.
+ int backwards_edges = 0;
+
+ void clear() { *this = TriangleMeshStats(); }
+
+ TriangleMeshStats merge(const TriangleMeshStats &rhs) const {
+ if (this->number_of_facets == 0)
+ return rhs;
+ else if (rhs.number_of_facets == 0)
+ return *this;
+ else {
+ TriangleMeshStats out;
+ out.number_of_facets = this->number_of_facets + rhs.number_of_facets;
+ out.min = this->min.cwiseMin(rhs.min);
+ out.max = this->max.cwiseMax(rhs.max);
+ out.size = out.max - out.min;
+ out.number_of_parts = this->number_of_parts + rhs.number_of_parts;
+ out.open_edges = this->open_edges + rhs.open_edges;
+ out.volume = this->volume + rhs.volume;
+ out.edges_fixed = this->edges_fixed + rhs.edges_fixed;
+ out.degenerate_facets = this->degenerate_facets + rhs.degenerate_facets;
+ out.facets_removed = this->facets_removed + rhs.facets_removed;
+ out.facets_reversed = this->facets_reversed + rhs.facets_reversed;
+ out.backwards_edges = this->backwards_edges + rhs.backwards_edges;
+ return out;
+ }
+ }
+
+ bool manifold() const { return open_edges == 0; }
+ bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; }
+};
class TriangleMesh
{
public:
- TriangleMesh() : repaired(false) {}
- TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets);
+ TriangleMesh() = default;
+ TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces);
+ TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces);
explicit TriangleMesh(const indexed_triangle_set &M);
- void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
- bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
- bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
- bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
- void repair(bool update_shared_vertices = true);
+ explicit TriangleMesh(indexed_triangle_set &&M);
+ void clear() { this->its.clear(); this->m_stats.clear(); }
+ bool ReadSTLFile(const char* input_file, bool repair = true);
+ bool write_ascii(const char* output_file);
+ bool write_binary(const char* output_file);
float volume();
- void check_topology();
- bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
void WriteOBJFile(const char* output_file) const;
void scale(float factor);
- void scale(const Vec3d &versor);
+ void scale(const Vec3f &versor);
void translate(float x, float y, float z);
void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis);
@@ -41,51 +95,52 @@ public:
void rotate_x(float angle) { this->rotate(angle, X); }
void rotate_y(float angle) { this->rotate(angle, Y); }
void rotate_z(float angle) { this->rotate(angle, Z); }
- void mirror(const Axis &axis);
+ void mirror(const Axis axis);
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& t, bool fix_left_handed = false);
+ // Flip triangles, negate volume.
+ void flip_triangles();
void align_to_origin();
void rotate(double angle, Point* center);
- TriangleMeshPtrs split() const;
+ std::vector<TriangleMesh> split() const;
void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const;
- const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); }
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
Polygon convex_hull();
BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
// Return the size of the mesh in coordinates.
- Vec3d size() const { return stl.stats.size.cast<double>(); }
+ Vec3d size() const { return m_stats.size.cast<double>(); }
/// Return the center of the related bounding box.
Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
// Slice this mesh at the provided Z levels and return the vector
std::vector<ExPolygons> slice(const std::vector<double>& z) const;
- void reset_repair_stats();
- bool needed_repair() const;
- void require_shared_vertices();
- bool has_shared_vertices() const { return ! this->its.vertices.empty(); }
- size_t facets_count() const { return this->stl.stats.number_of_facets; }
+ size_t facets_count() const { assert(m_stats.number_of_facets == this->its.indices.size()); return m_stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; }
- bool is_splittable() const;
+ bool repaired() const;
+ bool is_splittable() const;
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
size_t memsize() const;
+
+ // Used by the Undo / Redo stack, legacy interface. As of now there is nothing cached at TriangleMesh,
+ // but we may decide to cache some data in the future (for example normals), thus we keep the interface in place.
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
- size_t release_optional();
+ size_t release_optional() { return 0; }
// Restore optional data possibly released by release_optional().
- void restore_optional();
+ void restore_optional() {}
- stl_file stl;
+ const TriangleMeshStats& stats() const { return m_stats; }
+
indexed_triangle_set its;
- bool repaired;
private:
- std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
+ TriangleMeshStats m_stats;
};
// Index of face indices incident with a vertex index.
@@ -145,8 +200,18 @@ bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filenam
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
+std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
+// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
+bool its_number_of_patches(const indexed_triangle_set &its);
+bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
+// Same as its_number_of_patches(its) > 1, but faster.
bool its_is_splittable(const indexed_triangle_set &its);
+bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
+
+// Calculate number of unconnected face edges. There should be no unconnected edge in a manifold mesh.
+size_t its_num_open_edges(const indexed_triangle_set &its);
+size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors);
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);
@@ -205,16 +270,32 @@ void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
-indexed_triangle_set its_make_cube(double x, double y, double z);
-TriangleMesh make_cube(double x, double y, double z);
-
-// Generate a TriangleMesh of a cylinder
-indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
-
-indexed_triangle_set its_make_sphere(double rho, double fa=(2*PI/360));
-TriangleMesh make_cone(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
+std::vector<Vec3f> its_face_normals(const indexed_triangle_set &its);
+inline Vec3f face_normal(const stl_vertex vertex[3]) { return (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized(); }
+inline Vec3f face_normal_normalized(const stl_vertex vertex[3]) { return face_normal(vertex).normalized(); }
+inline Vec3f its_face_normal(const indexed_triangle_set &its, const stl_triangle_vertex_indices face)
+ { const stl_vertex vertices[3] { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; return face_normal_normalized(vertices); }
+inline Vec3f its_face_normal(const indexed_triangle_set &its, const int face_idx)
+ { return its_face_normal(its, its.indices[face_idx]); }
+
+indexed_triangle_set its_make_cube(double x, double y, double z);
+indexed_triangle_set its_make_prism(float width, float length, float height);
+indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
+indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
+indexed_triangle_set its_make_pyramid(float base, float height);
+indexed_triangle_set its_make_sphere(double radius, double fa);
+
+inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
+inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
+inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; }
+inline TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)) { return TriangleMesh(its_make_cone(r, h, fa)); }
+inline TriangleMesh make_pyramid(float base, float height) { return TriangleMesh(its_make_pyramid(base, height)); }
+inline TriangleMesh make_sphere(double rho, double fa=(2*PI/360)) { return TriangleMesh(its_make_sphere(rho, fa)); }
+
+bool its_write_stl_ascii(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
+inline bool its_write_stl_ascii(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_ascii(file, label, its.indices, its.vertices); }
+bool its_write_stl_binary(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
+inline bool its_write_stl_binary(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_binary(file, label, its.indices, its.vertices); }
inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); }
inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
@@ -239,18 +320,12 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
namespace cereal {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
- stl_allocate(&stl);
- archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
- stl_get_size(&stl);
- mesh.repair();
+ archive.loadBinary(reinterpret_cast<char*>(const_cast<Slic3r::TriangleMeshStats*>(&mesh.stats())), sizeof(Slic3r::TriangleMeshStats));
+ archive(mesh.its.indices, mesh.its.vertices);
}
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
- const stl_file& stl = mesh.stl;
- archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
- archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
+ archive.saveBinary(reinterpret_cast<const char*>(&mesh.stats()), sizeof(Slic3r::TriangleMeshStats));
+ archive(mesh.its.indices, mesh.its.vertices);
}
}
diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp
index aa2763968..d3c9a49b5 100644
--- a/src/libslic3r/TriangleMeshSlicer.cpp
+++ b/src/libslic3r/TriangleMeshSlicer.cpp
@@ -1967,7 +1967,8 @@ static void triangulate_slice(
int num_original_vertices,
// Z height of the slice.
float z,
- bool triangulate)
+ bool triangulate,
+ bool normals_down)
{
sort_remove_duplicates(slice_vertices);
@@ -1988,7 +1989,7 @@ static void triangulate_slice(
std::vector<int> map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
int i = 0;
int k = 0;
- for (; i < int(map_vertex_to_index.size()); ++ i) {
+ for (; i < int(map_vertex_to_index.size());) {
map_vertex_to_index[k ++] = map_vertex_to_index[i];
const Vec2f &ipos = map_vertex_to_index[i].first;
const int iidx = map_vertex_to_index[i].second;
@@ -2003,6 +2004,7 @@ static void triangulate_slice(
// map to the first vertex
map_duplicate_vertex[jidx - num_original_vertices] = iidx;
}
+ i = j;
}
map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
for (stl_triangle_vertex_indices &f : its.indices)
@@ -2013,7 +2015,7 @@ static void triangulate_slice(
if (triangulate) {
size_t idx_vertex_new_first = its.vertices.size();
- Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, true);
+ Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, normals_down);
for (size_t i = 0; i < triangles.size(); ) {
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
@@ -2049,6 +2051,33 @@ static void triangulate_slice(
// its_remove_degenerate_faces(its);
}
+void project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ Polygons *out_top,
+ Polygons *out_bottom,
+ std::function<void()> throw_on_cancel)
+{
+ std::vector<Polygons> top, bottom;
+ std::vector<float> zs { -1e10, 1e10 };
+ slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel);
+ if (out_top)
+ *out_top = std::move(top.front());
+ if (out_bottom)
+ *out_bottom = std::move(bottom.back());
+}
+
+Polygons project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ std::function<void()> throw_on_cancel)
+{
+ std::vector<Polygons> top, bottom;
+ std::vector<float> zs { -1e10, 1e10 };
+ slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel);
+ return union_(top.front(), bottom.back());
+}
+
void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower, bool triangulate_caps)
{
assert(upper || lower);
@@ -2069,6 +2098,10 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
lower->indices.reserve(mesh.indices.size());
}
+#ifndef NDEBUG
+ size_t num_open_edges_old = triangulate_caps ? its_num_open_edges(mesh) : 0;
+#endif // NDEBUG
+
// To triangulate the caps after slicing.
IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices;
@@ -2135,13 +2168,14 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
// get vertices starting from the isolated one
int iv = isolated_vertex;
stl_vertex v0v1, v2v0;
- assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id ||facets_edge_ids[facet_idx](iv) == line.edge_b_id);
+ assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
- v0v1 = to_3d(unscaled<float>(line.a), z);
- v2v0 = to_3d(unscaled<float>(line.b), z);
+ // Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
+ v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
+ v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
} else {
- v0v1 = to_3d(unscaled<float>(line.b), z);
- v2v0 = to_3d(unscaled<float>(line.a), z);
+ v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
+ v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
}
const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[iv];
@@ -2195,11 +2229,25 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
}
}
- if (upper != nullptr)
- triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
+ if (upper != nullptr) {
+ triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
+#ifndef NDEBUG
+ if (triangulate_caps) {
+ size_t num_open_edges_new = its_num_open_edges(*upper);
+ assert(num_open_edges_new <= num_open_edges_old);
+ }
+#endif // NDEBUG
+ }
- if (lower != nullptr)
- triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
+ if (lower != nullptr) {
+ triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP);
+#ifndef NDEBUG
+ if (triangulate_caps) {
+ size_t num_open_edges_new = its_num_open_edges(*lower);
+ assert(num_open_edges_new <= num_open_edges_old);
+ }
+#endif // NDEBUG
+ }
}
-}
+} // namespace Slic3r
diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp
index 7ea7ac3a9..5e08b58e7 100644
--- a/src/libslic3r/TriangleMeshSlicer.hpp
+++ b/src/libslic3r/TriangleMeshSlicer.hpp
@@ -98,7 +98,21 @@ void slice_mesh_slabs(
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel);
-void cut_mesh(
+// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
+void project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ Polygons *out_top,
+ Polygons *out_bottom,
+ std::function<void()> throw_on_cancel);
+
+// Project mesh into 2D polygons.
+Polygons project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ std::function<void()> throw_on_cancel);
+
+void cut_mesh(
const indexed_triangle_set &mesh,
float z,
indexed_triangle_set *upper,
diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp
index 6178bcec8..987ef1c0a 100644
--- a/src/libslic3r/TriangleSelector.cpp
+++ b/src/libslic3r/TriangleSelector.cpp
@@ -9,16 +9,6 @@
namespace Slic3r {
-static inline Vec3i root_neighbors(const TriangleMesh &mesh, int triangle_id)
-{
- Vec3i neighbors;
- const stl_neighbors& neighbors_src = mesh.stl.neighbors_start[triangle_id];
- for (int i = 0; i < 3; ++i)
- // Refuse a neighbor with a flipped normal.
- neighbors(i) = neighbors_src.neighbor[i];
- return neighbors;
-}
-
#ifndef NDEBUG
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
{
@@ -129,7 +119,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
if (!m_triangles[facet_idx].valid())
return -1;
- Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+ Vec3i neighbors = m_neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
}
@@ -167,7 +157,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
if (! visited[facet]) {
if (select_triangle(facet, new_state, triangle_splitting)) {
// add neighboring facets to list to be proccessed later
- for (int neighbor_idx : m_mesh->stl.neighbors_start[facet].neighbor) {
+ for (int neighbor_idx : m_neighbors[facet]) {
if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
facets_to_check.push_back(neighbor_idx);
}
@@ -213,12 +203,12 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
if (current_facet < m_orig_size_indices)
// Propagate over the original triangles.
- for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
+ for (int neighbor_idx : m_neighbors[current_facet]) {
assert(neighbor_idx >= -1);
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
- const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
- const Vec3f &n2 = m_mesh->stl.facet_start[m_triangles[current_facet].source_triangle].normal;
+ const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
+ const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
if (std::clamp(n1.dot(n2), 0.f, 1.f) >= facet_angle_limit)
facet_queue.push(neighbor_idx);
}
@@ -261,7 +251,7 @@ std::pair<std::vector<Vec3i>, std::vector<Vec3i>> TriangleSelector::precompute_a
std::vector<Vec3i> neighbors(m_triangles.size(), Vec3i(-1, -1, -1));
std::vector<Vec3i> neighbors_propagated(m_triangles.size(), Vec3i(-1, -1, -1));
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
- neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
+ neighbors[facet_idx] = m_neighbors[facet_idx];
neighbors_propagated[facet_idx] = neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
if (m_triangles[facet_idx].is_split())
@@ -403,7 +393,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type,
if (! m_triangles[facet_idx].valid())
return false;
- Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+ Vec3i neighbors = m_neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
@@ -906,14 +896,10 @@ bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
bool TriangleSelector::faces_camera(int facet) const
{
assert(facet < m_orig_size_indices);
- // The normal is cached in mesh->stl, use it.
- Vec3f normal = m_mesh->stl.facet_start[facet].normal;
-
- if (! m_cursor.uniform_scaling) {
- // Transform the normal into world coords.
- normal = m_cursor.trafo_normal * normal;
- }
- return (normal.dot(m_cursor.dir) < 0.);
+ Vec3f n = m_face_normals[facet];
+ if (! m_cursor.uniform_scaling)
+ n = m_cursor.trafo_normal * n;
+ return n.dot(m_cursor.dir) < 0.;
}
@@ -1094,7 +1080,7 @@ void TriangleSelector::garbage_collect()
}
TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
- : m_mesh{&mesh}
+ : m_mesh{mesh}, m_neighbors(its_face_neighbors(mesh.its)), m_face_normals(its_face_normals(mesh.its))
{
reset();
}
@@ -1107,16 +1093,17 @@ void TriangleSelector::reset()
m_invalid_triangles = 0;
m_free_triangles_head = -1;
m_free_vertices_head = -1;
- m_vertices.reserve(m_mesh->its.vertices.size());
- for (const stl_vertex& vert : m_mesh->its.vertices)
+ m_vertices.reserve(m_mesh.its.vertices.size());
+ for (const stl_vertex& vert : m_mesh.its.vertices)
m_vertices.emplace_back(vert);
- m_triangles.reserve(m_mesh->its.indices.size());
- for (size_t i = 0; i < m_mesh->its.indices.size(); ++i) {
- const stl_triangle_vertex_indices &ind = m_mesh->its.indices[i];
+ m_triangles.reserve(m_mesh.its.indices.size());
+ for (size_t i = 0; i < m_mesh.its.indices.size(); ++i) {
+ const stl_triangle_vertex_indices &ind = m_mesh.its.indices[i];
push_triangle(ind[0], ind[1], ind[2], int(i));
}
m_orig_size_vertices = int(m_vertices.size());
m_orig_size_indices = int(m_triangles.size());
+
}
@@ -1286,7 +1273,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
}
for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
- this->get_facets_strict_recursive(m_triangles[itriangle], root_neighbors(*m_mesh, itriangle), state, out.indices);
+ this->get_facets_strict_recursive(m_triangles[itriangle], m_neighbors[itriangle], state, out.indices);
for (auto &triangle : out.indices)
for (int i = 0; i < 3; ++ i)
@@ -1398,7 +1385,7 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
std::vector<Vec2i> TriangleSelector::get_seed_fill_contour() const {
std::vector<Vec2i> edges_out;
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
- const Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+ const Vec3i neighbors = m_neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out);
}
@@ -1522,10 +1509,10 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
// Reserve number of triangles as if each triangle was saved with 4 bits.
// With MMU painting this estimate may be somehow low, but better than nothing.
- m_triangles.reserve(std::max(m_mesh->its.indices.size(), data.second.size() / 4));
+ m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4));
// Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
// Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
- m_vertices.reserve(std::max(m_mesh->its.vertices.size(), m_triangles.size() / 2));
+ m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
// Vector to store all parents that have offsprings.
struct ProcessingInfo {
@@ -1565,7 +1552,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
if (is_split) {
// root is split, add it into list of parents and split it.
// then go to the next.
- Vec3i neighbors = root_neighbors(*m_mesh, triangle_id);
+ Vec3i neighbors = m_neighbors[triangle_id];
parents.push_back({triangle_id, neighbors, 0, num_of_children});
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp
index 2ab053123..05a78e2ee 100644
--- a/src/libslic3r/TriangleSelector.hpp
+++ b/src/libslic3r/TriangleSelector.hpp
@@ -161,7 +161,9 @@ protected:
// Lists of vertices and triangles, both original and new
std::vector<Vertex> m_vertices;
std::vector<Triangle> m_triangles;
- const TriangleMesh* m_mesh;
+ const TriangleMesh &m_mesh;
+ const std::vector<Vec3i> m_neighbors;
+ const std::vector<Vec3f> m_face_normals;
// Number of invalid triangles (to trigger garbage collection).
int m_invalid_triangles;
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index d4f53013f..9ed12a607 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -256,6 +256,19 @@ template<typename T> struct IsTriviallyCopyable { static constexpr bool value =
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
#endif
+// A very lightweight ROII wrapper around C FILE.
+// The old C file API is much faster than C++ streams, thus they are recommended for processing large / huge files.
+struct FilePtr {
+ FilePtr(FILE *f) : f(f) {}
+ ~FilePtr() { this->close(); }
+ void close() {
+ if (this->f) {
+ ::fclose(this->f);
+ this->f = nullptr;
+ }
+ }
+ FILE* f = nullptr;
+};
class ScopeGuard
{
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index c5dbdac9c..efffcfe24 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -889,7 +889,7 @@ std::string string_printf(const char *format, ...)
std::string header_slic3r_generated()
{
- return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
+ return std::string("generated by PrusaSlicer " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
}
std::string header_gcodeviewer_generated()
@@ -906,6 +906,7 @@ unsigned get_current_pid()
#endif
}
+//FIXME this has potentially O(n^2) time complexity!
std::string xml_escape(std::string text, bool is_marked/* = false*/)
{
std::string::size_type pos = 0;
diff --git a/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in b/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in
index eed737cb7..518f6b7b6 100644
--- a/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in
+++ b/src/platform/msw/PrusaSlicer-gcodeviewer.rc.in
@@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer"
- VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
+ VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
VALUE "OriginalFilename", "prusa-gcodeviewer.exe"
}
}
diff --git a/src/platform/msw/PrusaSlicer.rc.in b/src/platform/msw/PrusaSlicer.rc.in
index a4520c6d7..d7f860204 100644
--- a/src/platform/msw/PrusaSlicer.rc.in
+++ b/src/platform/msw/PrusaSlicer.rc.in
@@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@"
- VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
+ VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
VALUE "OriginalFilename", "prusa-slicer.exe"
}
}
diff --git a/src/platform/osx/Info.plist.in b/src/platform/osx/Info.plist.in
index 46858bb29..f1de1d24b 100644
--- a/src/platform/osx/Info.plist.in
+++ b/src/platform/osx/Info.plist.in
@@ -5,7 +5,7 @@
<key>CFBundleExecutable</key>
<string>@SLIC3R_APP_KEY@</string>
<key>CFBundleGetInfoString</key>
- <string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2020 Prusa Reseach</string>
+ <string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2021 Prusa Reseach</string>
<key>CFBundleIconFile</key>
<string>PrusaSlicer.icns</string>
<key>CFBundleName</key>
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 34f1a188d..29d1fae29 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -179,6 +179,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/SLAImportJob.hpp
GUI/Jobs/SLAImportJob.cpp
GUI/Jobs/ProgressIndicator.hpp
+ GUI/Jobs/NotificationProgressIndicator.hpp
+ GUI/Jobs/NotificationProgressIndicator.cpp
GUI/ProgressStatusBar.hpp
GUI/ProgressStatusBar.cpp
GUI/Mouse3DController.cpp
diff --git a/src/slic3r/GUI/2DBed.cpp b/src/slic3r/GUI/2DBed.cpp
index e184b43fc..e7c2cbe54 100644
--- a/src/slic3r/GUI/2DBed.cpp
+++ b/src/slic3r/GUI/2DBed.cpp
@@ -92,7 +92,7 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
}
- polylines = intersection_pl(polylines, (Polygons)bed_polygon);
+ polylines = intersection_pl(polylines, bed_polygon);
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
for (auto pl : polylines)
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index ba72c432d..dfc10658d 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -9,9 +9,7 @@
#include "3DScene.hpp"
#include "GLShader.hpp"
#include "GUI_App.hpp"
-#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "Plater.hpp"
-#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
@@ -25,9 +23,7 @@
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ClipperUtils.hpp"
-#if ENABLE_SINKING_CONTOURS
#include "libslic3r/Tesselate.hpp"
-#endif // ENABLE_SINKING_CONTOURS
#include <stdio.h>
#include <stdlib.h>
@@ -158,22 +154,27 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh)
}
else {
#endif // ENABLE_SMOOTH_NORMALS
- this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
-
- unsigned int vertices_count = 0;
- for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
- const stl_facet& facet = mesh.stl.facet_start[i];
- for (int j = 0; j < 3; ++j)
- this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
-
- this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
- vertices_count += 3;
- }
+ this->load_its_flat_shading(mesh.its);
#if ENABLE_SMOOTH_NORMALS
}
#endif // ENABLE_SMOOTH_NORMALS
}
+void GLIndexedVertexArray::load_its_flat_shading(const indexed_triangle_set &its)
+{
+ this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * its.indices.size());
+ unsigned int vertices_count = 0;
+ for (int i = 0; i < int(its.indices.size()); ++ i) {
+ stl_triangle_vertex_indices face = its.indices[i];
+ stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
+ stl_vertex n = face_normal_normalized(vertex);
+ for (int j = 0; j < 3; ++j)
+ this->push_geometry(vertex[j](0), vertex[j](1), vertex[j](2), n(0), n(1), n(2));
+ this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
+ vertices_count += 3;
+ }
+}
+
void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
{
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
@@ -288,7 +289,6 @@ void GLIndexedVertexArray::render(
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
-#if ENABLE_SINKING_CONTOURS
const float GLVolume::SinkingContours::HalfWidth = 0.25f;
void GLVolume::SinkingContours::render()
@@ -313,7 +313,6 @@ void GLVolume::SinkingContours::update()
m_shift = Vec3d::Zero();
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
- assert(mesh.has_shared_vertices());
m_model.reset();
GUI::GLModel::InitializationData init_data;
@@ -357,7 +356,6 @@ void GLVolume::SinkingContours::update()
else
m_model.reset();
}
-#endif // ENABLE_SINKING_CONTOURS
const std::array<float, 4> GLVolume::SELECTED_COLOR = { 0.0f, 1.0f, 0.0f, 1.0f };
const std::array<float, 4> GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
@@ -376,12 +374,8 @@ const std::array<std::array<float, 4>, 4> GLVolume::MODEL_COLOR = { {
} };
GLVolume::GLVolume(float r, float g, float b, float a)
- : m_transformed_bounding_box_dirty(true)
- , m_sla_shift_z(0.0)
- , m_transformed_convex_hull_bounding_box_dirty(true)
-#if ENABLE_SINKING_CONTOURS
+ : m_sla_shift_z(0.0)
, m_sinking_contours(*this)
-#endif // ENABLE_SINKING_CONTOURS
// geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0)
@@ -399,9 +393,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, force_transparent(false)
, force_native_color(false)
, force_neutral_color(false)
-#if ENABLE_SINKING_CONTOURS
, force_sinking_contours(false)
-#endif // ENABLE_SINKING_CONTOURS
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
@@ -506,32 +498,27 @@ bool GLVolume::is_left_handed() const
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
{
- const BoundingBoxf3& box = bounding_box();
- assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
-
- BoundingBoxf3* transformed_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_bounding_box);
- bool* transformed_bounding_box_dirty = const_cast<bool*>(&m_transformed_bounding_box_dirty);
- if (*transformed_bounding_box_dirty) {
- *transformed_bounding_box = box.transformed(world_matrix());
- *transformed_bounding_box_dirty = false;
+ if (!m_transformed_bounding_box.has_value()) {
+ const BoundingBoxf3& box = bounding_box();
+ assert(box.defined || box.min.x() >= box.max.x() || box.min.y() >= box.max.y() || box.min.z() >= box.max.z());
+ std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_bounding_box);
+ *trans_box = box.transformed(world_matrix());
}
- return *transformed_bounding_box;
+ return *m_transformed_bounding_box;
}
const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
{
- BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_convex_hull_bounding_box);
- bool* transformed_convex_hull_bounding_box_dirty = const_cast<bool*>(&m_transformed_convex_hull_bounding_box_dirty);
- if (*transformed_convex_hull_bounding_box_dirty) {
- *transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
- *transformed_convex_hull_bounding_box_dirty = false;
+ if (!m_transformed_convex_hull_bounding_box.has_value()) {
+ std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_convex_hull_bounding_box);
+ *trans_box = transformed_convex_hull_bounding_box(world_matrix());
}
- return *transformed_convex_hull_bounding_box;
+ return *m_transformed_convex_hull_bounding_box;
}
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
{
- return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
+ return (m_convex_hull && ! m_convex_hull->empty()) ?
m_convex_hull->transformed_bounding_box(trafo) :
bounding_box().transformed(trafo);
}
@@ -605,12 +592,10 @@ bool GLVolume::is_below_printbed() const
return transformed_convex_hull_bounding_box().max.z() < 0.0;
}
-#if ENABLE_SINKING_CONTOURS
void GLVolume::render_sinking_contours()
{
m_sinking_contours.render();
}
-#endif // ENABLE_SINKING_CONTOURS
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
@@ -733,21 +718,20 @@ int GLVolumeCollection::load_wipe_tower_preview(
float min_width = 30.f;
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model
// are shifted so that the front edge has y=0 and centerline of the back edge has y=depth:
- Pointf3s points;
- std::vector<Vec3i> facets;
float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
- int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
- {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
- {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
+ static constexpr const int out_facets_idx[][3] = {
+ { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
+ { 8, 10, 14 }, { 3, 12, 4 }, { 3, 13, 12 }, { 6, 13, 3 }, { 6, 14, 13 }, { 7, 14, 6 }, { 7, 15, 14 }, { 2, 15, 7 }, { 2, 8, 15 }, { 1, 8, 2 }, { 1, 9, 8 },
+ { 0, 9, 1 }, { 0, 10, 9 }, { 5, 10, 0 }, { 5, 11, 10 }, { 4, 11, 5 }, { 4, 12, 11 } };
+ indexed_triangle_set its;
for (int i = 0; i < 16; ++i)
- points.emplace_back(out_points_idx[i][0] / (100.f / min_width),
- out_points_idx[i][1] + depth, out_points_idx[i][2]);
- for (int i = 0; i < 28; ++i)
- facets.emplace_back(out_facets_idx[i][0],
- out_facets_idx[i][1],
- out_facets_idx[i][2]);
- TriangleMesh tooth_mesh(points, facets);
+ its.vertices.emplace_back(out_points_idx[i][0] / (100.f / min_width),
+ out_points_idx[i][1] + depth, out_points_idx[i][2]);
+ its.indices.reserve(28);
+ for (const int *face : out_facets_idx)
+ its.indices.emplace_back(face);
+ TriangleMesh tooth_mesh(std::move(its));
// We have the mesh ready. It has one tooth and width of min_width. We will now
// append several of these together until we are close to the required width
@@ -758,7 +742,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
tooth_mesh.translate(min_width, 0.f, 0.f);
}
- mesh.scale(Vec3d(width / (n * min_width), 1.f, height)); // Scaling to proper width
+ mesh.scale(Vec3f(width / (n * min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
@@ -770,7 +754,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
volumes.emplace_back(new GLVolume(color));
GLVolume& v = *volumes.back();
- v.indexed_vertex_array.load_mesh(mesh);
+ v.indexed_vertex_array.load_mesh(mesh);
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
@@ -834,11 +818,9 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
{
-#if ENABLE_SINKING_CONTOURS
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
if (to_render.empty())
return;
-#endif // ENABLE_SINKING_CONTOURS
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
if (shader == nullptr)
@@ -853,7 +835,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
if (disable_cullface)
glsafe(::glDisable(GL_CULL_FACE));
-#if ENABLE_SINKING_CONTOURS
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
@@ -903,7 +884,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
}
- if (m_show_sinking_contours)
+ if (m_show_sinking_contours) {
for (GLVolumeWithIdAndZ& volume : to_render) {
// render sinking contours of hovered/displaced volumes
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
@@ -915,48 +896,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
shader->start_using();
}
}
-#else
- glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
- glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
-
- shader->set_uniform("print_box.min", m_print_box_min, 3);
- shader->set_uniform("print_box.max", m_print_box_max, 3);
- shader->set_uniform("z_range", m_z_range, 2);
- shader->set_uniform("clipping_plane", m_clipping_plane, 4);
- shader->set_uniform("slope.normal_z", m_slope.normal_z);
-
-#if ENABLE_ENVIRONMENT_MAP
- unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
- bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
- shader->set_uniform("use_environment_tex", use_environment_texture);
- if (use_environment_texture)
- glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
-#endif // ENABLE_ENVIRONMENT_MAP
- glcheck();
-
- GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
- for (GLVolumeWithIdAndZ& volume : to_render) {
- volume.first->set_render_color();
- shader->set_uniform("uniform_color", volume.first->render_color);
- shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
- shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
- shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
- shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
- volume.first->render();
}
-#if ENABLE_ENVIRONMENT_MAP
- if (use_environment_texture)
- glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
-#endif // ENABLE_ENVIRONMENT_MAP
-
- glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
- glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
-
- glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
- glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-#endif // ENABLE_SINKING_CONTOURS
-
if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE));
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index 1a85cc41e..78b9a96d9 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -8,11 +8,10 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Geometry.hpp"
-#if ENABLE_SINKING_CONTOURS
#include "GLModel.hpp"
-#endif // ENABLE_SINKING_CONTOURS
#include <functional>
+#include <optional>
#define HAS_GLSAFE
#ifdef HAS_GLSAFE
@@ -128,6 +127,8 @@ public:
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
#endif // ENABLE_SMOOTH_NORMALS
+ void load_its_flat_shading(const indexed_triangle_set &its);
+
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
inline void reserve(size_t sz) {
@@ -258,9 +259,7 @@ public:
enum EHoverState : unsigned char
{
HS_None,
-#if ENABLE_SINKING_CONTOURS
HS_Hover,
-#endif // ENABLE_SINKING_CONTOURS
HS_Select,
HS_Deselect
};
@@ -275,17 +274,12 @@ private:
// Shift in z required by sla supports+pad
double m_sla_shift_z;
// Bounding box of this volume, in unscaled coordinates.
- BoundingBoxf3 m_transformed_bounding_box;
- // Whether or not is needed to recalculate the transformed bounding box.
- bool m_transformed_bounding_box_dirty;
+ std::optional<BoundingBoxf3> m_transformed_bounding_box;
// Convex hull of the volume, if any.
std::shared_ptr<const TriangleMesh> m_convex_hull;
// Bounding box of this volume, in unscaled coordinates.
- BoundingBoxf3 m_transformed_convex_hull_bounding_box;
- // Whether or not is needed to recalculate the transformed convex hull bounding box.
- bool m_transformed_convex_hull_bounding_box_dirty;
+ std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
-#if ENABLE_SINKING_CONTOURS
class SinkingContours
{
static const float HalfWidth;
@@ -303,7 +297,6 @@ private:
};
SinkingContours m_sinking_contours;
-#endif // ENABLE_SINKING_CONTOURS
public:
// Color of the triangles / quads held by this volume.
@@ -365,10 +358,8 @@ public:
bool force_native_color : 1;
// Whether or not render this volume in neutral
bool force_neutral_color : 1;
-#if ENABLE_SINKING_CONTOURS
// Whether or not to force rendering of sinking contours
bool force_sinking_contours : 1;
-#endif // ENABLE_SINKING_CONTOURS
};
// Is mouse or rectangle selection over this object to select/deselect it ?
@@ -490,16 +481,14 @@ public:
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
- void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
+ void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
bool is_sla_support() const;
bool is_sla_pad() const;
bool is_sinking() const;
bool is_below_printbed() const;
-#if ENABLE_SINKING_CONTOURS
void render_sinking_contours();
-#endif // ENABLE_SINKING_CONTOURS
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp
index 2aae9270d..a6b99a08b 100644
--- a/src/slic3r/GUI/AboutDialog.cpp
+++ b/src/slic3r/GUI/AboutDialog.cpp
@@ -275,7 +275,7 @@ AboutDialog::AboutDialog()
"<html>"
"<body bgcolor= %1% link= %2%>"
"<font color=%3%>"
- "%4% &copy; 2016-2020 Prusa Research. <br />"
+ "%4% &copy; 2016-2021 Prusa Research. <br />"
"%5% &copy; 2011-2018 Alessandro Ranellucci. <br />"
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index b4de4a509..e93f32b03 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -209,8 +209,8 @@ void BackgroundSlicingProcess::process_sla()
void BackgroundSlicingProcess::thread_proc()
{
- set_current_thread_name("slic3r_BgSlcPcs");
- name_tbb_thread_pool_threads();
+ set_current_thread_name("slic3r_BgSlcPcs");
+ name_tbb_thread_pool_threads_set_locale();
assert(m_print != nullptr);
assert(m_print == m_fff_print || m_print == m_sla_print);
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 3f388f485..ee6e3d5ab 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -41,6 +41,7 @@
#include "format.hpp"
#include "MsgDialog.hpp"
#include "libslic3r/libslic3r.h"
+#include "UnsavedChangesDialog.hpp"
#if defined(__linux__) && defined(__WXGTK3__)
#define wxLinux_gtk3 true
@@ -741,10 +742,10 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
- wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers.");
+ wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
wxString text;
if (all_printers) {
- wxString second_line = _L("All installed printers are compatible with the selected filament.");
+ wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
text = wxString::Format(
"<html>"
"<style>"
@@ -764,7 +765,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
, second_line
);
} else {
- wxString second_line = _L("Only the following installed printers are compatible with the selected filament:");
+ wxString second_line = printer_names.empty() ? "" : format_wxstr(_L("Only the following installed printers are compatible with the selected %1%:"), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
text = wxString::Format(
"<html>"
"<style>"
@@ -2473,8 +2474,16 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin
return *diff.begin();
}
-bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
+bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes)
{
+ wxString header, caption = _L("Configuration is editing from ConfigWizard");
+ bool check_unsaved_preset_changes = page_welcome->reset_user_profile();
+ if (check_unsaved_preset_changes)
+ header = _L("All user presets will be deleted.");
+ int act_btns = UnsavedChangesDialog::ActionButtons::KEEP;
+ if (!check_unsaved_preset_changes)
+ act_btns |= UnsavedChangesDialog::ActionButtons::SAVE;
+
const auto enabled_vendors = appconfig_new.vendors();
// Install bundles from resources if needed:
@@ -2500,6 +2509,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
install_bundles.emplace_back(pair.first);
}
}
+ if (!check_unsaved_preset_changes)
+ if ((check_unsaved_preset_changes = install_bundles.size() > 0))
+ header = _L_PLURAL("New vendor was installed and one of its printer will be activated", "New vendors were installed and one of theirs printer will be activated", install_bundles.size());
#ifdef __linux__
// Desktop integration on Linux
@@ -2531,6 +2543,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?")))
return false;
+ if (check_unsaved_preset_changes &&
+ !wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
+ return false;
+
if (install_bundles.size() > 0) {
// Install bundles from resources.
// Don't create snapshot - we've already done that above if applicable.
@@ -2586,17 +2602,52 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
}
}
+ // if unsaved changes was not cheched till this moment
+ if (!check_unsaved_preset_changes) {
+ if ((check_unsaved_preset_changes = !preferred_model.empty())) {
+ header = _L("A new Printer was installed and it will be activated.");
+ if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
+ return false;
+ }
+ else if ((check_unsaved_preset_changes = enabled_vendors_old != enabled_vendors)) {
+ header = _L("Some Printers were uninstalled.");
+ if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
+ return false;
+ }
+ }
+
std::string first_added_filament, first_added_sla_material;
- auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
+ auto get_first_added_material_preset = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
if (appconfig_new.has_section(section_name)) {
// get first of new added preset names
const std::map<std::string, std::string>& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>();
first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name));
- app_config->set_section(section_name, appconfig_new.get_section(section_name));
}
};
- apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament);
- apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material);
+ get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
+ get_first_added_material_preset(AppConfig::SECTION_MATERIALS, first_added_sla_material);
+
+ // if unsaved changes was not cheched till this moment
+ if (!check_unsaved_preset_changes) {
+ if ((check_unsaved_preset_changes = !first_added_filament.empty() || !first_added_sla_material.empty())) {
+ header = format_wxstr(_L("A new %1% was installed and it will be activated."), !first_added_filament.empty() ? _L("Filament") : _L("SLA material"));
+ if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
+ return false;
+ }
+ else {
+ bool is_filaments_changed = app_config->get_section(AppConfig::SECTION_FILAMENTS) != appconfig_new.get_section(AppConfig::SECTION_FILAMENTS);
+ bool is_sla_materials_changed = app_config->get_section(AppConfig::SECTION_MATERIALS) != appconfig_new.get_section(AppConfig::SECTION_MATERIALS);
+ if ((check_unsaved_preset_changes = is_filaments_changed || is_sla_materials_changed)) {
+ header = format_wxstr(_L("Some %1% were uninstalled."), is_filaments_changed ? _L("Filaments") : _L("SLA materials"));
+ if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
+ return false;
+ }
+ }
+ }
+
+ // apply materials in app_config
+ for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
+ app_config->set_section(section_name, appconfig_new.get_section(section_name));
app_config->set_vendors(appconfig_new);
@@ -2624,10 +2675,16 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
page_mode->serialize_mode(app_config);
- preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
- {preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
+ if (check_unsaved_preset_changes)
+ preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
+ {preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
if (page_custom->custom_wanted()) {
+ // if unsaved changes was not cheched till this moment
+ if (!check_unsaved_preset_changes &&
+ !wxGetApp().check_and_keep_current_preset_changes(caption, _L("Custom printer was installed and it will be activated."), act_btns, &apply_keeped_changes))
+ return false;
+
page_firmware->apply_custom_config(*custom_config);
page_bed->apply_custom_config(*custom_config);
page_diams->apply_custom_config(*custom_config);
@@ -2851,8 +2908,13 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
p->set_start_page(start_page);
if (ShowModal() == wxID_OK) {
- if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater))
+ bool apply_keeped_changes = false;
+ if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes))
return false;
+
+ if (apply_keeped_changes)
+ app.apply_keeped_preset_modifications();
+
app.app_config->set_legacy_datadir(false);
app.update_mode();
app.obj_manipul()->update_ui_from_settings();
diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp
index 84def4202..d4b1fac04 100644
--- a/src/slic3r/GUI/ConfigWizard_private.hpp
+++ b/src/slic3r/GUI/ConfigWizard_private.hpp
@@ -611,7 +611,7 @@ struct ConfigWizard::priv
bool on_bnt_finish();
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
- bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
+ bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
// #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
#ifdef __linux__
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 7d7ed08a2..77679a1ad 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -1556,10 +1556,8 @@ void Control::OnMotion(wxMouseEvent& event)
event.Skip();
// Set tooltips with information for each icon
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (GUI::wxGetApp().is_editor())
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
- this->SetToolTip(get_tooltip(tick));
+ this->SetToolTip(get_tooltip(tick));
if (action) {
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
@@ -1821,7 +1819,8 @@ void Control::OnChar(wxKeyEvent& event)
void Control::OnRightDown(wxMouseEvent& event)
{
- if (HasCapture()) return;
+ if (HasCapture() || m_is_left_down)
+ return;
this->CaptureMouse();
const wxPoint pos = event.GetLogicalPosition(wxClientDC(this));
@@ -2099,7 +2098,7 @@ void Control::auto_color_change()
void Control::OnRightUp(wxMouseEvent& event)
{
- if (!HasCapture())
+ if (!HasCapture() || m_is_left_down)
return;
this->ReleaseMouse();
m_is_right_down = m_is_one_layer = false;
diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp
index 5fe86db27..533b59398 100644
--- a/src/slic3r/GUI/ExtraRenderers.cpp
+++ b/src/slic3r/GUI/ExtraRenderers.cpp
@@ -323,15 +323,19 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
c_editor->SetSelection(atoi(data.GetText().c_str()));
- // to avoid event propagation to other sidebar items
- c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
- evt.StopPropagation();
+
#ifdef __linux__
- // FinishEditing grabs new selection and triggers config update. We better call
- // it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
- this->FinishEditing();
-#endif
+ c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
+ // to avoid event propagation to other sidebar items
+ evt.StopPropagation();
+ // FinishEditing grabs new selection and triggers config update. We better call
+ // it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
+ this->FinishEditing();
});
+#else
+ // to avoid event propagation to other sidebar items
+ c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); });
+#endif
return c_editor;
}
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 06cfd0a1c..7dbb9d787 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -977,7 +977,7 @@ void Choice::BUILD() {
propagate_value();
} );
- temp->Bind(wxEVT_TEXT_ENTER, [this, temp](wxEvent& e) {
+ temp->Bind(wxEVT_TEXT_ENTER, [this](wxEvent& e) {
EnterPressed enter(this);
propagate_value();
} );
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index b1483dcdc..f33c8e165 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -294,7 +294,12 @@ void GCodeViewer::SequentialView::Marker::render() const
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":");
ImGui::SameLine();
char buf[1024];
- sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position(0), m_world_position(1), m_world_position(2));
+#if ENABLE_FIX_SEAMS_SYNCH
+ const Vec3f position = m_world_position + m_world_offset;
+ sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z());
+#else
+ sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position.x(), m_world_position.y(), m_world_position.z());
+#endif // ENABLE_FIX_SEAMS_SYNCH
imgui.text(std::string(buf));
// force extra frame to automatically update window size
@@ -311,7 +316,7 @@ void GCodeViewer::SequentialView::Marker::render() const
ImGui::PopStyleVar();
}
-void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
+void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends)
{
assert(! m_file.is_open());
if (m_file.is_open())
@@ -590,9 +595,94 @@ GCodeViewer::GCodeViewer()
set_toolpath_move_type_visible(EMoveType::Extrude, true);
#endif // !ENABLE_SEAMS_USING_MODELS
+ m_extrusions.reset_role_visibility_flags();
+
// m_sequential_view.skip_invisible_moves = true;
}
+#if ENABLE_SEAMS_USING_MODELS
+void GCodeViewer::init()
+{
+ if (m_gl_data_initialized)
+ return;
+
+ // initializes opengl data of TBuffers
+ for (size_t i = 0; i < m_buffers.size(); ++i) {
+ TBuffer& buffer = m_buffers[i];
+ EMoveType type = buffer_type(i);
+ switch (type)
+ {
+ default: { break; }
+ case EMoveType::Tool_change:
+ case EMoveType::Color_change:
+ case EMoveType::Pause_Print:
+ case EMoveType::Custom_GCode:
+ case EMoveType::Retract:
+ case EMoveType::Unretract:
+ case EMoveType::Seam: {
+#if ENABLE_SEAMS_USING_BATCHED_MODELS
+ if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
+ buffer.shader = "gouraud_light_instanced";
+ buffer.model.model.init_from(diamond(16));
+ buffer.model.color = option_color(type);
+ buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
+ }
+ else {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
+ buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
+ buffer.shader = "gouraud_light";
+
+ buffer.model.data = diamond(16);
+ buffer.model.color = option_color(type);
+ buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
+ }
+ break;
+#else
+ if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
+ buffer.shader = "gouraud_light_instanced";
+ buffer.model.model.init_from(diamond(16));
+ buffer.model.color = option_color(type);
+ }
+ else {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
+ buffer.vertices.format = VBuffer::EFormat::Position;
+ buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
+ }
+ break;
+#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
+ }
+ case EMoveType::Wipe:
+ case EMoveType::Extrude: {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
+ buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
+ buffer.shader = "gouraud_light";
+ break;
+ }
+ case EMoveType::Travel: {
+ buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
+ buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
+ buffer.shader = "toolpaths_lines";
+ break;
+ }
+ }
+
+ set_toolpath_move_type_visible(EMoveType::Extrude, true);
+ }
+
+ // initializes tool marker
+ m_sequential_view.marker.init();
+
+ // initializes point sizes
+ std::array<int, 2> point_sizes;
+ ::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data());
+ m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
+
+ m_gl_data_initialized = true;
+}
+#endif // ENABLE_SEAMS_USING_MODELS
+
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
{
// avoid processing if called with the same gcode_result
@@ -604,12 +694,12 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
// release gpu memory, if used
reset();
- m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
+ m_sequential_view.gcode_window.load_gcode(gcode_result.filename,
+ // Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function).
+ std::move(const_cast<std::vector<size_t>&>(gcode_result.lines_ends)));
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_gcode_viewer())
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
load_toolpaths(gcode_result);
@@ -773,7 +863,6 @@ void GCodeViewer::reset()
m_extruder_ids = std::vector<unsigned char>();
m_filament_diameters = std::vector<float>();
m_filament_densities = std::vector<float>();
- m_extrusions.reset_role_visibility_flags();
m_extrusions.reset_ranges();
m_shells.volumes.clear();
m_layers.reset();
@@ -785,9 +874,7 @@ void GCodeViewer::reset()
m_layers_times[i] = std::vector<float>();
}
#endif // ENABLE_PREVIEW_LAYER_TIME
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_sequential_view.gcode_window.reset();
#if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.reset_all();
@@ -796,6 +883,7 @@ void GCodeViewer::reset()
void GCodeViewer::render()
{
+#if !ENABLE_SEAMS_USING_MODELS
auto init_gl_data = [this]() {
// initializes opengl data of TBuffers
for (size_t i = 0; i < m_buffers.size(); ++i) {
@@ -811,26 +899,6 @@ void GCodeViewer::render()
case EMoveType::Retract:
case EMoveType::Unretract:
case EMoveType::Seam: {
-#if ENABLE_SEAMS_USING_MODELS
-#if ENABLE_SEAMS_USING_BATCHED_MODELS
- if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
- buffer.shader = "gouraud_light_instanced";
- buffer.model.model.init_from(diamond(16));
- buffer.model.color = option_color(type);
- buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
- }
- else {
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
- buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
- buffer.shader = "gouraud_light";
-
- buffer.model.data = diamond(16);
- buffer.model.color = option_color(type);
- buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
- }
- break;
-#else
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
buffer.shader = "gouraud_light_instanced";
@@ -843,36 +911,17 @@ void GCodeViewer::render()
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
}
break;
-#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
-#else
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
- buffer.vertices.format = VBuffer::EFormat::Position;
- buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
- break;
-#endif // ENABLE_SEAMS_USING_MODELS
}
case EMoveType::Wipe:
case EMoveType::Extrude: {
-#if ENABLE_SEAMS_USING_MODELS
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
- buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
-#endif // ENABLE_SEAMS_USING_MODELS
buffer.shader = "gouraud_light";
break;
}
case EMoveType::Travel: {
-#if ENABLE_SEAMS_USING_MODELS
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
- buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
-#endif // ENABLE_SEAMS_USING_MODELS
buffer.shader = "toolpaths_lines";
break;
}
}
-
-#if ENABLE_SEAMS_USING_MODELS
- set_toolpath_move_type_visible(EMoveType::Extrude, true);
-#endif // ENABLE_SEAMS_USING_MODELS
}
// initializes tool marker
@@ -884,6 +933,7 @@ void GCodeViewer::render()
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
m_gl_data_initialized = true;
};
+#endif // !ENABLE_SEAMS_USING_MODELS
#if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.reset_opengl();
@@ -892,10 +942,12 @@ void GCodeViewer::render()
#endif // ENABLE_SEAMS_USING_MODELS
#endif // ENABLE_GCODE_VIEWER_STATISTICS
+#if !ENABLE_SEAMS_USING_MODELS
// OpenGL data must be initialized after the glContext has been created.
// This is ensured when this method is called by GLCanvas3D::_render_gcode().
if (!m_gl_data_initialized)
init_gl_data();
+#endif // !ENABLE_SEAMS_USING_MODELS
if (m_roles.empty())
return;
@@ -907,6 +959,9 @@ void GCodeViewer::render()
render_legend(legend_height);
if (m_sequential_view.current.last != m_sequential_view.endpoints.last) {
m_sequential_view.marker.set_world_position(m_sequential_view.current_position);
+#if ENABLE_FIX_SEAMS_SYNCH
+ m_sequential_view.marker.set_world_offset(m_sequential_view.current_offset);
+#endif // ENABLE_FIX_SEAMS_SYNCH
m_sequential_view.render(legend_height);
}
#if ENABLE_GCODE_VIEWER_STATISTICS
@@ -1545,10 +1600,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
m_max_bounding_box = m_paths_bounding_box;
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
+#if ENABLE_FIX_SEAMS_SYNCH
m_sequential_view.gcode_ids.clear();
+ for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
+ const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
+ if (move.type != EMoveType::Seam)
+ m_sequential_view.gcode_ids.push_back(move.gcode_id);
+ }
+#else
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
m_sequential_view.gcode_ids.push_back(move.gcode_id);
}
+#endif // ENABLE_FIX_SEAMS_SYNCH
std::vector<MultiVertexBuffer> vertices(m_buffers.size());
std::vector<MultiIndexBuffer> indices(m_buffers.size());
@@ -1556,11 +1619,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
std::vector<InstanceBuffer> instances(m_buffers.size());
std::vector<InstanceIdBuffer> instances_ids(m_buffers.size());
#endif // ENABLE_SEAMS_USING_MODELS
+#if ENABLE_FIX_SEAMS_SYNCH
+ std::vector<InstancesOffsets> instances_offsets(m_buffers.size());
+#endif // ENABLE_FIX_SEAMS_SYNCH
std::vector<float> options_zs;
+#if ENABLE_FIX_SEAMS_SYNCH
+ size_t seams_count = 0;
+ std::vector<size_t> seams_ids;
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
// toolpaths data -> extract vertices from result
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ if (curr.type == EMoveType::Seam) {
+ ++seams_count;
+ seams_ids.push_back(i);
+ }
+
+ size_t move_id = i - seams_count;
+#endif // ENABLE_FIX_SEAMS_SYNCH
// skip first vertex
if (i == 0)
@@ -1583,6 +1662,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_SEAMS_USING_MODELS
InstanceBuffer& inst_buffer = instances[id];
InstanceIdBuffer& inst_id_buffer = instances_ids[id];
+#if ENABLE_FIX_SEAMS_SYNCH
+ InstancesOffsets& inst_offsets = instances_offsets[id];
+#endif // ENABLE_FIX_SEAMS_SYNCH
#endif // ENABLE_SEAMS_USING_MODELS
// ensure there is at least one vertex buffer
@@ -1601,7 +1683,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
Path& last_path = t_buffer.paths.back();
if (prev.type == curr.type && last_path.matches(curr))
+#if ENABLE_FIX_SEAMS_SYNCH
+ last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1);
+#else
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
@@ -1611,12 +1697,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
{
case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; }
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
+#if ENABLE_FIX_SEAMS_SYNCH
+ case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; }
+#else
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
+#endif // ENABLE_FIX_SEAMS_SYNCH
#if ENABLE_SEAMS_USING_MODELS
#if ENABLE_SEAMS_USING_BATCHED_MODELS
case TBuffer::ERenderPrimitiveType::InstancedModel:
{
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
+ inst_offsets.push_back(prev.position - curr.position);
+#else
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.instances_count;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -1624,7 +1719,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
}
case TBuffer::ERenderPrimitiveType::BatchedModel:
{
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, move_id);
+ inst_offsets.push_back(prev.position - curr.position);
+#else
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.batched_count;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -1633,7 +1733,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#else
case TBuffer::ERenderPrimitiveType::Model:
{
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
+#else
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.instances_count;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -1652,7 +1756,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
}
// smooth toolpaths corners for the given TBuffer using triangles
+#if ENABLE_FIX_SEAMS_SYNCH
+ auto smooth_triangle_toolpaths_corners = [&gcode_result, &seams_ids](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
+#else
auto smooth_triangle_toolpaths_corners = [&gcode_result](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
+#endif // ENABLE_FIX_SEAMS_SYNCH
auto extract_position_at = [](const VertexBuffer& vertices, size_t offset) {
return Vec3f(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]);
};
@@ -1726,6 +1834,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
}
};
+#if ENABLE_FIX_SEAMS_SYNCH
+ auto extract_move_id = [&seams_ids](size_t id) {
+ for (int i = seams_ids.size() - 1; i >= 0; --i) {
+ if (seams_ids[i] < id + i + 1)
+ return id + (size_t)i + 1;
+ }
+ return id;
+ };
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
for (const Path& path : t_buffer.paths) {
// the two segments of the path sharing the current vertex may belong
@@ -1736,9 +1854,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
const float half_width = 0.5f * path.width;
for (size_t j = 1; j < path_vertices_count - 1; ++j) {
size_t curr_s_id = path.sub_paths.front().first.s_id + j;
+#if ENABLE_FIX_SEAMS_SYNCH
+ size_t move_id = extract_move_id(curr_s_id);
+ const Vec3f& prev = gcode_result.moves[move_id - 1].position;
+ const Vec3f& curr = gcode_result.moves[move_id].position;
+ const Vec3f& next = gcode_result.moves[move_id + 1].position;
+#else
const Vec3f& prev = gcode_result.moves[curr_s_id - 1].position;
const Vec3f& curr = gcode_result.moves[curr_s_id].position;
const Vec3f& next = gcode_result.moves[curr_s_id + 1].position;
+#endif // ENABLE_FIX_SEAMS_SYNCH
// select the subpaths which contains the previous/next segments
if (!path.sub_paths[prev_sub_path_id].contains(curr_s_id))
@@ -1805,6 +1930,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
}
}
+#if ENABLE_FIX_SEAMS_SYNCH
+ // dismiss, no more needed
+ std::vector<size_t>().swap(seams_ids);
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
for (MultiVertexBuffer& v_multibuffer : vertices) {
for (VertexBuffer& v_buffer : v_multibuffer) {
v_buffer.shrink_to_fit();
@@ -1829,6 +1959,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (!inst_buffer.empty()) {
t_buffer.model.instances.buffer = inst_buffer;
t_buffer.model.instances.s_ids = instances_ids[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ t_buffer.model.instances.offsets = instances_offsets[i];
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
else {
@@ -1837,6 +1970,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (!inst_buffer.empty()) {
t_buffer.model.instances.buffer = inst_buffer;
t_buffer.model.instances.s_ids = instances_ids[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ t_buffer.model.instances.offsets = instances_offsets[i];
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
#else
@@ -1845,6 +1981,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (!inst_buffer.empty()) {
t_buffer.model.instances.buffer = inst_buffer;
t_buffer.model.instances.s_ids = instances_ids[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ t_buffer.model.instances.offsets = instances_offsets[i];
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
else {
@@ -1905,8 +2044,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
using VboIndexList = std::vector<unsigned int>;
std::vector<VboIndexList> vbo_indices(m_buffers.size());
+#if ENABLE_FIX_SEAMS_SYNCH
+ seams_count = 0;
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ if (curr.type == EMoveType::Seam)
+ ++seams_count;
+
+ size_t move_id = i - seams_count;
+#endif // ENABLE_FIX_SEAMS_SYNCH
// skip first vertex
if (i == 0)
@@ -1957,7 +2106,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
Path& last_path = t_buffer.paths.back();
+#if ENABLE_FIX_SEAMS_SYNCH
+ last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
+#else
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
@@ -1982,7 +2135,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
Path& last_path = t_buffer.paths.back();
+#if ENABLE_FIX_SEAMS_SYNCH
+ last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
+#else
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
@@ -1991,17 +2148,29 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
switch (t_buffer.render_primitive_type)
{
case TBuffer::ERenderPrimitiveType::Point: {
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
+#else
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
break;
}
case TBuffer::ERenderPrimitiveType::Line: {
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
+#else
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
break;
}
case TBuffer::ERenderPrimitiveType::Triangle: {
+#if ENABLE_FIX_SEAMS_SYNCH
+ add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
+#else
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
+#endif // ENABLE_FIX_SEAMS_SYNCH
break;
}
#if ENABLE_SEAMS_USING_BATCHED_MODELS
@@ -2096,16 +2265,34 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// layers zs / roles / extruder ids -> extract from result
size_t last_travel_s_id = 0;
+#if ENABLE_FIX_SEAMS_SYNCH
+ seams_count = 0;
+#endif // ENABLE_FIX_SEAMS_SYNCH
for (size_t i = 0; i < m_moves_count; ++i) {
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
+#if ENABLE_FIX_SEAMS_SYNCH
+ if (move.type == EMoveType::Seam)
+ ++seams_count;
+
+ size_t move_id = i - seams_count;
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
if (move.type == EMoveType::Extrude) {
// layers zs
const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back();
const double z = static_cast<double>(move.position.z());
if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
+#if ENABLE_FIX_SEAMS_SYNCH
+ m_layers.append(z, { last_travel_s_id, move_id });
+#else
m_layers.append(z, { last_travel_s_id, i });
+#endif // ENABLE_FIX_SEAMS_SYNCH
else
+#if ENABLE_FIX_SEAMS_SYNCH
+ m_layers.get_ranges().back().last = move_id;
+#else
m_layers.get_ranges().back().last = i;
+#endif // ENABLE_FIX_SEAMS_SYNCH
// extruder ids
m_extruder_ids.emplace_back(move.extruder_id);
// roles
@@ -2113,10 +2300,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
m_roles.emplace_back(move.extrusion_role);
}
else if (move.type == EMoveType::Travel) {
+#if ENABLE_FIX_SEAMS_SYNCH
+ if (move_id - last_travel_s_id > 1 && !m_layers.empty())
+ m_layers.get_ranges().back().last = move_id;
+
+ last_travel_s_id = move_id;
+#else
if (i - last_travel_s_id > 1 && !m_layers.empty())
m_layers.get_ranges().back().last = i;
last_travel_s_id = i;
+#endif // ENABLE_FIX_SEAMS_SYNCH
}
}
@@ -2407,6 +2601,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
sequential_view->current_position.x() = buffer.model.instances.buffer[offset + 0];
sequential_view->current_position.y() = buffer.model.instances.buffer[offset + 1];
sequential_view->current_position.z() = buffer.model.instances.buffer[offset + 2];
+#if ENABLE_FIX_SEAMS_SYNCH
+ sequential_view->current_offset = buffer.model.instances.offsets[i];
+#endif // ENABLE_FIX_SEAMS_SYNCH
found = true;
break;
@@ -2446,6 +2643,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(sequential_view->current_position.data())));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
+#if ENABLE_FIX_SEAMS_SYNCH
+ sequential_view->current_offset = Vec3f::Zero();
+#endif // ENABLE_FIX_SEAMS_SYNCH
+
found = true;
break;
}
@@ -3115,7 +3316,11 @@ void GCodeViewer::render_legend(float& legend_height)
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
+#if ENABLE_SEAMS_USING_BATCHED_MODELS
auto append_item = [icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
+#else
+ auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
+#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array<float, 4>& offsets = { 0.0f, 0.0f, 0.0f, 0.0f },
double used_filament_m = 0.0, double used_filament_g = 0.0,
std::function<void()> callback = nullptr) {
@@ -3514,12 +3719,9 @@ void GCodeViewer::render_legend(float& legend_height)
}
break;
}
- case EViewType::ColorPrint: {
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
+ case EViewType::ColorPrint:
+ {
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
-#else
- const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
size_t total_items = 1;
for (unsigned char i : m_extruder_ids) {
total_items += color_print_ranges(i, custom_gcode_per_print_z).size();
@@ -3614,11 +3816,7 @@ void GCodeViewer::render_legend(float& legend_height)
auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
PartialTimes items;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
-#else
- std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
int extruders_count = wxGetApp().extruders_edited_cnt();
std::vector<Color> last_color(extruders_count);
for (int i = 0; i < extruders_count; ++i) {
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index 28f009c7a..375425478 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -31,6 +31,9 @@ class GCodeViewer
using InstanceBuffer = std::vector<float>;
using InstanceIdBuffer = std::vector<size_t>;
#endif // ENABLE_SEAMS_USING_MODELS
+#if ENABLE_FIX_SEAMS_SYNCH
+ using InstancesOffsets = std::vector<Vec3f>;
+#endif // ENABLE_FIX_SEAMS_SYNCH
static const std::vector<Color> Extrusion_Role_Colors;
static const std::vector<Color> Options_Colors;
@@ -151,6 +154,10 @@ class GCodeViewer
InstanceBuffer buffer;
// indices of the moves for all instances
std::vector<size_t> s_ids;
+#if ENABLE_FIX_SEAMS_SYNCH
+ // position offsets, used to show the correct value of the tool position
+ InstancesOffsets offsets;
+#endif // ENABLE_FIX_SEAMS_SYNCH
Ranges render_ranges;
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
@@ -686,6 +693,12 @@ public:
GLModel m_model;
Vec3f m_world_position;
Transform3f m_world_transform;
+#if ENABLE_FIX_SEAMS_SYNCH
+ // for seams, the position of the marker is on the last endpoint of the toolpath containing it
+ // the offset is used to show the correct value of tool position in the "ToolPosition" window
+ // see implementation of render() method
+ Vec3f m_world_offset;
+#endif // ENABLE_FIX_SEAMS_SYNCH
float m_z_offset{ 0.5f };
bool m_visible{ true };
@@ -695,6 +708,9 @@ public:
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
void set_world_position(const Vec3f& position);
+#if ENABLE_FIX_SEAMS_SYNCH
+ void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
+#endif // ENABLE_FIX_SEAMS_SYNCH
bool is_visible() const { return m_visible; }
void set_visible(bool visible) { m_visible = visible; }
@@ -723,7 +739,7 @@ public:
public:
GCodeWindow() = default;
~GCodeWindow() { stop_mapping_file(); }
- void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
+ void load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends);
void reset() {
stop_mapping_file();
m_lines_ends.clear();
@@ -752,6 +768,9 @@ public:
Endpoints global;
#endif // ENABLE_SEAMS_USING_MODELS
Vec3f current_position{ Vec3f::Zero() };
+#if ENABLE_FIX_SEAMS_SYNCH
+ Vec3f current_offset{ Vec3f::Zero() };
+#endif // ENABLE_FIX_SEAMS_SYNCH
Marker marker;
GCodeWindow gcode_window;
std::vector<unsigned int> gcode_ids;
@@ -811,14 +830,16 @@ private:
std::array<std::vector<float>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times;
#endif // ENABLE_PREVIEW_LAYER_TIME
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
public:
GCodeViewer();
~GCodeViewer() { reset(); }
+#if ENABLE_SEAMS_USING_MODELS
+ void init();
+#endif // ENABLE_SEAMS_USING_MODELS
+
// extract rendering data from the given parameters
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
// recalculate ranges in dependence of what is visible and sets tool/print colors
@@ -862,10 +883,8 @@ public:
void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
size_t get_extruders_count() { return m_extruders_count; }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
private:
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 33399e38d..869f48a01 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -23,6 +23,7 @@
#include "slic3r/GUI/3DBed.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/MainFrame.hpp"
+#include "slic3r/Utils/UndoRedo.hpp"
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
@@ -1399,6 +1400,11 @@ void GLCanvas3D::render()
if (!is_initialized() && !init())
return;
+#if ENABLE_SEAMS_USING_MODELS
+ if (!m_main_toolbar.is_enabled())
+ m_gcode_viewer.init();
+#endif // ENABLE_SEAMS_USING_MODELS
+
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
@@ -1450,19 +1456,13 @@ void GLCanvas3D::render()
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_background();
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
_render_objects(GLVolumeCollection::ERenderType::Opaque);
-#else
- _render_objects();
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
if (!m_main_toolbar.is_enabled())
_render_gcode();
_render_sla_slices();
_render_selection();
_render_bed(!camera.is_looking_downward(), true);
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
_render_objects(GLVolumeCollection::ERenderType::Transparent);
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
_render_sequential_clearance();
#if ENABLE_RENDER_SELECTION_CENTER
@@ -2092,7 +2092,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
vol_old.finalize_geometry(gl_initialized);
}
-void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
+void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
{
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
@@ -2100,10 +2100,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
m_gcode_viewer.update_shells_color_by_extruder(m_config);
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
}
-}
-void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
-{
m_gcode_viewer.refresh(gcode_result, str_tool_colors);
set_as_dirty();
request_extra_frame();
@@ -2935,7 +2932,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
return;
}
-#if ENABLE_SINKING_CONTOURS
for (GLVolume* volume : m_volumes.volumes) {
volume->force_sinking_contours = false;
}
@@ -2947,7 +2943,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
m_dirty = true;
};
-#endif // ENABLE_SINKING_CONTOURS
if (m_gizmos.on_mouse(evt)) {
if (wxWindow::FindFocus() != m_canvas)
@@ -2973,7 +2968,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
default: { break; }
}
}
-#if ENABLE_SINKING_CONTOURS
else if (evt.Dragging()) {
switch (m_gizmos.get_current_type())
{
@@ -2987,7 +2981,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
default: { break; }
}
}
-#endif // ENABLE_SINKING_CONTOURS
return;
}
@@ -3297,10 +3290,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else
evt.Skip();
-#if ENABLE_SINKING_CONTOURS
if (m_moving)
show_sinking_contours();
-#endif // ENABLE_SINKING_CONTOURS
#ifdef __WXMSW__
if (on_enter_workaround)
@@ -4442,12 +4433,11 @@ bool GLCanvas3D::_init_main_toolbar()
arrow_data.top = 0;
arrow_data.right = 0;
arrow_data.bottom = 0;
-
if (!m_main_toolbar.init_arrow(arrow_data))
{
BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
}
-
+ // m_gizmos is created at constructor, thus we can init arrow here.
if (!m_gizmos.init_arrow(arrow_data))
{
BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
@@ -4658,6 +4648,18 @@ bool GLCanvas3D::_init_undoredo_toolbar()
return true;
}
+ // init arrow
+ BackgroundTexture::Metadata arrow_data;
+ arrow_data.filename = "toolbar_arrow.svg";
+ arrow_data.left = 0;
+ arrow_data.top = 0;
+ arrow_data.right = 0;
+ arrow_data.bottom = 0;
+ if (!m_undoredo_toolbar.init_arrow(arrow_data))
+ {
+ BOOST_LOG_TRIVIAL(error) << "Undo/Redo toolbar failed to load arrow texture.";
+ }
+
// m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
m_undoredo_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left);
@@ -5074,12 +5076,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
}
-
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
-#else
-void GLCanvas3D::_render_objects()
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
{
if (m_volumes.empty())
return;
@@ -5111,20 +5108,14 @@ void GLCanvas3D::_render_objects()
if (shader != nullptr) {
shader->start_using();
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
switch (type)
{
default:
case GLVolumeCollection::ERenderType::Opaque:
{
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
-#else
- m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
@@ -5133,11 +5124,7 @@ void GLCanvas3D::_render_objects()
}
else {
// do not cull backfaces to show broken geometry, if any
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
-#else
- m_volumes.render(GLVolumeCollection::ERenderType::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
}
@@ -5156,7 +5143,6 @@ void GLCanvas3D::_render_objects()
shader->start_using();
}
}
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
break;
}
case GLVolumeCollection::ERenderType::Transparent:
@@ -5165,9 +5151,6 @@ void GLCanvas3D::_render_objects()
break;
}
}
-#else
- m_volumes.render(GLVolumeCollection::ERenderType::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
shader->stop_using();
}
@@ -5410,6 +5393,10 @@ void GLCanvas3D::_render_undoredo_toolbar()
m_undoredo_toolbar.set_position(top, left);
m_undoredo_toolbar.render(*this);
+ if (m_toolbar_highlighter.m_render_arrow)
+ {
+ m_undoredo_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item);
+ }
}
void GLCanvas3D::_render_collapse_toolbar() const
@@ -5676,11 +5663,8 @@ void GLCanvas3D::_update_volumes_hover_state()
}
}
}
-#if ENABLE_SINKING_CONTOURS
else if (volume.selected)
volume.hover = GLVolume::HS_Hover;
-#endif // ENABLE_SINKING_CONTOURS
-
}
}
@@ -6449,7 +6433,7 @@ void GLCanvas3D::_update_selection_from_hover()
// the selection is going to be modified (Add)
if (!contains_all) {
- wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")));
+ wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")), UndoRedo::SnapshotType::Selection);
selection_changed = true;
}
}
@@ -6464,7 +6448,7 @@ void GLCanvas3D::_update_selection_from_hover()
// the selection is going to be modified (Remove)
if (contains_any) {
- wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")));
+ wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")), UndoRedo::SnapshotType::Selection);
selection_changed = true;
}
}
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index eb0220cd4..86ec5634f 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -617,6 +617,9 @@ public:
void reset_volumes();
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
+#if ENABLE_SEAMS_USING_MODELS
+ void init_gcode_viewer() { m_gcode_viewer.init(); }
+#endif // ENABLE_SEAMS_USING_MODELS
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
@@ -710,10 +713,8 @@ public:
void set_toolpath_view_type(GCodeViewer::EViewType type);
void set_volumes_z_range(const std::array<double, 2>& range);
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_gcode_viewer.get_custom_gcode_per_print_z(); }
size_t get_gcode_extruders_count() { return m_gcode_viewer.get_extruders_count(); }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(const Model& model, int obj_idx);
@@ -722,8 +723,7 @@ public:
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
- void load_gcode_preview(const GCodeProcessor::Result& gcode_result);
- void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
+ void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
void refresh_gcode_preview_render_paths();
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
@@ -896,11 +896,7 @@ private:
void _render_background() const;
void _render_bed(bool bottom, bool show_axes);
void _render_bed_for_picking(bool bottom);
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void _render_objects(GLVolumeCollection::ERenderType type);
-#else
- void _render_objects();
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void _render_gcode();
void _render_selection() const;
void _render_sequential_clearance();
diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp
index d22925673..fb27aced5 100644
--- a/src/slic3r/GUI/GLModel.cpp
+++ b/src/slic3r/GUI/GLModel.cpp
@@ -7,9 +7,7 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
-#if ENABLE_SINKING_CONTOURS
#include "libslic3r/Polygon.hpp"
-#endif // ENABLE_SINKING_CONTOURS
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/predicate.hpp>
@@ -78,7 +76,7 @@ void GLModel::init_from(const InitializationData& data)
}
}
-void GLModel::init_from(const TriangleMesh& mesh)
+void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox)
{
if (!m_render_data.empty()) // call reset() if you want to reuse this model
return;
@@ -86,31 +84,36 @@ void GLModel::init_from(const TriangleMesh& mesh)
RenderData data;
data.type = PrimitiveType::Triangles;
- std::vector<float> vertices = std::vector<float>(18 * mesh.stl.stats.number_of_facets);
- std::vector<unsigned int> indices = std::vector<unsigned int>(3 * mesh.stl.stats.number_of_facets);
+ std::vector<float> vertices = std::vector<float>(18 * its.indices.size());
+ std::vector<unsigned int> indices = std::vector<unsigned int>(3 * its.indices.size());
unsigned int vertices_count = 0;
- for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
- const stl_facet& facet = mesh.stl.facet_start[i];
- for (size_t j = 0; j < 3; ++j) {
+ for (uint32_t i = 0; i < its.indices.size(); ++i) {
+ stl_triangle_vertex_indices face = its.indices[i];
+ stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
+ stl_vertex n = face_normal_normalized(vertex);
+ for (size_t j = 0; j < 3; ++ j) {
size_t offset = i * 18 + j * 6;
- ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(facet.vertex[j].data()), 3 * sizeof(float));
- ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(facet.normal.data()), 3 * sizeof(float));
+ ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(vertex[j].data()), 3 * sizeof(float));
+ ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(n.data()), 3 * sizeof(float));
}
- for (size_t j = 0; j < 3; ++j) {
+ for (size_t j = 0; j < 3; ++j)
indices[i * 3 + j] = vertices_count + j;
- }
vertices_count += 3;
}
data.indices_count = static_cast<unsigned int>(indices.size());
- m_bounding_box = mesh.bounding_box();
+ m_bounding_box = bbox;
send_to_gpu(data, vertices, indices);
m_render_data.emplace_back(data);
}
-#if ENABLE_SINKING_CONTOURS
+void GLModel::init_from(const indexed_triangle_set& its)
+{
+ this->init_from(its, bounding_box(its));
+}
+
void GLModel::init_from(const Polygons& polygons, float z)
{
auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) {
@@ -137,7 +140,6 @@ void GLModel::init_from(const Polygons& polygons, float z)
}
init_from(init_data);
}
-#endif // ENABLE_SINKING_CONTOURS
bool GLModel::init_from_file(const std::string& filename)
{
@@ -157,7 +159,8 @@ bool GLModel::init_from_file(const std::string& filename)
return false;
}
- init_from(model.mesh());
+ TriangleMesh mesh = model.mesh();
+ init_from(mesh.its, mesh.bounding_box());
m_filename = filename;
diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp
index 7422466f0..f2fc9da9b 100644
--- a/src/slic3r/GUI/GLModel.hpp
+++ b/src/slic3r/GUI/GLModel.hpp
@@ -6,13 +6,13 @@
#include <vector>
#include <string>
+struct indexed_triangle_set;
+
namespace Slic3r {
class TriangleMesh;
-#if ENABLE_SINKING_CONTOURS
class Polygon;
using Polygons = std::vector<Polygon>;
-#endif // ENABLE_SINKING_CONTOURS
namespace GUI {
@@ -70,10 +70,9 @@ namespace GUI {
virtual ~GLModel() { reset(); }
void init_from(const InitializationData& data);
- void init_from(const TriangleMesh& mesh);
-#if ENABLE_SINKING_CONTOURS
+ void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
+ void init_from(const indexed_triangle_set& its);
void init_from(const Polygons& polygons, float z);
-#endif // ENABLE_SINKING_CONTOURS
bool init_from_file(const std::string& filename);
// if entity_id == -1 set the color of all entities
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 515b6ed3a..86827442d 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -1143,6 +1143,10 @@ void GLToolbar::render_background(float left, float top, float right, float bott
void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
{
+ // arrow texture not initialized
+ if (m_arrow_texture.texture.get_id() == 0)
+ return;
+
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
@@ -1157,6 +1161,7 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
float left = m_layout.left;
float top = m_layout.top - icon_stride;
+ bool found = false;
for (const GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
@@ -1164,20 +1169,21 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
if (item->is_separator())
left += separator_stride;
else {
- if (item->get_name() == highlighted_item->get_name())
+ if (item->get_name() == highlighted_item->get_name()) {
+ found = true;
break;
+ }
left += icon_stride;
}
}
+ if (!found)
+ return;
left += border;
top -= separator_stride;
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
- // width and height of icon arrow is pointing to
- float tex_width = (float)m_icons_texture.get_width();
- float tex_height = (float)m_icons_texture.get_height();
// arrow width and height
float arr_tex_width = (float)m_arrow_texture.texture.get_width();
float arr_tex_height = (float)m_arrow_texture.texture.get_height();
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 59ccd9d66..023ba6f35 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -383,6 +383,9 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons
// the following line messes up the popup size the first time it is shown on wxWidgets 3.1.3
// comboCtrl->EnablePopupAnimation(false);
+#ifdef _WIN32
+ popup->SetFont(comboCtrl->GetFont());
+#endif // _WIN32
comboCtrl->SetPopupControl(popup);
wxString title = from_u8(text);
max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x);
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 79186a8ea..9c08159fc 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -429,9 +429,8 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
/* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ",
/* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
/* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;",
- /* FT_PRUSA */ "Prusa Control files (*.prusa)|*.prusa;*.PRUSA",
/* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC",
- /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
+ /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF",
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
/* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ",
@@ -695,7 +694,6 @@ GUI_App::GUI_App(EAppMode mode)
, m_app_mode(mode)
, m_em_unit(10)
, m_imgui(new ImGuiWrapper())
- , m_wizard(nullptr)
, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>())
{
@@ -1334,8 +1332,6 @@ void GUI_App::recreate_GUI(const wxString& msg_name)
dlg.Update(30, _L("Recreating") + dots);
old_main_frame->Destroy();
- // For this moment ConfigWizard is deleted, invalidate it.
- m_wizard = nullptr;
dlg.Update(80, _L("Loading of current presets") + dots);
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
@@ -1404,17 +1400,15 @@ void GUI_App::force_colors_update()
void GUI_App::update_ui_from_settings()
{
update_label_colours();
- mainframe->update_ui_from_settings();
-
#ifdef _WIN32
+ // Upadte UU 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();
- if (m_wizard)
- m_wizard->force_color_changed();
}
#endif
+ mainframe->update_ui_from_settings();
}
void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_maximized)
@@ -1872,8 +1866,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
#endif
case ConfigMenuTakeSnapshot:
// Take a configuration snapshot.
- if (check_and_save_current_preset_changes()) {
- wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
+ if (wxString action_name = _L("Taking a configuration snapshot");
+ check_and_save_current_preset_changes(action_name, _L("Some presets are modified and the unsaved changes will not be captured by the configuration snapshot."), false, true)) {
+ wxTextEntryDialog dlg(nullptr, action_name, _L("Snapshot name"));
UpdateDlgDarkUI(&dlg);
// set current normal font for dialog children,
@@ -1889,7 +1884,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
}
break;
case ConfigMenuSnapshots:
- if (check_and_save_current_preset_changes()) {
+ if (check_and_save_current_preset_changes(_L("Loading a configuration snapshot"), "", false)) {
std::string on_snapshot;
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
on_snapshot = app_config->get("on_snapshot");
@@ -1911,9 +1906,6 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
// Load the currently selected preset into the GUI, update the preset selection box.
load_current_presets();
-
- // update config wizard in respect to the new config
- update_wizard_from_config();
} catch (std::exception &ex) {
GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what()));
}
@@ -2082,13 +2074,28 @@ std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets(
return ret;
}
-// This is called when closing the application, when loading a config file or when starting the config wizard
-// to notify the user whether he is aware that some preset changes will be lost.
-bool GUI_App::check_and_save_current_preset_changes(const wxString& header, const wxString& caption)
-{
- if (/*this->plater()->model().objects.empty() && */has_current_preset_changes()) {
- UnsavedChangesDialog dlg(header, caption);
- if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
+// To notify the user whether he is aware that some preset changes will be lost,
+// UnsavedChangesDialog: "Discard / Save / Cancel"
+// This is called when:
+// - Close Application & Current project isn't saved
+// - Load Project & Current project isn't saved
+// - Undo / Redo with change of print technologie
+// - Loading snapshot
+// - Loading config_file/bundle
+// UnsavedChangesDialog: "Don't save / Save / Cancel"
+// This is called when:
+// - Exporting config_bundle
+// - Taking snapshot
+bool GUI_App::check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice/* = true*/, bool dont_save_insted_of_discard/* = false*/)
+{
+ if (has_current_preset_changes()) {
+ const std::string app_config_key = remember_choice ? "default_action_on_close_application" : "";
+ int act_buttons = UnsavedChangesDialog::ActionButtons::SAVE;
+ if (dont_save_insted_of_discard)
+ act_buttons |= UnsavedChangesDialog::ActionButtons::DONT_SAVE;
+ UnsavedChangesDialog dlg(caption, header, app_config_key, act_buttons);
+ std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key);
+ if (act == "none" && dlg.ShowModal() == wxID_CANCEL)
return false;
if (dlg.save_preset()) // save selected changes
@@ -2096,18 +2103,122 @@ bool GUI_App::check_and_save_current_preset_changes(const wxString& header, cons
for (const std::pair<std::string, Preset::Type>& nt : dlg.get_names_and_types())
preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second));
+ load_current_presets(false);
+
// if we saved changes to the new presets, we should to
// synchronize config.ini with the current selections.
preset_bundle->export_selections(*app_config);
- wxMessageBox(_L_PLURAL("The preset modifications are successfully saved",
- "The presets modifications are successfully saved", dlg.get_names_and_types().size()));
+ MessageDialog(nullptr, _L_PLURAL("The preset modifications are successfully saved",
+ "The presets modifications are successfully saved", dlg.get_names_and_types().size())).ShowModal();
}
}
return true;
}
+void GUI_App::apply_keeped_preset_modifications()
+{
+ PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
+ for (Tab* tab : tabs_list) {
+ if (tab->supports_printer_technology(printer_technology))
+ tab->apply_config_from_cache();
+ }
+ load_current_presets(false);
+}
+
+// This is called when creating new project or load another project
+// OR close ConfigWizard
+// to ask the user what should we do with unsaved changes for presets.
+// New Project => Current project is saved => UnsavedChangesDialog: "Keep / Discard / Cancel"
+// => Current project isn't saved => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
+// Close ConfigWizard => Current project is saved => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
+// Note: no_nullptr postponed_apply_of_keeped_changes indicates that thie function is called after ConfigWizard is closed
+bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes/* = nullptr*/)
+{
+ if (has_current_preset_changes()) {
+ bool is_called_from_configwizard = postponed_apply_of_keeped_changes != nullptr;
+
+ const std::string app_config_key = is_called_from_configwizard ? "" : "default_action_on_new_project";
+ UnsavedChangesDialog dlg(caption, header, app_config_key, action_buttons);
+ std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key);
+ if (act == "none" && dlg.ShowModal() == wxID_CANCEL)
+ return false;
+
+ auto reset_modifications = [this, is_called_from_configwizard]() {
+ if (is_called_from_configwizard)
+ return; // no need to discared changes. It will be done fromConfigWizard closing
+
+ PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
+ for (const Tab* const tab : tabs_list) {
+ if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
+ tab->m_presets->discard_current_changes();
+ }
+ load_current_presets(false);
+ };
+
+ if (dlg.discard())
+ reset_modifications();
+ else // save selected changes
+ {
+ const auto& preset_names_and_types = dlg.get_names_and_types();
+ if (dlg.save_preset()) {
+ for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types)
+ preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second));
+
+ // if we saved changes to the new presets, we should to
+ // synchronize config.ini with the current selections.
+ preset_bundle->export_selections(*app_config);
+
+ wxString text = _L_PLURAL("The preset modifications are successfully saved",
+ "The presets modifications are successfully saved", preset_names_and_types.size());
+ if (!is_called_from_configwizard)
+ text += "\n\n" + _L("For new project all modifications will be reseted");
+
+ MessageDialog(nullptr, text).ShowModal();
+ reset_modifications();
+ }
+ else if (dlg.transfer_changes() && (dlg.has_unselected_options() || is_called_from_configwizard)) {
+ // execute this part of code only if not all modifications are keeping to the new project
+ // OR this function is called when ConfigWizard is closed and "Keep modifications" is selected
+ for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types) {
+ Preset::Type type = nt.second;
+ Tab* tab = get_tab(type);
+ std::vector<std::string> selected_options = dlg.get_selected_options(type);
+ if (type == Preset::TYPE_PRINTER) {
+ auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count");
+ if (it != selected_options.end()) {
+ // erase "extruders_count" option from the list
+ selected_options.erase(it);
+ // cache the extruders count
+ static_cast<TabPrinter*>(tab)->cache_extruder_cnt();
+ }
+ }
+ tab->cache_config_diff(selected_options);
+ if (!is_called_from_configwizard)
+ tab->m_presets->discard_current_changes();
+ }
+ if (is_called_from_configwizard)
+ *postponed_apply_of_keeped_changes = true;
+ else
+ apply_keeped_preset_modifications();
+ }
+ }
+ }
+
+ return true;
+}
+
+bool GUI_App::can_load_project()
+{
+ int saved_project = plater()->save_project_if_dirty(_L("Loading a new project while the current project is modified."));
+ if (saved_project == wxID_CANCEL ||
+ (plater()->is_project_dirty() && saved_project == wxID_NO &&
+ !check_and_save_current_preset_changes(_L("Project is loading"), _L("Loading a new project while some presets are modified."))))
+ return false;
+ return true;
+}
+
bool GUI_App::check_print_host_queue()
{
wxString dirty;
@@ -2165,17 +2276,6 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/)
}
}
-void GUI_App::update_wizard_from_config()
-{
- if (!m_wizard)
- return;
- // If ConfigWizard was created before changing of the configuration,
- // we have to destroy it to have possibility to create it again in respect to the new config's parameters
- m_wizard->Reparent(nullptr);
- m_wizard->Destroy();
- m_wizard = nullptr;
-}
-
bool GUI_App::OnExceptionInMainLoop()
{
generic_exception_handle();
@@ -2280,7 +2380,7 @@ wxBookCtrlBase* GUI_App::tab_panel() const
return mainframe->m_tabpanel;
}
-std::shared_ptr<NotificationManager> GUI_App::notification_manager()
+NotificationManager * GUI_App::notification_manager()
{
return plater_->get_notification_manager();
}
@@ -2337,19 +2437,12 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
if (reason == ConfigWizard::RR_USER) {
- wxString header = _L("Updates to Configuration Wizard may cause an another preset selection and lost of preset modification as a result.\n"
- "So, check unsaved changes and save them if necessary.") + "\n";
- if (!check_and_save_current_preset_changes(header, _L("ConfigWizard is opening")) ||
- preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED)
+ if (preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED)
return false;
}
- if (! m_wizard) {
- wxBusyCursor wait;
- m_wizard = new ConfigWizard(mainframe);
- }
-
- const bool res = m_wizard->run(reason, start_page);
+ auto wizard = new ConfigWizard(mainframe);
+ const bool res = wizard->run(reason, start_page);
if (res) {
load_current_presets();
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 5acab0c94..60a143144 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -55,7 +55,6 @@ enum FileType
FT_OBJ,
FT_AMF,
FT_3MF,
- FT_PRUSA,
FT_GCODE,
FT_MODEL,
FT_PROJECT,
@@ -151,7 +150,6 @@ private:
std::unique_ptr<ImGuiWrapper> m_imgui;
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
- ConfigWizard* m_wizard; // Managed by wxWindow tree
std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker;
std::string m_instance_hash_string;
@@ -247,11 +245,13 @@ public:
bool has_current_preset_changes() const;
void update_saved_preset_from_current_preset();
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
- bool check_and_save_current_preset_changes(const wxString& header = wxString(), const wxString& caption = wxString());
+ bool check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice = true, bool use_dont_save_insted_of_discard = false);
+ void apply_keeped_preset_modifications();
+ bool check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes = nullptr);
+ bool can_load_project();
bool check_print_host_queue();
bool checked_tab(Tab* tab);
void load_current_presets(bool check_printer_presets = true);
- void update_wizard_from_config();
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
@@ -276,7 +276,7 @@ public:
ObjectLayers* obj_layers();
Plater* plater();
Model& model();
- std::shared_ptr<NotificationManager> notification_manager();
+ NotificationManager * notification_manager();
// Parameters extracted from the command line to be passed to GUI after initialization.
GUI_InitParams* init_params { nullptr };
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 92ba427b5..3671ee130 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -16,6 +16,10 @@
#include <boost/algorithm/string.hpp>
#include "slic3r/Utils/FixModelByWin10.hpp"
+#ifdef __APPLE__
+#include "wx/dcclient.h"
+#include "slic3r/Utils/MacDarkMode.hpp"
+#endif
namespace Slic3r
{
@@ -211,7 +215,6 @@ static int GetSelectedChoices( wxArrayInt& selections,
const wxString& caption,
const wxArrayString& choices)
{
-#ifdef _WIN32
wxMultiChoiceDialog dialog(nullptr, message, caption, choices);
wxGetApp().UpdateDlgDarkUI(&dialog);
@@ -219,6 +222,39 @@ static int GetSelectedChoices( wxArrayInt& selections,
// deselects the first item which is selected by default
dialog.SetSelections(selections);
+#ifdef __APPLE__
+ // Improvements for ChoiceListBox: Height of control will restect to items count
+ for (auto child : dialog.GetChildren())
+ if (dynamic_cast<wxListBox*>(child) && !choices.IsEmpty()) {
+ wxClientDC dc(child);
+
+ int height = dc.GetTextExtent(choices[0]).y;
+ int width = 0;
+ for (const auto& string : choices)
+ width = std::max(width, dc.GetTextExtent(string).x);
+
+ // calculate best size of ListBox
+ height += 3 * mac_max_scaling_factor(); // extend height by margins
+ width += 3 * height; // extend width by checkbox width and margins
+
+ // don't make the listbox too tall (limit height to around 10 items)
+ // but don't make it too small neither
+ int list_height = wxMax(height * wxMin(wxMax(choices.Count(), 3), 10), 70);
+ wxSize sz_best = wxSize(width, list_height);
+
+ wxSize sz = child->GetSize();
+ child->SetMinSize(sz_best);
+
+ // extend Dialog size, if calculated best size of ListBox is bigger then its size
+ wxSize dlg_sz = dialog.GetSize();
+ if (int delta_x = sz_best.x - sz.x; delta_x > 0) dlg_sz.x += delta_x;
+ if (int delta_y = sz_best.y - sz.y; delta_y > 0) dlg_sz.y += delta_y;
+ dialog.SetSize(dlg_sz);
+
+ break;
+ }
+#endif
+
if (dialog.ShowModal() != wxID_OK)
{
// NB: intentionally do not clear the selections array here, the caller
@@ -229,9 +265,6 @@ static int GetSelectedChoices( wxArrayInt& selections,
selections = dialog.GetSelections();
return static_cast<int>(selections.GetCount());
-#else
- return wxGetSelectedChoices(selections, message, caption, choices);
-#endif
}
static wxMenu* create_settings_popupmenu(wxMenu* parent_menu, const bool is_object_settings, wxDataViewItem item/*, ModelConfig& config*/)
@@ -656,7 +689,7 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
return nullptr;
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "",
[](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
- []() {return plater()->can_fix_through_netfabb(); }, plater());
+ []() {return plater()->can_fix_through_netfabb(); }, m_parent);
return menu_item;
}
@@ -665,7 +698,7 @@ wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu)
{
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "",
[](wxCommandEvent&) { obj_list()->simplify(); }, "", menu,
- []() {return plater()->can_simplify(); }, plater());
+ []() {return plater()->can_simplify(); }, m_parent);
menu->AppendSeparator();
return menu_item;
@@ -746,7 +779,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
}
append_submenu(menu, extruder_selection_menu, wxID_ANY, name, _L("Use another extruder"),
- "edit_uni"/* : "change_extruder"*/, []() {return true; }, GUI::wxGetApp().plater());
+ "edit_uni"/* : "change_extruder"*/, []() {return true; }, m_parent);
// menu->AppendSubMenu(extruder_selection_menu, name);
}
@@ -1028,6 +1061,7 @@ wxMenu* MenuFactory::multi_selection_menu()
wxMenu* menu = new MenuWithSeparators();
+ append_menu_item_fix_through_netfabb(menu);
append_menu_item_reload_from_disk(menu);
append_menu_items_convert_unit(menu);
if (obj_list()->can_merge_to_multipart_object())
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 378f91cbc..b94110427 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -10,6 +10,7 @@
#include "BitmapComboBox.hpp"
#include "GalleryDialog.hpp"
#include "MainFrame.hpp"
+#include "slic3r/Utils/UndoRedo.hpp"
#include "OptionsGroup.hpp"
#include "Tab.hpp"
@@ -19,6 +20,7 @@
#include "Selection.hpp"
#include "format.hpp"
#include "NotificationManager.hpp"
+#include "MsgDialog.hpp"
#include <boost/algorithm/string.hpp>
#include <wx/progdlg.h>
@@ -369,7 +371,7 @@ void ObjectList::get_selection_indexes(std::vector<int>& obj_idxs, std::vector<i
}
}
- std::sort(obj_idxs.begin(), obj_idxs.end(), std::greater<int>());
+ std::sort(obj_idxs.begin(), obj_idxs.end(), std::less<int>());
obj_idxs.erase(std::unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end());
}
@@ -381,19 +383,24 @@ int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -
return (*m_objects)[obj_idx]->get_mesh_errors_count(vol_idx);
}
-wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /*= -1*/) const
+static std::string get_warning_icon_name(const TriangleMeshStats& stats)
+{
+ return stats.repaired() ? (stats.manifold() ? "exclamation_manifold" : "exclamation") : "";
+}
+
+std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, bool from_plater /*= false*/) const
{
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
if (errors == 0)
- return ""; // hide tooltip
+ return { "", "" }; // hide tooltip
// Create tooltip string, if there are errors
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n";
- const stl_stats& stats = vol_idx == -1 ?
- (*m_objects)[obj_idx]->get_object_stl_stats() :
- (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
+ const TriangleMeshStats& stats = vol_idx == -1 ?
+ (*m_objects)[obj_idx]->get_object_stl_stats() :
+ (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
if (stats.degenerate_facets > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
@@ -401,28 +408,31 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n";
if (stats.facets_removed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
- if (stats.facets_added > 0)
- tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet added", "%1$d facets added", stats.facets_added), stats.facets_added) + "\n";
if (stats.facets_reversed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
if (stats.backwards_edges > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
- if (is_windows10())
- tooltip += _L("Right button click the icon to fix STL through Netfabb");
+ if (!stats.manifold()) {
+ tooltip += _L("Remaning errors") + ":\n";
+ tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n";
+ }
- return tooltip;
+ if (is_windows10() && !from_plater)
+ tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb");
+
+ return { tooltip, get_warning_icon_name(stats) };
}
-wxString ObjectList::get_mesh_errors_list()
+std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*= false*/)
{
if (!GetSelection())
- return "";
+ return { "", "" };
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx);
- return get_mesh_errors_list(obj_idx, vol_idx);
+ return get_mesh_errors(obj_idx, vol_idx, from_plater);
}
void ObjectList::set_tooltip_for_item(const wxPoint& pt)
@@ -460,7 +470,7 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
{
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx, item);
- tooltip = get_mesh_errors_list(obj_idx, vol_idx);
+ tooltip = get_mesh_errors(obj_idx, vol_idx).first;
}
GetMainWindow()->SetToolTip(tooltip);
@@ -1534,7 +1544,6 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
}
TriangleMesh mesh = model.mesh();
- mesh.repair();
// Mesh will be centered when loading.
ModelVolume* new_volume = model_object.add_volume(std::move(mesh), type);
new_volume->name = boost::filesystem::path(input_file).filename().string();
@@ -1557,27 +1566,24 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
static TriangleMesh create_mesh(const std::string& type_name, const BoundingBoxf3& bb)
{
- TriangleMesh mesh;
-
const double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
+ indexed_triangle_set mesh;
if (type_name == "Box")
// Sitting on the print bed, left front front corner at (0, 0).
- mesh = make_cube(side, side, side);
+ mesh = its_make_cube(side, side, side);
else if (type_name == "Cylinder")
// Centered around 0, sitting on the print bed.
// The cylinder has the same volume as the box above.
- mesh = make_cylinder(0.564 * side, side);
+ mesh = its_make_cylinder(0.564 * side, side);
else if (type_name == "Sphere")
// Centered around 0, half the sphere below the print bed, half above.
// The sphere has the same volume as the box above.
- mesh = make_sphere(0.62 * side, PI / 18);
+ mesh = its_make_sphere(0.62 * side, PI / 18);
else if (type_name == "Slab")
// Sitting on the print bed, left front front corner at (0, 0).
- mesh = make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
- mesh.repair();
-
- return mesh;
+ mesh = its_make_cube(bb.size().x() * 1.5, bb.size().y() * 1.5, bb.size().z() * 0.5);
+ return TriangleMesh(mesh);
}
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
@@ -1780,8 +1786,12 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
return;
// If last volume item with warning was deleted, unmark object item
- if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
- m_objects_model->DeleteWarningIcon(parent);
+ if (type & itVolume) {
+ if (auto obj = object(obj_idx); obj->get_mesh_errors_count() == 0)
+ m_objects_model->DeleteWarningIcon(parent);
+ else
+ m_objects_model->AddWarningIcon(parent, get_warning_icon_name(obj->mesh().stats()));
+ }
m_objects_model->Delete(item);
update_info_items(obj_idx);
@@ -1990,7 +2000,7 @@ void ObjectList::split()
for (const ModelVolume* volume : model_object->volumes) {
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
- volume->get_mesh_errors_count()>0,
+ get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
// add settings to the part, if it has those
@@ -2489,7 +2499,7 @@ void ObjectList::part_selection_changed()
if (item) {
// wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item));
wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item));
- wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id));
+ wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors(obj_idx, volume_id));
}
}
@@ -2632,7 +2642,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
const wxString& item_name = from_u8(model_object->name);
const auto item = m_objects_model->Add(item_name,
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
- get_mesh_errors_count(obj_idx) > 0);
+ get_warning_icon_name(model_object->mesh().stats()));
update_info_items(obj_idx, nullptr, call_selection_changed);
@@ -2642,7 +2652,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
from_u8(volume->name),
volume->type(),
- volume->get_mesh_errors_count()>0,
+ get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
add_settings_item(vol_item, &volume->config.get());
@@ -2751,6 +2761,8 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
// If last volume item with warning was deleted, unmark object item
if (obj->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(parent);
+ else
+ m_objects_model->AddWarningIcon(parent, get_warning_icon_name(obj->mesh().stats()));
}
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx, printer_technology() != ptSLA);
}
@@ -3458,7 +3470,7 @@ void ObjectList::update_selections_on_canvas()
volume_idxs = selection.get_missing_volume_idxs_from(volume_idxs);
if (volume_idxs.size() > 0)
{
- Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list")));
+ Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list")), UndoRedo::SnapshotType::Selection);
selection.remove_volumes(mode, volume_idxs);
}
}
@@ -3470,7 +3482,7 @@ void ObjectList::update_selections_on_canvas()
// OR there is no single selection
if (selection.get_mode() == mode || !single_selection)
volume_idxs = selection.get_unselected_volume_idxs_from(volume_idxs);
- Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list")));
+ Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list")), UndoRedo::SnapshotType::Selection);
selection.add_volumes(mode, volume_idxs, single_selection);
}
@@ -4013,13 +4025,124 @@ void ObjectList::rename_item()
void ObjectList::fix_through_netfabb()
{
- int obj_idx, vol_idx;
- get_selected_item_indexes(obj_idx, vol_idx);
+ // Do not fix anything when a gizmo is open. There might be issues with updates
+ // and what is worse, the snapshot time would refer to the internal stack.
+ if (!wxGetApp().plater()->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
+ return;
- wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx);
-
- update_item_error_icon(obj_idx, vol_idx);
- update_info_items(obj_idx);
+ // model_name
+ std::vector<std::string> succes_models;
+ // model_name failing reason
+ std::vector<std::pair<std::string, std::string>> failed_models;
+
+ std::vector<int> obj_idxs, vol_idxs;
+ get_selection_indexes(obj_idxs, vol_idxs);
+
+ std::vector<std::string> model_names;
+
+ // clear selections from the non-broken models if any exists
+ // and than fill names of models to repairing
+ if (vol_idxs.empty()) {
+ for (int i = int(obj_idxs.size())-1; i >= 0; --i)
+ if (object(obj_idxs[i])->get_mesh_errors_count() == 0)
+ obj_idxs.erase(obj_idxs.begin()+i);
+ for (int obj_idx : obj_idxs)
+ model_names.push_back(object(obj_idx)->name);
+ }
+ else {
+ ModelObject* obj = object(obj_idxs.front());
+ for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
+ if (obj->get_mesh_errors_count(vol_idxs[i]) == 0)
+ vol_idxs.erase(vol_idxs.begin() + i);
+ for (int vol_idx : vol_idxs)
+ model_names.push_back(obj->volumes[vol_idx]->name);
+ }
+
+ auto plater = wxGetApp().plater();
+
+ auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx,
+ int model_idx,
+ wxProgressDialog& progress_dlg,
+ std::vector<std::string>& succes_models,
+ std::vector<std::pair<std::string, std::string>>& failed_models)
+ {
+ const std::string& model_name = model_names[model_idx];
+ wxString msg = _L("Repairing model");
+ if (model_names.size() == 1)
+ msg += ": " + from_u8(model_name) + "\n";
+ else {
+ msg += ":\n";
+ for (int i = 0; i < int(model_names.size()); ++i)
+ msg += (i == model_idx ? " > " : " ") + from_u8(model_names[i]) + "\n";
+ msg += "\n";
+ }
+
+ plater->clear_before_change_mesh(obj_idx);
+ std::string res;
+ if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
+ return false;
+ wxGetApp().plater()->changed_mesh(obj_idx);
+
+ plater->changed_mesh(obj_idx);
+
+ if (res.empty())
+ succes_models.push_back(model_name);
+ else
+ failed_models.push_back({ model_name, res });
+
+ update_item_error_icon(obj_idx, vol_idx);
+ update_info_items(obj_idx);
+
+ return true;
+ };
+
+ Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb"));
+
+ // Open a progress dialog.
+ wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100,
+ nullptr, // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing
+ wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
+ int model_idx{ 0 };
+ if (vol_idxs.empty()) {
+ int vol_idx{ -1 };
+ for (int obj_idx : obj_idxs) {
+ if (object(obj_idx)->get_mesh_errors_count(vol_idx) == 0)
+ continue;
+ if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
+ break;
+ model_idx++;
+ }
+ }
+ else {
+ int obj_idx{ obj_idxs.front() };
+ for (int vol_idx : vol_idxs) {
+ if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
+ break;
+ model_idx++;
+ }
+ }
+ // Close the progress dialog
+ progress_dlg.Update(100, "");
+
+ // Show info message
+ wxString msg;
+ wxString bullet_suf = "\n - ";
+ if (!succes_models.empty()) {
+ msg = _L_PLURAL("Folowing model is repaired successfully", "Folowing models are repaired successfully", succes_models.size()) + ":";
+ for (auto& model : succes_models)
+ msg += bullet_suf + from_u8(model);
+ msg += "\n\n";
+ }
+ if (!failed_models.empty()) {
+ msg += _L_PLURAL("Folowing model repair failed", "Folowing models repair failed", failed_models.size()) + ":\n";
+ for (auto& model : failed_models)
+ msg += bullet_suf + from_u8(model.first) + ": " + _(model.second);
+ }
+ if (msg.IsEmpty())
+ msg = _L("Repairing was canceled");
+ // !!! Use wxMessageDialog instead of MessageDialog here
+ // It will not be "dark moded" but the Application will not lose a focus after model repairing
+ wxMessageDialog(nullptr, msg, _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK).ShowModal();
}
void ObjectList::simplify()
@@ -4056,8 +4179,10 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co
// unmark fixed item only
m_objects_model->DeleteWarningIcon(item);
}
- else
- m_objects_model->AddWarningIcon(item);
+ else {
+ auto obj = object(obj_idx);
+ m_objects_model->AddWarningIcon(item, get_warning_icon_name(vol_idx < 0 ? obj->mesh().stats() : obj->volumes[vol_idx]->mesh().stats()));
+ }
}
void ObjectList::msw_rescale()
@@ -4188,7 +4313,7 @@ wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, s
for (const ModelVolume* volume : object->volumes) {
wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name),
volume->type(),
- volume->get_mesh_errors_count() > 0,
+ get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
// add settings to the part, if it has those
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 0fbad1919..491bd2684 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -210,12 +210,12 @@ public:
void get_selection_indexes(std::vector<int>& obj_idxs, std::vector<int>& vol_idxs);
// Get count of errors in the mesh
int get_mesh_errors_count(const int obj_idx, const int vol_idx = -1) const;
- /* Get list of errors in the mesh. Return value is a string, used for the tooltip
- * Function without parameters is for a call from Manipulation panel,
- * when we don't know parameters of selected item
- */
- wxString get_mesh_errors_list(const int obj_idx, const int vol_idx = -1) const;
- wxString get_mesh_errors_list();
+ // Get list of errors in the mesh and name of the warning icon
+ // Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon
+ // Function without parameters is for a call from Manipulation panel,
+ // when we don't know parameters of selected item
+ std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, bool from_plater = false) const;
+ std::pair<wxString, std::string> get_mesh_errors(bool from_plater = false);
void set_tooltip_for_item(const wxPoint& pt);
void selection_changed();
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index d35def196..6eaa6316d 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -132,7 +132,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
return;
wxGetApp().obj_list()->fix_through_netfabb();
- update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
+ update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors());
});
sizer->Add(m_fix_throught_netfab_bitmap);
@@ -776,8 +776,12 @@ void ObjectManipulation::update_item_name(const wxString& item_name)
m_item_name->SetLabel(item_name);
}
-void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
-{
+void ObjectManipulation::update_warning_icon_state(const std::pair<wxString, std::string>& warning)
+{
+ if (const std::string& warning_icon_name = warning.second;
+ !warning_icon_name.empty())
+ m_manifold_warning_bmp = ScalableBitmap(m_parent, warning_icon_name);
+ const wxString& tooltip = warning.first;
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp
index 92ba01775..9b77591be 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp
@@ -194,7 +194,7 @@ public:
#endif // __APPLE__
void update_item_name(const wxString &item_name);
- void update_warning_icon_state(const wxString& tooltip);
+ void update_warning_icon_state(const std::pair<wxString, std::string>& warning);
void msw_rescale();
void sys_color_changed();
void on_change(const std::string& opt_key, int axis, double new_value);
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 2885ee9cb..d50de71cf 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -636,7 +636,6 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
update_layers_slider_mode();
Plater* plater = wxGetApp().plater();
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Info ticks_info_from_model;
if (wxGetApp().is_editor())
ticks_info_from_model = plater->model().custom_gcode_per_print_z;
@@ -644,18 +643,10 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
ticks_info_from_model.mode = CustomGCode::Mode::SingleExtruder;
ticks_info_from_model.gcodes = m_canvas->get_custom_gcode_per_print_z();
}
-#else
- CustomGCode::Info& ticks_info_from_model = plater->model().custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
check_layers_slider_values(ticks_info_from_model.gcodes, layers_z);
//first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
-#else
- m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config());
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-
m_layers_slider->SetSliderValues(layers_z);
assert(m_layers_slider->GetMinValue() == 0);
m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1);
@@ -738,7 +729,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices);
if( bottom_area - top_area > delta_area) {
- std::shared_ptr<NotificationManager> notif_mngr = wxGetApp().plater()->get_notification_manager();
+ NotificationManager *notif_mngr = wxGetApp().plater()->get_notification_manager();
notif_mngr->push_notification(
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotificationLevel,
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
@@ -925,14 +916,10 @@ void Preview::load_print_as_fff(bool keep_z_range)
colors = wxGetApp().plater()->get_colors_for_color_print(m_gcode_result);
if (!gcode_preview_data_valid) {
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_editor())
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
else
color_print_values = m_canvas->get_custom_gcode_per_print_z();
-#else
- color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
colors.push_back("#808080"); // gray color for pause print or custom G-code
}
}
@@ -947,19 +934,14 @@ void Preview::load_print_as_fff(bool keep_z_range)
m_canvas->set_selected_extruder(0);
if (gcode_preview_data_valid) {
// Load the real G-code preview.
- m_canvas->load_gcode_preview(*m_gcode_result);
- m_canvas->refresh_gcode_preview(*m_gcode_result, colors);
+ m_canvas->load_gcode_preview(*m_gcode_result, colors);
m_left_sizer->Show(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
zs = m_canvas->get_gcode_layers_zs();
m_loaded = true;
}
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
else if (wxGetApp().is_editor()) {
-#else
- else {
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
m_left_sizer->Hide(m_bottom_toolbar_panel);
@@ -968,7 +950,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
zs = m_canvas->get_volumes_print_zs(true);
}
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (!zs.empty() && !m_keep_current_preview_type) {
unsigned int number_extruders = wxGetApp().is_editor() ?
(unsigned int)print->extruders().size() :
@@ -992,7 +973,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
}
}
}
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (zs.empty()) {
// all layers filtered out
@@ -1001,23 +981,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
} else
update_layers_slider(zs, keep_z_range);
}
-
-#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
- if (!m_keep_current_preview_type) {
- unsigned int number_extruders = (unsigned int)print->extruders().size();
- const wxString choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes.empty() ?
- _L("Color Print") :
- (number_extruders > 1) ? _L("Tool") : _L("Feature type");
-
- int type = m_choice_view_type->FindString(choice);
- if (m_choice_view_type->GetSelection() != type) {
- if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
- m_choice_view_type->SetSelection(type);
- m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
- }
- }
- }
-#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
void Preview::load_print_as_sla()
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index 64479a39e..735328881 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -53,9 +53,9 @@ void GLGizmoBase::Grabber::render(float size, const std::array<float, 4>& render
if (! cube_initialized) {
// This cannot be done in constructor, OpenGL is not yet
// initialized at that point (on Linux at least).
- TriangleMesh mesh = make_cube(1., 1., 1.);
- mesh.translate(Vec3f(-0.5, -0.5, -0.5));
- const_cast<GLModel&>(cube).init_from(mesh);
+ indexed_triangle_set mesh = its_make_cube(1., 1., 1.);
+ its_translate(mesh, Vec3f(-0.5, -0.5, -0.5));
+ const_cast<GLModel&>(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } });
const_cast<bool&>(cube_initialized) = true;
}
@@ -90,9 +90,9 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
m_highlight_color = DEFAULT_HIGHLIGHT_COLOR;
- m_cone.init_from(make_cone(1., 1., 2 * PI / 24));
- m_sphere.init_from(make_sphere(1., (2 * M_PI) / 24.));
- m_cylinder.init_from(make_cylinder(1., 1., 2 * PI / 24.));
+ m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24));
+ m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.));
+ m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.));
}
void GLGizmoBase::set_hover_id(int id)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 3dcb9e2b1..c8142ba34 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -16,9 +16,7 @@
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
-#if ENABLE_SINKING_CONTOURS
#include "libslic3r/TriangleMeshSlicer.hpp"
-#endif // ENABLE_SINKING_CONTOURS
namespace Slic3r {
namespace GUI {
@@ -92,9 +90,7 @@ void GLGizmoCut::on_render()
m_max_z = box.max.z();
set_cut_z(m_cut_z);
-#if ENABLE_SINKING_CONTOURS
update_contours();
-#endif // ENABLE_SINKING_CONTOURS
const float min_x = box.min.x() - Margin;
const float max_x = box.max.x() + Margin;
@@ -143,13 +139,11 @@ void GLGizmoCut::on_render()
shader->stop_using();
-#if ENABLE_SINKING_CONTOURS
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
glsafe(::glLineWidth(2.0f));
m_cut_contours.contours.render();
glsafe(::glPopMatrix());
-#endif // ENABLE_SINKING_CONTOURS
}
void GLGizmoCut::on_render_for_picking()
@@ -275,7 +269,6 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const
return ret;
}
-#if ENABLE_SINKING_CONTOURS
void GLGizmoCut::update_contours()
{
const Selection& selection = m_parent.get_selection();
@@ -289,10 +282,8 @@ void GLGizmoCut::update_contours()
if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) {
m_cut_contours.cut_z = m_cut_z;
- if (m_cut_contours.object_id != model_object->id()) {
+ if (m_cut_contours.object_id != model_object->id())
m_cut_contours.mesh = model_object->raw_mesh();
- m_cut_contours.mesh.repair();
- }
m_cut_contours.position = box.center();
m_cut_contours.shift = Vec3d::Zero();
@@ -315,7 +306,6 @@ void GLGizmoCut::update_contours()
else
m_cut_contours.contours.reset();
}
-#endif // ENABLE_SINKING_CONTOURS
} // namespace GUI
} // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
index b691357af..4007f89d4 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
@@ -2,11 +2,9 @@
#define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp"
-#if ENABLE_SINKING_CONTOURS
#include "slic3r/GUI/GLModel.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/ObjectID.hpp"
-#endif // ENABLE_SINKING_CONTOURS
namespace Slic3r {
namespace GUI {
@@ -26,7 +24,6 @@ class GLGizmoCut : public GLGizmoBase
bool m_keep_lower{ true };
bool m_rotate_lower{ false };
-#if ENABLE_SINKING_CONTOURS
struct CutContours
{
TriangleMesh mesh;
@@ -39,7 +36,6 @@ class GLGizmoCut : public GLGizmoBase
};
CutContours m_cut_contours;
-#endif // ENABLE_SINKING_CONTOURS
public:
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@@ -66,9 +62,7 @@ private:
void perform_cut(const Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
BoundingBoxf3 bounding_box() const;
-#if ENABLE_SINKING_CONTOURS
void update_contours();
-#endif // ENABLE_SINKING_CONTOURS
};
} // namespace GUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 1ebba4d11..01eeebe10 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -287,13 +287,14 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
float dot_limit = limit.dot(down);
// Now calculate dot product of vert_direction and facets' normals.
- int idx = -1;
- for (const stl_facet &facet : mv->mesh().stl.facet_start) {
- ++idx;
- if (facet.normal.dot(down) > dot_limit) {
+ int idx = 0;
+ const indexed_triangle_set &its = mv->mesh().its;
+ for (stl_triangle_vertex_indices face : its.indices) {
+ if (its_face_normal(its, face).dot(down) > dot_limit) {
m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
m_triangle_selectors.back()->request_update_render_data();
}
+ ++ idx;
}
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
index b8c7d9f92..9e30202bd 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
@@ -142,11 +142,13 @@ void GLGizmoFlatten::update_planes()
// Now we'll go through all the facets and append Points of facets sharing the same normal.
// This part is still performed in mesh coordinate system.
- const int num_of_facets = ch.stl.stats.number_of_facets;
- std::vector<int> facet_queue(num_of_facets, 0);
- std::vector<bool> facet_visited(num_of_facets, false);
- int facet_queue_cnt = 0;
- const stl_normal* normal_ptr = nullptr;
+ const int num_of_facets = ch.facets_count();
+ const std::vector<Vec3f> face_normals = its_face_normals(ch.its);
+ const std::vector<Vec3i> face_neighbors = its_face_neighbors(ch.its);
+ std::vector<int> facet_queue(num_of_facets, 0);
+ std::vector<bool> facet_visited(num_of_facets, false);
+ int facet_queue_cnt = 0;
+ const stl_normal* normal_ptr = nullptr;
while (1) {
// Find next unvisited triangle:
int facet_idx = 0;
@@ -154,7 +156,7 @@ void GLGizmoFlatten::update_planes()
if (!facet_visited[facet_idx]) {
facet_queue[facet_queue_cnt ++] = facet_idx;
facet_visited[facet_idx] = true;
- normal_ptr = &ch.stl.facet_start[facet_idx].normal;
+ normal_ptr = &face_normals[facet_idx];
m_planes.emplace_back();
break;
}
@@ -163,18 +165,16 @@ void GLGizmoFlatten::update_planes()
while (facet_queue_cnt > 0) {
int facet_idx = facet_queue[-- facet_queue_cnt];
- const stl_normal& this_normal = ch.stl.facet_start[facet_idx].normal;
+ const stl_normal& this_normal = face_normals[facet_idx];
if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) {
- stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex;
+ const Vec3i face = ch.its.indices[facet_idx];
for (int j=0; j<3; ++j)
- m_planes.back().vertices.emplace_back(first_vertex[j].cast<double>());
+ m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast<double>());
facet_visited[facet_idx] = true;
- for (int j = 0; j < 3; ++ j) {
- int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j];
- if (! facet_visited[neighbor_idx])
+ for (int j = 0; j < 3; ++ j)
+ if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
- }
}
}
m_planes.back().normal = normal_ptr->cast<double>();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index f405f457d..17630e5c6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -20,7 +20,7 @@ namespace GUI {
GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
- m_vbo_cylinder.init_from(make_cylinder(1., 1.));
+ m_vbo_cylinder.init_from(its_make_cylinder(1., 1.));
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
index 4bd98f8c8..30f7ff7cf 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
@@ -291,6 +291,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_imgui->text("");
ImGui::Separator();
+ ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("first_color"));
ImGui::SameLine(combo_label_width);
ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
@@ -302,6 +303,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
m_modified_extruders_colors[m_first_selected_extruder_idx] = {first_color.x, first_color.y, first_color.z, first_color.w};
+ ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("second_color"));
ImGui::SameLine(combo_label_width);
ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
@@ -317,14 +319,12 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::Separator();
- ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("tool_type"));
float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(2.f)) / 2.f;
ImGui::NewLine();
- ImGui::AlignTextToFramePadding();
ImGui::SameLine(tool_type_offset + m_imgui->scaled(0.f));
ImGui::PushItemWidth(tool_type_radio_brush);
if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) {
@@ -382,12 +382,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::Separator();
if(m_tool_type == ToolType::BRUSH) {
- ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_type"));
ImGui::NewLine();
float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(2.f)) / 2.f;
- ImGui::AlignTextToFramePadding();
ImGui::SameLine(cursor_type_offset + m_imgui->scaled(0.f));
ImGui::PushItemWidth(cursor_type_radio_sphere);
if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
@@ -458,8 +456,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::Separator();
} else if(m_tool_type == ToolType::SMART_FILL) {
- m_imgui->text(m_desc["smart_fill_angle"] + ":");
ImGui::AlignTextToFramePadding();
+ m_imgui->text(m_desc["smart_fill_angle"] + ":");
std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in MMU gizmo,"
"placed after the number with no whitespace in between.");
ImGui::SameLine(sliders_width);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index e7b6a0ad6..e73a85647 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -20,7 +20,7 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam
, m_starting_box_center(Vec3d::Zero())
, m_starting_box_bottom_center(Vec3d::Zero())
{
- m_vbo_cone.init_from(make_cone(1., 1., 2*PI/36));
+ m_vbo_cone.init_from(its_make_cone(1., 1., 2*PI/36));
}
std::string GLGizmoMove3D::get_tooltip() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 155738b32..be8fc331d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -8,8 +8,10 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Plater.hpp"
-#include "libslic3r/PresetBundle.hpp"
+#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/Model.hpp"
+#include "libslic3r/PresetBundle.hpp"
+#include "libslic3r/TriangleMesh.hpp"
@@ -20,17 +22,10 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
: GLGizmoBase(parent, icon_filename, sprite_id)
{
// Make sphere and save it into a vertex buffer.
- const TriangleMesh sphere_mesh = make_sphere(1., (2*M_PI)/24.);
- for (size_t i=0; i<sphere_mesh.its.vertices.size(); ++i)
- m_vbo_sphere.push_geometry(sphere_mesh.its.vertices[i].cast<double>(),
- sphere_mesh.stl.facet_start[i].normal.cast<double>());
- for (const stl_triangle_vertex_indices& indices : sphere_mesh.its.indices)
- m_vbo_sphere.push_triangle(indices(0), indices(1), indices(2));
+ m_vbo_sphere.load_its_flat_shading(its_make_sphere(1., (2*M_PI)/24.));
m_vbo_sphere.finalize_geometry(true);
}
-
-
// port of 948bc382655993721d93d3b9fce9b0186fcfb211
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
{
@@ -48,14 +43,14 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
if (activate && !m_internal_stack_active) {
if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str)
- Plater::TakeSnapshot(plater, str);
+ Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::EnteringGizmo);
plater->enter_gizmos_stack();
m_internal_stack_active = true;
}
if (!activate && m_internal_stack_active) {
plater->leave_gizmos_stack();
if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str)
- Plater::TakeSnapshot(plater, str);
+ Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::LeavingGizmoWithAction);
m_internal_stack_active = false;
}
}
@@ -632,9 +627,15 @@ void TriangleSelectorGUI::update_render_data()
GLIndexedVertexArray &iva = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers;
int & cnt = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt;
-
- for (int i = 0; i < 3; ++i)
- iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
+ const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v;
+ const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v;
+ const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v;
+ //FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort
+ // or the current implementation may be more cache friendly.
+ const Vec3f n = (v1 - v0).cross(v2 - v1).normalized();
+ iva.push_geometry(v0, n);
+ iva.push_geometry(v1, n);
+ iva.push_geometry(v2, n);
iva.push_triangle(cnt, cnt + 1, cnt + 2);
cnt += 3;
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
index a2c4910e3..d4ee885b3 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
@@ -313,9 +313,8 @@ void GLGizmoSimplify::process()
}
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
- auto tm = std::make_unique<TriangleMesh>(its);
- tm->repair();
- m_volume->set_mesh(std::move(tm));
+ m_volume->set_mesh(its);
+ m_volume->calculate_convex_hull();
m_volume->set_new_unique_id();
m_volume->get_object()->invalidate_bounding_box();
m_need_reload = true;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 11446d6d7..b7a6d89fa 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -4,6 +4,7 @@
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "slic3r/GUI/MainFrame.hpp"
+#include "slic3r/Utils/UndoRedo.hpp"
#include <GL/glew.h>
@@ -881,7 +882,7 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
void GLGizmoSlaSupports::ask_about_changes_call_after(std::function<void()> on_yes, std::function<void()> on_no)
{
- wxGetApp().CallAfter([this, on_yes, on_no]() {
+ wxGetApp().CallAfter([on_yes, on_no]() {
// Following is called through CallAfter, because otherwise there was a problem
// on OSX with the wxMessageDialog being shown several times when clicked into.
MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
index b22e72be9..cf4b18a86 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
@@ -270,11 +270,10 @@ void HollowedMesh::on_update()
m_drainholes = print_object->model_object()->sla_drain_holes;
m_old_hollowing_timestamp = timestamp;
- const indexed_triangle_set &interior = print_object->hollowed_interior_mesh();
+ indexed_triangle_set interior = print_object->hollowed_interior_mesh();
if (!interior.empty()) {
- m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(interior);
- m_hollowed_interior_transformed->repaired = false;
- m_hollowed_interior_transformed->repair(true);
+ its_flip_triangles(interior);
+ m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
m_hollowed_interior_transformed->transform(trafo_inv);
}
}
diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp
index 9535e0126..38ea95f2c 100644
--- a/src/slic3r/GUI/InstanceCheck.cpp
+++ b/src/slic3r/GUI/InstanceCheck.cpp
@@ -352,10 +352,16 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) {
#endif
instance_check_internal::send_message(cla.cl_string, lock_name);
- BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
+ BOOST_LOG_TRIVIAL(error) << "Instance check: Another instance found. This instance will terminate. Lock file of current running instance is located at " << data_dir() <<
+#ifdef _WIN32
+ "\\cache\\"
+#else // mac & linx
+ "/cache/"
+#endif
+ << lock_name << ".lock";
return true;
}
- BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
+ BOOST_LOG_TRIVIAL(info) << "Instance check: Another instance not found or single-instance not set.";
return false;
}
diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp
index 2ccb7a04f..a5ecc0c83 100644
--- a/src/slic3r/GUI/Jobs/ArrangeJob.hpp
+++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp
@@ -9,7 +9,6 @@ namespace Slic3r {
class ModelInstance;
namespace GUI {
-class NotificationManager;
class ArrangeJob : public PlaterJob
{
@@ -18,21 +17,21 @@ class ArrangeJob : public PlaterJob
ArrangePolygons m_selected, m_unselected, m_unprintable;
std::vector<ModelInstance*> m_unarranged;
-
+
// clear m_selected and m_unselected, reserve space for next usage
void clear_input();
// Prepare all objects on the bed regardless of the selection
void prepare_all();
-
+
// Prepare the selected and unselected items separately. If nothing is
// selected, behaves as if everything would be selected.
void prepare_selected();
ArrangePolygon get_arrange_poly_(ModelInstance *mi);
-
+
protected:
-
+
void prepare() override;
void on_exception(const std::exception_ptr &) override;
@@ -40,15 +39,15 @@ protected:
void process() override;
public:
- ArrangeJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
- : PlaterJob{nm, plater}
+ ArrangeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
+ : PlaterJob{std::move(pri), plater}
{}
-
+
int status_range() const override
{
return int(m_selected.size() + m_unprintable.size());
}
-
+
void finalize() override;
};
diff --git a/src/slic3r/GUI/Jobs/FillBedJob.hpp b/src/slic3r/GUI/Jobs/FillBedJob.hpp
index 548974fa6..bf407656d 100644
--- a/src/slic3r/GUI/Jobs/FillBedJob.hpp
+++ b/src/slic3r/GUI/Jobs/FillBedJob.hpp
@@ -6,7 +6,6 @@
namespace Slic3r { namespace GUI {
class Plater;
-class NotificationManager;
class FillBedJob : public PlaterJob
{
@@ -28,8 +27,8 @@ protected:
void process() override;
public:
- FillBedJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
- : PlaterJob{nm, plater}
+ FillBedJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
+ : PlaterJob{std::move(pri), plater}
{}
int status_range() const override
diff --git a/src/slic3r/GUI/Jobs/Job.cpp b/src/slic3r/GUI/Jobs/Job.cpp
index c198de40c..7590f2d42 100644
--- a/src/slic3r/GUI/Jobs/Job.cpp
+++ b/src/slic3r/GUI/Jobs/Job.cpp
@@ -2,11 +2,9 @@
#include <exception>
#include "Job.hpp"
-#include "../NotificationManager.hpp"
#include <libslic3r/Thread.hpp>
#include <boost/log/trivial.hpp>
-
namespace Slic3r {
void GUI::Job::run(std::exception_ptr &eptr)
@@ -19,7 +17,7 @@ void GUI::Job::run(std::exception_ptr &eptr)
}
m_running.store(false);
-
+
// ensure to call the last status to finalize the job
update_status(status_range(), "");
}
@@ -32,8 +30,8 @@ void GUI::Job::update_status(int st, const wxString &msg)
wxQueueEvent(this, evt);
}
-GUI::Job::Job(std::shared_ptr<NotificationManager> nm)
- : m_notifications(nm)
+GUI::Job::Job(std::shared_ptr<ProgressIndicator> pri)
+ : m_progress(std::move(pri))
{
m_thread_evt_id = wxNewId();
@@ -42,21 +40,21 @@ GUI::Job::Job(std::shared_ptr<NotificationManager> nm)
auto msg = evt.GetString();
if (!msg.empty() && !m_worker_error)
- m_notifications->progress_indicator_set_status_text(msg.ToUTF8().data());
+ m_progress->set_status_text(msg.ToUTF8().data());
if (m_finalized) return;
- m_notifications->progress_indicator_set_progress(evt.GetInt());
+ m_progress->set_progress(evt.GetInt());
if (evt.GetInt() == status_range() || m_worker_error) {
// set back the original range and cancel callback
- m_notifications->progress_indicator_set_range(m_range);
- m_notifications->progress_indicator_set_cancel_callback();
+ m_progress->set_range(m_range);
+ m_progress->set_cancel_callback();
wxEndBusyCursor();
-
+
if (m_worker_error) {
m_finalized = true;
- m_notifications->progress_indicator_set_status_text("");
- m_notifications->progress_indicator_set_progress(m_range);
+ m_progress->set_status_text("");
+ m_progress->set_progress(m_range);
on_exception(m_worker_error);
}
else {
@@ -86,22 +84,22 @@ void GUI::Job::start()
{ // Start the job. No effect if the job is already running
if (!m_running.load()) {
prepare();
-
+
// Save the current status indicatior range and push the new one
- m_range = m_notifications->progress_indicator_get_range();
- m_notifications->progress_indicator_set_range(status_range());
-
+ m_range = m_progress->get_range();
+ m_progress->set_range(status_range());
+
// init cancellation flag and set the cancel callback
m_canceled.store(false);
- m_notifications->progress_indicator_set_cancel_callback(
+ m_progress->set_cancel_callback(
[this]() { m_canceled.store(true); });
-
+
m_finalized = false;
m_finalizing = false;
-
+
// Changing cursor to busy
wxBeginBusyCursor();
-
+
try { // Execute the job
m_worker_error = nullptr;
m_thread = create_thread([this] { this->run(m_worker_error); });
@@ -110,7 +108,7 @@ void GUI::Job::start()
_(L("ERROR: not enough resources to "
"execute a new job.")));
}
-
+
// The state changes will be undone when the process hits the
// last status value, in the status update handler (see ctor)
}
@@ -119,12 +117,12 @@ void GUI::Job::start()
bool GUI::Job::join(int timeout_ms)
{
if (!m_thread.joinable()) return true;
-
+
if (timeout_ms <= 0)
m_thread.join();
else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms)))
return false;
-
+
return true;
}
@@ -137,10 +135,10 @@ void GUI::ExclusiveJobGroup::start(size_t jid) {
void GUI::ExclusiveJobGroup::join_all(int wait_ms)
{
std::vector<bool> aborted(m_jobs.size(), false);
-
+
for (size_t jid = 0; jid < m_jobs.size(); ++jid)
aborted[jid] = m_jobs[jid]->join(wait_ms);
-
+
if (!std::all_of(aborted.begin(), aborted.end(), [](bool t) { return t; }))
BOOST_LOG_TRIVIAL(error) << "Could not abort a job!";
}
diff --git a/src/slic3r/GUI/Jobs/Job.hpp b/src/slic3r/GUI/Jobs/Job.hpp
index ed80d8b5f..8243ce943 100644
--- a/src/slic3r/GUI/Jobs/Job.hpp
+++ b/src/slic3r/GUI/Jobs/Job.hpp
@@ -8,13 +8,14 @@
#include <slic3r/GUI/I18N.hpp>
+#include "ProgressIndicator.hpp"
+
#include <wx/event.h>
#include <boost/thread.hpp>
namespace Slic3r { namespace GUI {
-class NotificationManager;
// A class to handle UI jobs like arranging and optimizing rotation.
// These are not instant jobs, the user has to be informed about their
// state in the status progress indicator. On the other hand they are
@@ -32,7 +33,7 @@ class Job : public wxEvtHandler
boost::thread m_thread;
std::atomic<bool> m_running{false}, m_canceled{false};
bool m_finalized = false, m_finalizing = false;
- std::shared_ptr<NotificationManager> m_notifications;
+ std::shared_ptr<ProgressIndicator> m_progress;
std::exception_ptr m_worker_error = nullptr;
void run(std::exception_ptr &);
@@ -64,7 +65,7 @@ protected:
}
public:
- Job(std::shared_ptr<NotificationManager> nm);
+ Job(std::shared_ptr<ProgressIndicator> pri);
bool is_finalized() const { return m_finalized; }
diff --git a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp
new file mode 100644
index 000000000..cb7170568
--- /dev/null
+++ b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp
@@ -0,0 +1,33 @@
+#include "NotificationProgressIndicator.hpp"
+#include "slic3r/GUI/NotificationManager.hpp"
+
+namespace Slic3r { namespace GUI {
+
+NotificationProgressIndicator::NotificationProgressIndicator(NotificationManager *nm): m_nm{nm} {}
+
+void NotificationProgressIndicator::set_range(int range)
+{
+ m_nm->progress_indicator_set_range(range);
+}
+
+void NotificationProgressIndicator::set_cancel_callback(CancelFn fn)
+{
+ m_nm->progress_indicator_set_cancel_callback(std::move(fn));
+}
+
+void NotificationProgressIndicator::set_progress(int pr)
+{
+ m_nm->progress_indicator_set_progress(pr);
+}
+
+void NotificationProgressIndicator::set_status_text(const char *msg)
+{
+ m_nm->progress_indicator_set_status_text(msg);
+}
+
+int NotificationProgressIndicator::get_range() const
+{
+ return m_nm->progress_indicator_get_range();
+}
+
+}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp
new file mode 100644
index 000000000..6b03af69d
--- /dev/null
+++ b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp
@@ -0,0 +1,26 @@
+#ifndef NOTIFICATIONPROGRESSINDICATOR_HPP
+#define NOTIFICATIONPROGRESSINDICATOR_HPP
+
+#include "ProgressIndicator.hpp"
+
+namespace Slic3r { namespace GUI {
+
+class NotificationManager;
+
+class NotificationProgressIndicator: public ProgressIndicator {
+ NotificationManager *m_nm = nullptr;
+
+public:
+
+ explicit NotificationProgressIndicator(NotificationManager *nm);
+
+ void set_range(int range) override;
+ void set_cancel_callback(CancelFn = CancelFn()) override;
+ void set_progress(int pr) override;
+ void set_status_text(const char *) override; // utf8 char array
+ int get_range() const override;
+};
+
+}} // namespace Slic3r::GUI
+
+#endif // NOTIFICATIONPROGRESSINDICATOR_HPP
diff --git a/src/slic3r/GUI/Jobs/PlaterJob.hpp b/src/slic3r/GUI/Jobs/PlaterJob.hpp
index 52e5741aa..fcf0a54b8 100644
--- a/src/slic3r/GUI/Jobs/PlaterJob.hpp
+++ b/src/slic3r/GUI/Jobs/PlaterJob.hpp
@@ -6,7 +6,6 @@
namespace Slic3r { namespace GUI {
class Plater;
-class NotificationManager;
class PlaterJob : public Job {
protected:
@@ -16,8 +15,8 @@ protected:
public:
- PlaterJob(std::shared_ptr<NotificationManager> nm, Plater *plater):
- Job{nm}, m_plater{plater} {}
+ PlaterJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater):
+ Job{std::move(pri)}, m_plater{plater} {}
};
}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp
index f967bb7a3..cdb367f23 100644
--- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp
+++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp
@@ -10,8 +10,6 @@ namespace Slic3r {
namespace GUI {
-class NotificationManager;
-
class RotoptimizeJob : public PlaterJob
{
using FindFn = std::function<Vec2d(const ModelObject & mo,
@@ -54,8 +52,8 @@ protected:
public:
- RotoptimizeJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
- : PlaterJob{nm, plater}
+ RotoptimizeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
+ : PlaterJob{std::move(pri), plater}
{}
void finalize() override;
diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp
index 9b9151f77..3d611ffc3 100644
--- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp
+++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp
@@ -20,79 +20,79 @@
namespace Slic3r { namespace GUI {
enum class Sel { modelAndProfile, profileOnly, modelOnly};
-
+
class ImportDlg: public wxDialog {
wxFilePickerCtrl *m_filepicker;
wxComboBox *m_import_dropdown, *m_quality_dropdown;
-
+
public:
ImportDlg(Plater *plater)
: wxDialog{plater, wxID_ANY, "Import SLA archive"}
{
auto szvert = new wxBoxSizer{wxVERTICAL};
auto szfilepck = new wxBoxSizer{wxHORIZONTAL};
-
+
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY,
from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")),
"SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP",
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
-
+
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
szfilepck->Add(m_filepicker, 1);
szvert->Add(szfilepck, 0, wxALL | wxEXPAND, 5);
-
+
auto szchoices = new wxBoxSizer{wxHORIZONTAL};
-
+
static const std::vector<wxString> inp_choices = {
_(L("Import model and profile")),
_(L("Import profile only")),
_(L("Import model only"))
};
-
+
m_import_dropdown = new wxComboBox(
this, wxID_ANY, inp_choices[0], wxDefaultPosition, wxDefaultSize,
inp_choices.size(), inp_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
-
+
szchoices->Add(m_import_dropdown);
szchoices->Add(new wxStaticText(this, wxID_ANY, _L("Quality") + ": "), 0, wxALIGN_CENTER | wxALL, 5);
-
+
static const std::vector<wxString> qual_choices = {
_(L("Accurate")),
_(L("Balanced")),
_(L("Quick"))
};
-
+
m_quality_dropdown = new wxComboBox(
this, wxID_ANY, qual_choices[0], wxDefaultPosition, wxDefaultSize,
qual_choices.size(), qual_choices.data(), wxCB_READONLY | wxCB_DROPDOWN);
szchoices->Add(m_quality_dropdown);
-
+
m_import_dropdown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
if (get_selection() == Sel::profileOnly)
m_quality_dropdown->Disable();
else m_quality_dropdown->Enable();
});
-
+
szvert->Add(szchoices, 0, wxALL, 5);
szvert->AddStretchSpacer(1);
auto szbtn = new wxBoxSizer(wxHORIZONTAL);
szbtn->Add(new wxButton{this, wxID_CANCEL});
szbtn->Add(new wxButton{this, wxID_OK});
szvert->Add(szbtn, 0, wxALIGN_RIGHT | wxALL, 5);
-
+
SetSizerAndFit(szvert);
}
-
+
Sel get_selection() const
{
int sel = m_import_dropdown->GetSelection();
return Sel(std::min(int(Sel::modelOnly), std::max(0, sel)));
}
-
+
Vec2i get_marchsq_windowsize() const
{
enum { Accurate, Balanced, Fast};
-
+
switch(m_quality_dropdown->GetSelection())
{
case Fast: return {8, 8};
@@ -102,7 +102,7 @@ public:
return {2, 2};
}
}
-
+
wxString get_path() const
{
return m_filepicker->GetPath();
@@ -112,7 +112,7 @@ public:
class SLAImportJob::priv {
public:
Plater *plater;
-
+
Sel sel = Sel::modelAndProfile;
indexed_triangle_set mesh;
@@ -125,8 +125,8 @@ public:
priv(Plater *plt) : plater{plt} {}
};
-SLAImportJob::SLAImportJob(std::shared_ptr<NotificationManager> nm, Plater *plater)
- : PlaterJob{nm, plater}, p{std::make_unique<priv>(plater)}
+SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
+ : PlaterJob{std::move(pri), plater}, p{std::make_unique<priv>(plater)}
{}
SLAImportJob::~SLAImportJob() = default;
@@ -138,27 +138,27 @@ void SLAImportJob::process()
update_status(int(s), _(L("Importing SLA archive")));
return !was_canceled();
};
-
+
if (p->path.empty()) return;
-
+
std::string path = p->path.ToUTF8().data();
try {
switch (p->sel) {
case Sel::modelAndProfile:
- p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr);
- break;
case Sel::modelOnly:
- p->config_substitutions = import_sla_archive(path, p->win, p->mesh, progr);
+ p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr);
break;
case Sel::profileOnly:
p->config_substitutions = import_sla_archive(path, p->profile);
break;
}
-
+ } catch (MissingProfileError &) {
+ p->err = _L("The archive doesn't contain any profile data. Try to import after switching "
+ "to an SLA profile that can be used as fallback.").ToStdString();
} catch (std::exception &ex) {
p->err = ex.what();
}
-
+
update_status(100, was_canceled() ? _(L("Importing canceled.")) :
_(L("Importing done.")));
}
@@ -167,7 +167,7 @@ void SLAImportJob::reset()
{
p->sel = Sel::modelAndProfile;
p->mesh = {};
- p->profile = {};
+ p->profile = m_plater->sla_print().full_print_config();
p->win = {2, 2};
p->path.Clear();
}
@@ -175,9 +175,9 @@ void SLAImportJob::reset()
void SLAImportJob::prepare()
{
reset();
-
+
ImportDlg dlg{p->plater};
-
+
if (dlg.ShowModal() == wxID_OK) {
auto path = dlg.get_path();
auto nm = wxFileName(path);
@@ -194,16 +194,27 @@ void SLAImportJob::finalize()
{
// Ignore the arrange result if aborted.
if (was_canceled()) return;
-
+
if (!p->err.empty()) {
show_error(p->plater, p->err);
p->err = "";
return;
}
-
+
std::string name = wxFileName(p->path).GetName().ToUTF8().data();
-
- if (!p->profile.empty()) {
+
+ if (p->profile.empty()) {
+ m_plater->get_notification_manager()->push_notification(
+ NotificationType::CustomNotification,
+ NotificationManager::NotificationLevel::WarningNotificationLevel,
+ _L("Loaded archive did not contain any profile data. "
+ "The current SLA profile was used as fallback.").ToStdString());
+ }
+
+ if (p->sel != Sel::modelOnly) {
+ if (p->profile.empty())
+ p->profile = m_plater->sla_print().full_print_config();
+
const ModelObjectPtrs& objects = p->plater->model().objects;
for (auto object : objects)
if (object->volumes.size() > 1)
@@ -214,15 +225,15 @@ void SLAImportJob::finalize()
_(L("Attention!")) );
return;
}
-
+
DynamicPrintConfig config = {};
config.apply(SLAFullPrintConfig::defaults());
config += std::move(p->profile);
-
+
wxGetApp().preset_bundle->load_config_model(name, std::move(config));
wxGetApp().load_current_presets();
}
-
+
if (!p->mesh.empty()) {
bool is_centered = false;
p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh},
diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.hpp b/src/slic3r/GUI/Jobs/SLAImportJob.hpp
index 7bc4d5e7e..c2ca10ef6 100644
--- a/src/slic3r/GUI/Jobs/SLAImportJob.hpp
+++ b/src/slic3r/GUI/Jobs/SLAImportJob.hpp
@@ -5,20 +5,18 @@
namespace Slic3r { namespace GUI {
-class NotificationManager;
-
class SLAImportJob : public PlaterJob {
class priv;
-
+
std::unique_ptr<priv> p;
-
+
protected:
void prepare() override;
void process() override;
void finalize() override;
public:
- SLAImportJob(std::shared_ptr<NotificationManager> nm, Plater *plater);
+ SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater);
~SLAImportJob();
void reset();
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 6d5fbf11c..b42a9c7bf 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -222,13 +222,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
}
if (m_plater != nullptr) {
- int saved_project = m_plater->save_project_if_dirty();
+ int saved_project = m_plater->save_project_if_dirty(_L("Closing PrusaSlicer. Current project is modified."));
if (saved_project == wxID_CANCEL) {
event.Veto();
return;
}
// check unsaved changes only if project wasn't saved
- else if (saved_project == wxID_NO && event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) {
+ else if (saved_project == wxID_NO && event.CanVeto() &&
+ !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) {
event.Veto();
return;
}
@@ -434,8 +435,9 @@ void MainFrame::update_layout()
{
m_plater->Reparent(m_tabpanel);
#ifdef _MSW_DARK_MODE
+ m_plater->Layout();
if (!wxGetApp().tabs_as_menu())
- dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"));
+ dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"), true);
else
#endif
m_tabpanel->InsertPage(0, m_plater, _L("Plater"));
@@ -460,7 +462,7 @@ void MainFrame::update_layout()
m_plater_page = new wxPanel(m_tabpanel);
#ifdef _MSW_DARK_MODE
if (!wxGetApp().tabs_as_menu())
- dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"));
+ dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"), true);
else
#endif
m_tabpanel->InsertPage(0, m_plater_page, _L("Plater")); // empty panel just for Plater tab */
@@ -820,7 +822,10 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab)
bool MainFrame::can_start_new_project() const
{
- return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || GetTitle().StartsWith('*') || !m_plater->model().objects.empty());
+ return m_plater && (!m_plater->get_project_filename(".3mf").IsEmpty() ||
+ GetTitle().StartsWith('*')||
+ wxGetApp().has_current_preset_changes() ||
+ !m_plater->model().objects.empty() );
}
bool MainFrame::can_save() const
@@ -852,13 +857,14 @@ void MainFrame::save_project()
save_project_as(m_plater->get_project_filename(".3mf"));
}
-void MainFrame::save_project_as(const wxString& filename)
+bool MainFrame::save_project_as(const wxString& filename)
{
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
if (ret) {
// wxGetApp().update_saved_preset_from_current_preset();
m_plater->reset_project_dirty_after_save();
}
+ return ret;
}
bool MainFrame::can_export_model() const
@@ -1151,8 +1157,10 @@ void MainFrame::init_menubar_as_editor()
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
size_t file_id = evt.GetId() - wxID_FILE1;
wxString filename = m_recent_projects.GetHistoryFile(file_id);
- if (wxFileExists(filename))
- m_plater->load_project(filename);
+ if (wxFileExists(filename)) {
+ if (wxGetApp().can_load_project())
+ m_plater->load_project(filename);
+ }
else
{
//wxMessageDialog msg(this, _L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?"), _L("Error"), wxYES_NO | wxYES_DEFAULT);
@@ -1740,7 +1748,6 @@ void MainFrame::repair_stl()
Slic3r::TriangleMesh tmesh;
tmesh.ReadSTLFile(input_file.ToUTF8().data());
- tmesh.repair();
tmesh.WriteOBJFile(output_file.ToUTF8().data());
Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair"));
}
@@ -1773,7 +1780,7 @@ void MainFrame::export_config()
// Load a config file containing a Print, Filament & Printer preset.
void MainFrame::load_config_file()
{
- if (!wxGetApp().check_and_save_current_preset_changes())
+ if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration file"), "", false))
return;
wxFileDialog dlg(this, _L("Select configuration to load:"),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
@@ -1804,7 +1811,8 @@ bool MainFrame::load_config_file(const std::string &path)
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
{
- if (!wxGetApp().check_and_save_current_preset_changes())
+ if (!wxGetApp().check_and_save_current_preset_changes(_L("Exporting configuration bundle"),
+ _L("Some presets are modified and the unsaved changes will not be exported into configuration bundle."), false, true))
return;
// validate current configuration in case it's dirty
auto err = wxGetApp().preset_bundle->full_config().validate();
@@ -1836,7 +1844,7 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
// but that behavior was not documented and likely buggy.
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
{
- if (!wxGetApp().check_and_save_current_preset_changes())
+ if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration bundle"), "", false))
return;
if (file.IsEmpty()) {
wxFileDialog dlg(this, _L("Select configuration to load:"),
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 487d002af..1b66a6052 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -190,7 +190,7 @@ public:
bool can_save() const;
bool can_save_as() const;
void save_project();
- void save_project_as(const wxString& filename = wxString());
+ bool save_project_as(const wxString& filename = wxString());
void add_to_recent_projects(const wxString& filename);
void technology_changed();
diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp
index 75232c930..0f72d0f78 100644
--- a/src/slic3r/GUI/MeshUtils.cpp
+++ b/src/slic3r/GUI/MeshUtils.cpp
@@ -91,11 +91,9 @@ void MeshClipper::recalculate_triangles()
MeshSlicingParams slicing_params;
slicing_params.trafo.rotate(Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(up, Vec3d::UnitZ()));
- assert(m_mesh->has_shared_vertices());
ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params));
if (m_negative_mesh && !m_negative_mesh->empty()) {
- assert(m_negative_mesh->has_shared_vertices());
ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params));
expolys = diff_ex(expolys, neg_expolys);
}
@@ -255,11 +253,10 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
std::vector<unsigned> out;
const Transform3d& instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true);
- Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
- Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
- Vec3f scaling = trafo.get_scaling_factor().cast<float>();
- direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
- const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
+ Vec3d direction_to_camera = -camera.get_dir_forward();
+ Vec3d direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera).normalized().eval();
+ direction_to_camera_mesh = direction_to_camera_mesh.cwiseProduct(trafo.get_scaling_factor());
+ const Transform3d inverse_trafo = trafo.get_matrix().inverse();
for (size_t i=0; i<points.size(); ++i) {
const Vec3f& pt = points[i];
@@ -270,9 +267,8 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<sla::IndexedMesh::hit_result> hits;
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
- hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast<double>(),
- direction_to_camera.cast<double>());
-
+ hits = m_emesh.query_ray_hits((inverse_trafo * pt.cast<double>() + direction_to_camera_mesh * EPSILON),
+ direction_to_camera_mesh);
if (! hits.empty()) {
// If the closest hit facet normal points in the same direction as the ray,
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index 65c326116..767abd7fd 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -30,11 +30,8 @@ public:
ClippingPlane(const Vec3d& direction, double offset)
{
- Vec3d norm_dir = direction.normalized();
- m_data[0] = norm_dir(0);
- m_data[1] = norm_dir(1);
- m_data[2] = norm_dir(2);
- m_data[3] = offset;
+ set_normal(direction);
+ set_offset(offset);
}
bool operator==(const ClippingPlane& cp) const {
@@ -48,7 +45,13 @@ public:
}
bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
- void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
+ void set_normal(const Vec3d& normal)
+ {
+ const Vec3d norm_dir = normal.normalized();
+ m_data[0] = norm_dir.x();
+ m_data[1] = norm_dir.y();
+ m_data[2] = norm_dir.z();
+ }
void set_offset(double offset) { m_data[3] = offset; }
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
bool is_active() const { return m_data[3] != DBL_MAX; }
@@ -113,10 +116,8 @@ public:
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
+ , m_normals(its_face_normals(mesh.its))
{
- m_normals.reserve(mesh.stl.facet_start.size());
- for (const stl_facet& facet : mesh.stl.facet_start)
- m_normals.push_back(facet.normal);
}
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp
index 56e6ddf6c..ff5020b9c 100644
--- a/src/slic3r/GUI/Notebook.hpp
+++ b/src/slic3r/GUI/Notebook.hpp
@@ -84,6 +84,9 @@ public:
if (int page_idx = evt.GetId(); page_idx >= 0)
SetSelection(page_idx);
});
+
+ this->Bind(wxEVT_NAVIGATION_KEY, &Notebook::OnNavigationKey, this);
+
return true;
}
@@ -165,8 +168,8 @@ public:
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name);
- if (!DoSetSelectionAfterInsertion(n, bSelect))
- page->Hide();
+ if (bSelect)
+ SetSelection(n);
return true;
}
@@ -174,7 +177,14 @@ public:
virtual int SetSelection(size_t n) override
{
GetBtnsListCtrl()->SetSelection(n);
- return DoSetSelection(n, SetSelection_SendEvent);
+ int ret = DoSetSelection(n, SetSelection_SendEvent);
+
+ // check that only the selected page is visible and others are hidden:
+ for (size_t page = 0; page < m_pages.size(); page++)
+ if (page != n)
+ m_pages[page]->Hide();
+
+ return ret;
}
virtual int ChangeSelection(size_t n) override
@@ -235,6 +245,89 @@ public:
GetBtnsListCtrl()->Rescale();
}
+ void Notebook::OnNavigationKey(wxNavigationKeyEvent& event)
+ {
+ if (event.IsWindowChange()) {
+ // change pages
+ AdvanceSelection(event.GetDirection());
+ }
+ else {
+ // we get this event in 3 cases
+ //
+ // a) one of our pages might have generated it because the user TABbed
+ // out from it in which case we should propagate the event upwards and
+ // our parent will take care of setting the focus to prev/next sibling
+ //
+ // or
+ //
+ // b) the parent panel wants to give the focus to us so that we
+ // forward it to our selected page. We can't deal with this in
+ // OnSetFocus() because we don't know which direction the focus came
+ // from in this case and so can't choose between setting the focus to
+ // first or last panel child
+ //
+ // or
+ //
+ // c) we ourselves (see MSWTranslateMessage) generated the event
+ //
+ wxWindow* const parent = GetParent();
+
+ // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
+ const bool isFromParent = event.GetEventObject() == (wxObject*)parent;
+ const bool isFromSelf = event.GetEventObject() == (wxObject*)this;
+ const bool isForward = event.GetDirection();
+
+ if (isFromSelf && !isForward)
+ {
+ // focus is currently on notebook tab and should leave
+ // it backwards (Shift-TAB)
+ event.SetCurrentFocus(this);
+ parent->HandleWindowEvent(event);
+ }
+ else if (isFromParent || isFromSelf)
+ {
+ // no, it doesn't come from child, case (b) or (c): forward to a
+ // page but only if entering notebook page (i.e. direction is
+ // backwards (Shift-TAB) comething from out-of-notebook, or
+ // direction is forward (TAB) from ourselves),
+ if (m_selection != wxNOT_FOUND &&
+ (!event.GetDirection() || isFromSelf))
+ {
+ // so that the page knows that the event comes from it's parent
+ // and is being propagated downwards
+ event.SetEventObject(this);
+
+ wxWindow* page = m_pages[m_selection];
+ if (!page->HandleWindowEvent(event))
+ {
+ page->SetFocus();
+ }
+ //else: page manages focus inside it itself
+ }
+ else // otherwise set the focus to the notebook itself
+ {
+ SetFocus();
+ }
+ }
+ else
+ {
+ // it comes from our child, case (a), pass to the parent, but only
+ // if the direction is forwards. Otherwise set the focus to the
+ // notebook itself. The notebook is always the 'first' control of a
+ // page.
+ if (!isForward)
+ {
+ SetFocus();
+ }
+ else if (parent)
+ {
+ event.SetCurrentFocus(this);
+ parent->HandleWindowEvent(event);
+ }
+ }
+ }
+ }
+
protected:
virtual void UpdateSelectedPage(size_t WXUNUSED(newsel)) override
{
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 0841efcb0..98abb4863 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -593,7 +593,7 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
m_state = EState::Unknown;
init();
// Timers when not fading
- } else if (m_state != EState::NotFading && m_state != EState::FadingOut && get_duration() != 0 && !paused) {
+ } else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_state != EState::ClosePending && m_state != EState::Finished && get_duration() != 0 && !paused) {
int64_t up_time = now - m_notification_start;
if (up_time >= get_duration() * 1000) {
m_state = EState::FadingOut;
@@ -633,6 +633,10 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
//---------------ExportFinishedNotification-----------
void NotificationManager::ExportFinishedNotification::count_spaces()
{
+ if (m_eject_pending)
+ {
+ return PopNotification::count_spaces();
+ }
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
@@ -650,7 +654,10 @@ void NotificationManager::ExportFinishedNotification::count_spaces()
void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
-
+ if (m_eject_pending)
+ {
+ return PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+ }
float x_offset = m_left_indentation;
std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
// Lines are always at least two and m_multiline is always true for ExportFinishedNotification.
@@ -669,7 +676,7 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
ImGui::SetCursorPosY(starting_y + i * shift_y);
imgui.text(line.c_str());
//hyperlink text
- if ( i == 0 ) {
+ if ( i == 0 && !m_eject_pending) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder."));
}
}
@@ -680,7 +687,7 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
void NotificationManager::ExportFinishedNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
PopNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
- if(m_to_removable)
+ if(m_to_removable && ! m_eject_pending)
render_eject_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
}
@@ -725,7 +732,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
- close();
+ on_eject_click();
}
//invisible large button
@@ -736,7 +743,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
- close();
+ on_eject_click();
}
ImGui::PopStyleColor(5);
}
@@ -745,6 +752,14 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
open_folder(m_export_dir_path);
return false;
}
+void NotificationManager::ExportFinishedNotification::on_eject_click()
+{
+ NotificationData data{ get_data().type, get_data().level , 0, _utf8("Ejecting.") };
+ m_eject_pending = true;
+ m_multiline = false;
+ update(data);
+}
+
//------ProgressBar----------------
void NotificationManager::ProgressBarNotification::init()
{
@@ -1071,7 +1086,7 @@ void NotificationManager::UpdatedItemsInfoNotification::render_left_sign(ImGuiWr
imgui.text(text.c_str());
}
-//------SlicingProgressNotificastion
+//------SlicingProgressNotification
void NotificationManager::SlicingProgressNotification::init()
{
if (m_sp_state == SlicingProgressState::SP_PROGRESS) {
@@ -1084,46 +1099,53 @@ void NotificationManager::SlicingProgressNotification::init()
}
}
-void NotificationManager::SlicingProgressNotification::set_progress_state(float percent)
+bool NotificationManager::SlicingProgressNotification::set_progress_state(float percent)
{
if (percent < 0.f)
- set_progress_state(SlicingProgressState::SP_CANCELLED);
+ return true;//set_progress_state(SlicingProgressState::SP_CANCELLED);
else if (percent >= 1.f)
- set_progress_state(SlicingProgressState::SP_COMPLETED);
+ return set_progress_state(SlicingProgressState::SP_COMPLETED);
else
- set_progress_state(SlicingProgressState::SP_PROGRESS, percent);
+ return set_progress_state(SlicingProgressState::SP_PROGRESS, percent);
}
-void NotificationManager::SlicingProgressNotification::set_progress_state(NotificationManager::SlicingProgressNotification::SlicingProgressState state, float percent/* = 0.f*/)
+bool NotificationManager::SlicingProgressNotification::set_progress_state(NotificationManager::SlicingProgressNotification::SlicingProgressState state, float percent/* = 0.f*/)
{
switch (state)
{
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_NO_SLICING:
+ case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_BEGAN:
m_state = EState::Hidden;
set_percentage(-1);
m_has_print_info = false;
set_export_possible(false);
- break;
+ m_sp_state = state;
+ return true;
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_PROGRESS:
+ if ((m_sp_state != SlicingProgressState::SP_BEGAN && m_sp_state != SlicingProgressState::SP_PROGRESS) || percent < m_percentage)
+ return false;
set_percentage(percent);
m_has_cancel_button = true;
- break;
+ m_sp_state = state;
+ return true;
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_CANCELLED:
set_percentage(-1);
m_has_cancel_button = false;
m_has_print_info = false;
set_export_possible(false);
- break;
+ m_sp_state = state;
+ return true;
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_COMPLETED:
set_percentage(1);
m_has_cancel_button = false;
m_has_print_info = false;
- // m_export_possible is important only for PROGRESS state, thus we can reset it here
+ // m_export_possible is important only for SP_PROGRESS state, thus we can reset it here
set_export_possible(false);
- break;
+ m_sp_state = state;
+ return true;
default:
break;
}
- m_sp_state = state;
+ return false;
}
void NotificationManager::SlicingProgressNotification::set_status_text(const std::string& text)
{
@@ -1169,22 +1191,24 @@ void NotificationManager::SlicingProgressNotification::set_print_info(const std:
void NotificationManager::SlicingProgressNotification::set_sidebar_collapsed(bool collapsed)
{
m_sidebar_collapsed = collapsed;
- if (m_sp_state == SlicingProgressState::SP_COMPLETED)
- m_state = EState::Shown;
+ if (m_sp_state == SlicingProgressState::SP_COMPLETED && collapsed)
+ m_state = EState::NotFading;
}
void NotificationManager::SlicingProgressNotification::on_cancel_button()
{
if (m_cancel_callback){
- m_cancel_callback();
+ if (!m_cancel_callback()) {
+ set_progress_state(SlicingProgressState::SP_NO_SLICING);
+ }
}
}
int NotificationManager::SlicingProgressNotification::get_duration()
{
if (m_sp_state == SlicingProgressState::SP_CANCELLED)
- return 10;
+ return 2;
else if (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)
- return 5;
+ return 2;
else
return 0;
}
@@ -1198,7 +1222,7 @@ bool NotificationManager::SlicingProgressNotification::update_state(bool paused
}
void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
- if (m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)) {
+ if (m_sp_state == SlicingProgressState::SP_PROGRESS /*|| (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)*/) {
ProgressBarNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
/* // enable for hypertext during slicing (correct call of export_enabled needed)
if (m_multiline) {
@@ -1233,7 +1257,7 @@ void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper&
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
}
*/
- } else if (m_sp_state == SlicingProgressState::SP_COMPLETED) {
+ } else if (m_sp_state == SlicingProgressState::SP_COMPLETED && m_sidebar_collapsed) {
// "Slicing Finished" on line 1 + hypertext, print info on line
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
@@ -1258,21 +1282,18 @@ void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper&
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
}
}
-void NotificationManager::SlicingProgressNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
+void NotificationManager::SlicingProgressNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
- if (!(m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed))) {
+ if (m_sp_state != SlicingProgressState::SP_PROGRESS) {
return;
}
- //std::string text;
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
- /*
- std::stringstream stream;
- stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "%";
- text = stream.str();
- ImGui::SetCursorPosX(m_left_indentation);
- ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
- imgui.text(text.c_str());
- */
+}
+void NotificationManager::SlicingProgressNotification::render_hypertext(ImGuiWrapper& imgui,const float text_x, const float text_y, const std::string text, bool more)
+{
+ if (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)
+ return;
+ ProgressBarNotification::render_hypertext(imgui, text_x, text_y, text, more);
}
void NotificationManager::SlicingProgressNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
@@ -1358,7 +1379,7 @@ void NotificationManager::ProgressIndicatorNotification::init()
m_state = EState::NotFading;
break;
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_COMPLETED:
- m_state = EState::Shown;
+ m_state = EState::ClosePending;
break;
default:
break;
@@ -1372,7 +1393,7 @@ void NotificationManager::ProgressIndicatorNotification::set_percentage(float pe
m_has_cancel_button = true;
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_REQUEST;
} else if (percent >= 1.0f) {
- m_state = EState::Shown;
+ m_state = EState::FadingOut;
m_progress_state = ProgressIndicatorState::PIS_COMPLETED;
m_has_cancel_button = false;
} else {
@@ -1386,6 +1407,7 @@ bool NotificationManager::ProgressIndicatorNotification::update_state(bool pause
// percentage was changed (and it called schedule_extra_frame), now update must know this needs render
m_next_render = 0;
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_UPDATED;
+ m_current_fade_opacity = 1.0f;
return true;
}
bool ret = ProgressBarNotification::update_state(paused, delta);
@@ -1626,6 +1648,7 @@ void NotificationManager::push_exporting_finished_notification(const std::string
close_notification_of_type(NotificationType::ExportFinished);
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotificationLevel, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
+ set_slicing_progress_hidden();
}
void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
@@ -1681,7 +1704,7 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
}
}
-void NotificationManager::init_slicing_progress_notification(std::function<void()> cancel_callback)
+void NotificationManager::init_slicing_progress_notification(std::function<bool()> cancel_callback)
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingProgress) {
@@ -1698,12 +1721,39 @@ void NotificationManager::init_slicing_progress_notification(std::function<void(
};
push_notification_data(std::make_unique<NotificationManager::SlicingProgressNotification>(data, m_id_provider, m_evt_handler, cancel_callback), 0);
}
+void NotificationManager::set_slicing_progress_began()
+{
+ for (std::unique_ptr<PopNotification> & notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingProgress) {
+ SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
+ spn->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_BEGAN);
+ return;
+ }
+ }
+ // Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
+ wxGetApp().plater()->init_notification_manager();
+}
void NotificationManager::set_slicing_progress_percentage(const std::string& text, float percentage)
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingProgress) {
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
- spn->set_progress_state(percentage);
+ if(spn->set_progress_state(percentage)) {
+ spn->set_status_text(text);
+ wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
+ }
+ return;
+ }
+ }
+ // Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
+ wxGetApp().plater()->init_notification_manager();
+}
+void NotificationManager::set_slicing_progress_canceled(const std::string& text)
+{
+ for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingProgress) {
+ SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
+ spn->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_CANCELLED);
spn->set_status_text(text);
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
return;
@@ -1712,7 +1762,6 @@ void NotificationManager::set_slicing_progress_percentage(const std::string& tex
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
wxGetApp().plater()->init_notification_manager();
}
-
void NotificationManager::set_slicing_progress_hidden()
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
@@ -1770,7 +1819,7 @@ void NotificationManager::init_progress_indicator()
return;
}
}
- NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 2};
+ NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 1};
auto notification = std::make_unique<NotificationManager::ProgressIndicatorNotification>(data, m_id_provider, m_evt_handler);
push_notification_data(std::move(notification), 0);
}
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 76dc9a688..4481ed701 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -188,9 +188,11 @@ public:
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
// slicing progress
- void init_slicing_progress_notification(std::function<void()> cancel_callback);
+ void init_slicing_progress_notification(std::function<bool()> cancel_callback);
+ void set_slicing_progress_began();
// percentage negative = canceled, <0-1) = progress, 1 = completed
void set_slicing_progress_percentage(const std::string& text, float percentage);
+ void set_slicing_progress_canceled(const std::string& text);
// hides slicing progress notification imidietly
void set_slicing_progress_hidden();
// Add a print time estimate to an existing SlicingProgress notification. Set said notification to SP_COMPLETED state.
@@ -492,11 +494,12 @@ private:
enum class SlicingProgressState
{
SP_NO_SLICING, // hidden
+ SP_BEGAN, // still hidden but allows to go to SP_PROGRESS state. This prevents showing progress after slicing was canceled.
SP_PROGRESS, // never fades outs, no close button, has cancel button
SP_CANCELLED, // fades after 10 seconds, simple message
SP_COMPLETED // Has export hyperlink and print info, fades after 20 sec if sidebar is shown, otherwise no fade out
};
- SlicingProgressNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, std::function<void()> callback)
+ SlicingProgressNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, std::function<bool()> callback)
: ProgressBarNotification(n, id_provider, evt_handler)
, m_cancel_callback(callback)
{
@@ -507,12 +510,12 @@ private:
// sets text of notification - call after setting progress state
void set_status_text(const std::string& text);
// sets cancel button callback
- void set_cancel_callback(std::function<void()> callback) { m_cancel_callback = callback; }
+ void set_cancel_callback(std::function<bool()> callback) { m_cancel_callback = callback; }
bool has_cancel_callback() const { return m_cancel_callback != nullptr; }
- // sets SlicingProgressState, negative percent means canceled
- void set_progress_state(float percent);
- // sets SlicingProgressState, percent is used only at progress state.
- void set_progress_state(SlicingProgressState state,float percent = 0.f);
+ // sets SlicingProgressState, negative percent means canceled, returns true if state was set succesfully.
+ bool set_progress_state(float percent);
+ // sets SlicingProgressState, percent is used only at progress state. Returns true if state was set succesfully.
+ bool set_progress_state(SlicingProgressState state,float percent = 0.f);
// sets additional string of print info and puts notification into Completed state.
void set_print_info(const std::string& info);
// sets fading if in Completed state.
@@ -541,11 +544,16 @@ private:
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_close_button(ImGuiWrapper& imgui,
- const float win_size_x, const float win_size_y,
- const float win_pos_x, const float win_pos_y) override;
+ const float win_size_x, const float win_size_y,
+ const float win_pos_x, const float win_pos_y) override;
+ void render_hypertext(ImGuiWrapper& imgui,
+ const float text_x, const float text_y,
+ const std::string text,
+ bool more = false) override ;
void on_cancel_button();
int get_duration() override;
- std::function<void()> m_cancel_callback;
+ // if returns false, process was already canceled
+ std::function<bool()> m_cancel_callback;
SlicingProgressState m_sp_state { SlicingProgressState::SP_PROGRESS };
bool m_has_print_info { false };
std::string m_print_info;
@@ -625,8 +633,10 @@ private:
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
{ m_minimize_b_visible = false; }
bool on_text_click() override;
+ void on_eject_click();
// local time of last hover for showing tooltip
long m_hover_time { 0 };
+ bool m_eject_pending { false };
};
class UpdatedItemsInfoNotification : public PopNotification
diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp
index 928249e5b..746a99a2f 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.cpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.cpp
@@ -37,7 +37,7 @@ void ObjectDataViewModelNode::init_container()
static constexpr char LayerRootIcon[] = "edit_layers_all";
static constexpr char LayerIcon[] = "edit_layers_some";
static constexpr char WarningIcon[] = "exclamation";
-static constexpr char InfoIcon[] = "objlist_info";
+static constexpr char WarningManifoldIcon[] = "exclamation_manifold";
struct InfoItemAtributes {
std::string name;
@@ -58,13 +58,15 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* pare
Slic3r::ModelVolumeType type,
const wxBitmap& bmp,
const wxString& extruder,
- const int idx/* = -1*/) :
+ const int idx/* = -1*/,
+ const std::string& warning_icon_name /*= std::string*/) :
m_parent(parent),
m_name(sub_obj_name),
m_type(itVolume),
m_volume_type(type),
m_idx(idx),
- m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
+ m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""),
+ m_warning_icon_name(warning_icon_name)
{
m_bmp = bmp;
set_action_and_extruder_icons();
@@ -171,6 +173,13 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png");
}
+void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name)
+{
+ m_warning_icon_name = warning_icon_name;
+ if (warning_icon_name.empty())
+ m_bmp = m_empty_bmp;
+}
+
void ObjectDataViewModelNode::update_settings_digest_bitmaps()
{
m_bmp = m_empty_bmp;
@@ -317,6 +326,8 @@ ObjectDataViewModel::ObjectDataViewModel()
m_volume_bmps = MenuFactory::get_volume_bitmaps();
m_warning_bmp = create_scaled_bitmap(WarningIcon);
+ m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
+
for (auto item : INFO_ITEMS)
m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name);
}
@@ -329,15 +340,19 @@ ObjectDataViewModel::~ObjectDataViewModel()
m_bitmap_cache = nullptr;
}
+wxBitmap& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name)
+{
+ return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp;
+}
+
wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
const int extruder,
- const bool has_errors/* = false*/)
+ const std::string& warning_icon_name/* = std::string()*/ )
{
- const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
+ const wxString extruder_str = extruder == 0 ? _L("default") : wxString::Format("%d", extruder);
auto root = new ObjectDataViewModelNode(name, extruder_str);
- // Add error icon if detected auto-repaire
- if (has_errors)
- root->m_bmp = m_warning_bmp;
+ // Add warning icon if detected auto-repaire
+ root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
m_objects.push_back(root);
// notify control
@@ -351,7 +366,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
- const bool has_errors/* = false*/,
+ const std::string& warning_icon_name/* = std::string()*/,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
@@ -365,12 +380,10 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
if (insert_position < 0)
insert_position = get_root_idx(root, itInstanceRoot);
- const bool obj_errors = root->m_bmp.IsOk();
-
if (create_frst_child && root->m_volumes_cnt == 0)
{
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
- const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, obj_errors), extruder_str, 0);
+ const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, root->m_warning_icon_name), extruder_str, 0, root->m_warning_icon_name);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
@@ -381,12 +394,13 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
if (insert_position >= 0) insert_position++;
}
- const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt);
+ const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, warning_icon_name), extruder_str, root->m_volumes_cnt, warning_icon_name);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// if part with errors is added, but object wasn't marked, then mark it
- if (!obj_errors && has_errors)
- root->SetBitmap(m_warning_bmp);
+ if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name &&
+ (root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) )
+ root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
// notify control
const wxDataViewItem child((void*)node);
@@ -1666,6 +1680,7 @@ void ObjectDataViewModel::Rescale()
{
m_volume_bmps = MenuFactory::get_volume_bitmaps();
m_warning_bmp = create_scaled_bitmap(WarningIcon);
+ m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
wxDataViewItemArray all_items;
GetAllChildren(wxDataViewItem(0), all_items);
@@ -1681,10 +1696,10 @@ void ObjectDataViewModel::Rescale()
switch (node->m_type)
{
case itObject:
- if (node->m_bmp.IsOk()) node->m_bmp = m_warning_bmp;
+ if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name);
break;
case itVolume:
- node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight());
+ node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name);
break;
case itLayerRoot:
node->m_bmp = create_scaled_bitmap(LayerRootIcon);
@@ -1698,19 +1713,19 @@ void ObjectDataViewModel::Rescale()
}
}
-wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/)
+wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/)
{
- if (!is_marked)
+ if (warning_icon_name.empty())
return m_volume_bmps[static_cast<int>(vol_type)];
- std::string scaled_bitmap_name = "warning" + std::to_string(static_cast<int>(vol_type));
- scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit());
+ std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast<int>(vol_type));
+ scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm");
wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name);
if (bmp == nullptr) {
std::vector<wxBitmap> bmps;
- bmps.emplace_back(m_warning_bmp);
+ bmps.emplace_back(GetWarningBitmap(warning_icon_name));
bmps.emplace_back(m_volume_bmps[static_cast<int>(vol_type)]);
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
@@ -1719,20 +1734,20 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty
return *bmp;
}
-void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item)
+void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->GetType() & itObject) {
- node->SetBitmap(m_warning_bmp);
+ node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
return;
}
if (node->GetType() & itVolume) {
- node->SetBitmap(GetVolumeIcon(node->GetVolumeType(), true));
- node->GetParent()->SetBitmap(m_warning_bmp);
+ node->SetWarningBitmap(GetVolumeIcon(node->GetVolumeType(), warning_icon_name), warning_icon_name);
+ node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
return;
}
}
@@ -1748,11 +1763,11 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo
return;
if (node->GetType() & itVolume) {
- node->SetBitmap(m_volume_bmps[static_cast<int>(node->volume_type())]);
+ node->SetWarningBitmap(m_volume_bmps[static_cast<int>(node->volume_type())], "");
return;
}
- node->SetBitmap(wxNullBitmap);
+ node->SetWarningBitmap(wxNullBitmap, "");
if (unmark_object)
{
wxDataViewItemArray children;
diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp
index f65f829f4..95e53babd 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.hpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.hpp
@@ -78,6 +78,7 @@ class ObjectDataViewModelNode
wxBitmap m_action_icon;
PrintIndicator m_printable {piUndef};
wxBitmap m_printable_icon;
+ std::string m_warning_icon_name{ "" };
std::string m_action_icon_name = "";
ModelVolumeType m_volume_type;
@@ -100,7 +101,8 @@ public:
Slic3r::ModelVolumeType type,
const wxBitmap& bmp,
const wxString& extruder,
- const int idx = -1);
+ const int idx = -1,
+ const std::string& warning_icon_name = std::string());
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
@@ -179,6 +181,7 @@ public:
void SetVolumeType(ModelVolumeType type) { m_volume_type = type; }
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
+ void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; }
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
@@ -225,6 +228,8 @@ public:
void set_extruder_icon();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
+ // Set warning icon for node
+ void set_warning_icon(const std::string& warning_icon);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
@@ -253,7 +258,9 @@ class ObjectDataViewModel :public wxDataViewModel
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap> m_volume_bmps;
std::map<InfoItemType, wxBitmap> m_info_bmps;
+ wxBitmap m_empty_bmp;
wxBitmap m_warning_bmp;
+ wxBitmap m_warning_manifold_bmp;
wxDataViewCtrl* m_ctrl { nullptr };
@@ -263,11 +270,11 @@ public:
wxDataViewItem Add( const wxString &name,
const int extruder,
- const bool has_errors = false);
+ const std::string& warning_icon_name = std::string());
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
- const bool has_errors = false,
+ const std::string& warning_icon_name = std::string(),
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
@@ -378,8 +385,8 @@ public:
void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
- const bool is_marked = false);
- void AddWarningIcon(const wxDataViewItem& item);
+ const std::string& warning_icon_name = std::string());
+ void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
@@ -392,6 +399,8 @@ private:
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
void AddAllChildren(const wxDataViewItem& parent);
+
+ wxBitmap& GetWarningBitmap(const std::string& warning_icon_name);
};
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index 519abab43..22ced12e4 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -532,9 +532,9 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
const std::vector<VendorProfile::PrinterModel>& models = preset->vendor->models;
auto it = std::find_if(models.begin(), models.end(),
[model_id](const VendorProfile::PrinterModel& model) { return model.id == model_id; });
- if (it != models.end() && it->family == "MK3")
+ if (it != models.end() && (it->family == "MK3" || it->family == "MINI"))
continue;
- } else if (!preset->vendor && model_id.rfind("MK3", 0) == 0) {
+ } else if (!preset->vendor && (boost::ends_with(model_id, "MK3") || boost::ends_with(model_id, "MINI"))) {
continue;
}
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 7d344c389..87b83ec24 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -67,8 +67,8 @@
#include "Jobs/FillBedJob.hpp"
#include "Jobs/RotoptimizeJob.hpp"
#include "Jobs/SLAImportJob.hpp"
+#include "Jobs/NotificationProgressIndicator.hpp"
#include "BackgroundSlicingProcess.hpp"
-#include "ProgressStatusBar.hpp"
#include "PrintHostDialogs.hpp"
#include "ConfigWizard.hpp"
#include "../Utils/ASCIIFolding.hpp"
@@ -125,6 +125,7 @@ wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
class ObjectInfo : public wxStaticBoxSizer
{
+ std::string m_warning_icon_name{ "exclamation" };
public:
ObjectInfo(wxWindow *parent);
@@ -142,6 +143,7 @@ public:
bool showing_manifold_warning_icon;
void show_sizer(bool show);
void msw_rescale();
+ void update_warning_icon(const std::string& warning_icon_name);
};
ObjectInfo::ObjectInfo(wxWindow *parent) :
@@ -175,7 +177,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
info_manifold_text->SetFont(wxGetApp().small_font());
info_manifold = new wxStaticText(parent, wxID_ANY, "");
info_manifold->SetFont(wxGetApp().small_font());
- manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("exclamation"));
+ manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name));
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
sizer_manifold->Add(info_manifold_text, 0);
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
@@ -194,7 +196,15 @@ void ObjectInfo::show_sizer(bool show)
void ObjectInfo::msw_rescale()
{
- manifold_warning_icon->SetBitmap(create_scaled_bitmap("exclamation"));
+ manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
+}
+
+void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
+{
+ if (warning_icon_name.empty())
+ return;
+ m_warning_icon_name = warning_icon_name;
+ manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
}
enum SlicedInfoIdx
@@ -1129,32 +1139,19 @@ void Sidebar::show_info_sizer()
p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0)*koef, size(1)*koef, size(2)*koef));
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
- const auto& stats = model_object->get_object_stl_stats();//model_object->volumes.front()->mesh.stl.stats;
+ const auto& stats = model_object->get_object_stl_stats();
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume*pow(koef,3)));
p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts),
static_cast<int>(model_object->facets_count()), stats.number_of_parts));
- int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
- stats.facets_added + stats.facets_reversed + stats.backwards_edges;
- if (errors > 0) {
- wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
- p->object_info->info_manifold->SetLabel(tooltip);
-
- tooltip += ":\n";
- if (stats.degenerate_facets > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + ", ";
- if (stats.edges_fixed > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + ", ";
- if (stats.facets_removed > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + ", ";
- if (stats.facets_added > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d facet added", "%1$d facets added", stats.facets_added), stats.facets_added) + ", ";
- if (stats.facets_reversed > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + ", ";
- if (stats.backwards_edges > 0)
- tooltip += format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + ", ";
- tooltip.RemoveLast(2);//remove last coma
+ if (stats.repaired()) {
+ int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_reversed + stats.backwards_edges;
+ p->object_info->info_manifold->SetLabel(format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors));
+
+ auto mesh_errors = obj_list()->get_mesh_errors(true);
+ wxString tooltip = mesh_errors.first;
+ p->object_info->update_warning_icon(mesh_errors.second);
p->object_info->showing_manifold_warning_icon = true;
p->object_info->info_manifold->SetToolTip(tooltip);
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
@@ -1501,7 +1498,7 @@ struct Plater::priv
GLToolbar view_toolbar;
GLToolbar collapse_toolbar;
Preview *preview;
- std::shared_ptr<NotificationManager> notification_manager;
+ std::unique_ptr<NotificationManager> notification_manager;
ProjectDirtyStateManager dirty_state;
@@ -1516,39 +1513,42 @@ struct Plater::priv
{
priv *m;
size_t m_arrange_id, m_fill_bed_id, m_rotoptimize_id, m_sla_import_id;
+ std::shared_ptr<NotificationProgressIndicator> m_pri;
void before_start() override { m->background_process.stop(); }
public:
- Jobs(priv *_m) : m(_m)
+ Jobs(priv *_m) :
+ m(_m),
+ m_pri{std::make_shared<NotificationProgressIndicator>(m->notification_manager.get())}
{
- m_arrange_id = add_job(std::make_unique<ArrangeJob>(m->notification_manager, m->q));
- m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m->notification_manager, m->q));
- m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m->notification_manager, m->q));
- m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m->notification_manager, m->q));
+ m_arrange_id = add_job(std::make_unique<ArrangeJob>(m_pri, m->q));
+ m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m_pri, m->q));
+ m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m_pri, m->q));
+ m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m_pri, m->q));
}
void arrange()
{
- m->take_snapshot(_(L("Arrange")));
+ m->take_snapshot(_L("Arrange"));
start(m_arrange_id);
}
void fill_bed()
{
- m->take_snapshot(_(L("Fill bed")));
+ m->take_snapshot(_L("Fill bed"));
start(m_fill_bed_id);
}
void optimize_rotation()
{
- m->take_snapshot(_(L("Optimize Rotation")));
+ m->take_snapshot(_L("Optimize Rotation"));
start(m_rotoptimize_id);
}
void import_sla_arch()
{
- m->take_snapshot(_(L("Import SLA archive")));
+ m->take_snapshot(_L("Import SLA archive"));
start(m_sla_import_id);
}
@@ -1575,16 +1575,22 @@ struct Plater::priv
bool is_project_dirty() const { return dirty_state.is_dirty(); }
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
- int save_project_if_dirty() {
+ int save_project_if_dirty(const wxString& reason) {
int res = wxID_NO;
if (dirty_state.is_dirty()) {
MainFrame* mainframe = wxGetApp().mainframe;
if (mainframe->can_save_as()) {
- //wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
- MessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
- res = dlg.ShowModal();
+ wxString suggested_project_name;
+ wxString project_name = suggested_project_name = get_project_filename(".3mf");
+ if (suggested_project_name.IsEmpty()) {
+ fs::path output_file = get_export_file_path(FT_3MF);
+ suggested_project_name = output_file.empty() ? _L("Untitled") : from_u8(output_file.stem().string());
+ }
+ res = MessageDialog(mainframe, reason + "\n" + format_wxstr(_L("Do you want to save the changes to \"%1%\"?"), suggested_project_name),
+ wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL).ShowModal();
if (res == wxID_YES)
- mainframe->save_project_as(wxGetApp().plater()->get_project_filename());
+ if (!mainframe->save_project_as(project_name))
+ res = wxID_CANCEL;
}
}
return res;
@@ -1644,6 +1650,7 @@ struct Plater::priv
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
+ fs::path get_export_file_path(GUI::FileType file_type);
wxString get_export_file(GUI::FileType file_type);
const Selection& get_selection() const;
@@ -1670,8 +1677,9 @@ struct Plater::priv
void enter_gizmos_stack();
void leave_gizmos_stack();
- void take_snapshot(const std::string& snapshot_name);
- void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); }
+ void take_snapshot(const std::string& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action);
+ void take_snapshot(const wxString& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action)
+ { this->take_snapshot(std::string(snapshot_name.ToUTF8().data()), snapshot_type); }
int get_active_snapshot_index();
void undo();
@@ -1717,9 +1725,11 @@ struct Plater::priv
}
void export_gcode(fs::path output_path, bool output_path_on_removable_media, PrintHostJob upload_job);
void reload_from_disk();
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ bool replace_volume_with_stl(int object_idx, int volume_idx, const fs::path& new_path, const wxString& snapshot = "");
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
void replace_with_stl();
void reload_all_from_disk();
- void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void create_simplify_notification(const std::vector<size_t>& obj_ids);
void set_current_panel(wxPanel* panel);
@@ -1847,7 +1857,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers"
}))
, sidebar(new Sidebar(q))
- , notification_manager(std::make_shared<NotificationManager>(q))
+ , notification_manager(std::make_unique<NotificationManager>(q))
, m_ui_jobs(this)
, delayed_scene_refresh(false)
, view_toolbar(GLToolbar::Radio, "View")
@@ -1911,6 +1921,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this);
sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); });
sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
+ // jump to found option from SearchDialog
+ q->Bind(wxCUSTOMEVT_JUMP_TO_OPTION, [this](wxCommandEvent& evt) { sidebar->jump_to_option(evt.GetInt()); });
}
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
@@ -2051,7 +2063,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
}
// Initialize the Undo / Redo stack with a first snapshot.
- this->take_snapshot(_L("New Project"));
+ this->take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) {
BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event.";
@@ -2427,7 +2439,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
for (ModelObject* model_object : model.objects) {
if (!type_3mf && !type_zip_amf)
model_object->center_around_origin(false);
- model_object->ensure_on_bed(is_project_file);
+ model_object->ensure_on_bed(is_project_file || type_3mf || type_zip_amf);
}
// check multi-part object adding for the SLA-printing
@@ -2444,7 +2456,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (one_by_one) {
if (type_3mf && !is_project_file)
model.center_instances_around_point(bed_shape_bb().center());
- auto loaded_idxs = load_model_objects(model.objects, is_project_file);
+ auto loaded_idxs = load_model_objects(model.objects, is_project_file || type_3mf || type_zip_amf);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
} else {
// This must be an .stl or .obj file, which may contain a maximum of one volume.
@@ -2542,16 +2554,14 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
if (max_ratio > 10000) {
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
// so scale down the mesh
- double inv = 1. / max_ratio;
- object->scale_mesh_after_creation(inv * Vec3d::Ones());
+ object->scale_mesh_after_creation(1. / max_ratio);
object->origin_translation = Vec3d::Zero();
object->center_around_origin();
scaled_down = true;
break;
}
else if (max_ratio > 5) {
- const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones();
- instance->set_scaling_factor(inverse.cwiseProduct(instance->get_scaling_factor()));
+ instance->set_scaling_factor(instance->get_scaling_factor() / max_ratio);
scaled_down = true;
}
}
@@ -2599,22 +2609,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
return obj_idxs;
}
-wxString Plater::priv::get_export_file(GUI::FileType file_type)
+fs::path Plater::priv::get_export_file_path(GUI::FileType file_type)
{
- wxString wildcard;
- switch (file_type) {
- case FT_STL:
- case FT_AMF:
- case FT_3MF:
- case FT_GCODE:
- case FT_OBJ:
- wildcard = file_wildcards(file_type);
- break;
- default:
- wildcard = file_wildcards(FT_MODEL);
- break;
- }
-
// Update printbility state of each of the ModelInstances.
this->update_print_volume_state();
@@ -2639,7 +2635,31 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
if (output_file.empty() && !model.objects.empty())
// Find the file name of the first object.
output_file = this->model.objects[0]->get_export_filename();
+
+ if (output_file.empty())
+ // Use _L("Untitled") name
+ output_file = into_path(_L("Untitled"));
}
+ return output_file;
+}
+
+wxString Plater::priv::get_export_file(GUI::FileType file_type)
+{
+ wxString wildcard;
+ switch (file_type) {
+ case FT_STL:
+ case FT_AMF:
+ case FT_3MF:
+ case FT_GCODE:
+ case FT_OBJ:
+ wildcard = file_wildcards(file_type);
+ break;
+ default:
+ wildcard = file_wildcards(FT_MODEL);
+ break;
+ }
+
+ fs::path output_file = get_export_file_path(file_type);
wxString dlg_title;
switch (file_type) {
@@ -2799,7 +2819,7 @@ void Plater::priv::delete_all_objects_from_model()
void Plater::priv::reset()
{
- Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
+ Plater::TakeSnapshot snapshot(q, _L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator);
clear_warnings();
@@ -3085,6 +3105,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
// (return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
// this->statusbar()->set_status_text(_L("Ready to slice"));
+ if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
+ (return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
+ notification_manager->set_slicing_progress_hidden();
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
@@ -3194,6 +3217,76 @@ void Plater::priv::update_sla_scene()
this->update_restart_background_process(true, true);
}
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const fs::path& new_path, const wxString& snapshot)
+{
+ const std::string path = new_path.string();
+ wxBusyCursor wait;
+ wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
+
+ Model new_model;
+ try {
+ new_model = Model::read_from_file(path, nullptr, nullptr, Model::LoadAttribute::AddDefaultInstances);
+ for (ModelObject* model_object : new_model.objects) {
+ model_object->center_around_origin();
+ model_object->ensure_on_bed();
+ }
+ }
+ catch (std::exception&) {
+ // error while loading
+ return false;
+ }
+
+ if (new_model.objects.size() > 1 || new_model.objects.front()->volumes.size() > 1) {
+ MessageDialog dlg(q, _L("Unable to replace with more than one volume"), _L("Error during replace"), wxOK | wxOK_DEFAULT | wxICON_WARNING);
+ dlg.ShowModal();
+ return false;
+ }
+
+ if (!snapshot.empty())
+ q->take_snapshot(snapshot);
+
+ ModelObject* old_model_object = model.objects[object_idx];
+ ModelVolume* old_volume = old_model_object->volumes[volume_idx];
+
+ bool sinking = old_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
+
+ ModelObject* new_model_object = new_model.objects.front();
+ old_model_object->add_volume(*new_model_object->volumes.front());
+ ModelVolume* new_volume = old_model_object->volumes.back();
+ new_volume->set_new_unique_id();
+ new_volume->config.apply(old_volume->config);
+ new_volume->set_type(old_volume->type());
+ new_volume->set_material_id(old_volume->material_id());
+ new_volume->set_transformation(old_volume->get_transformation());
+ new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
+ assert(!old_volume->source.is_converted_from_inches || !old_volume->source.is_converted_from_meters);
+ if (old_volume->source.is_converted_from_inches)
+ new_volume->convert_from_imperial_units();
+ else if (old_volume->source.is_converted_from_meters)
+ new_volume->convert_from_meters();
+ new_volume->supported_facets.assign(old_volume->supported_facets);
+ new_volume->seam_facets.assign(old_volume->seam_facets);
+ new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
+ std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back());
+ old_model_object->delete_volume(old_model_object->volumes.size() - 1);
+ if (!sinking)
+ old_model_object->ensure_on_bed();
+ old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
+
+ // if object has just one volume, rename object too
+ if (old_model_object->volumes.size() == 1)
+ old_model_object->name = old_model_object->volumes.front()->name;
+
+ // update new name in ObjectList
+ sidebar->obj_list()->update_name_in_list(object_idx, volume_idx);
+
+ sla::reproject_points_and_holes(old_model_object);
+
+ return true;
+}
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+
void Plater::priv::replace_with_stl()
{
if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::EType::Undefined))
@@ -3230,8 +3323,10 @@ void Plater::priv::replace_with_stl()
return;
}
- wxString fail_replace;
-
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ if (!replace_volume_with_stl(object_idx, volume_idx, out_path, _L("Replace with STL")))
+ return;
+#else
const auto& path = out_path.string();
wxBusyCursor wait;
wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
@@ -3292,7 +3387,8 @@ void Plater::priv::replace_with_stl()
// update new name in ObjectList
sidebar->obj_list()->update_name_in_list(object_idx, volume_idx);
- sla::reproject_points_and_holes(old_model_object);
+ sla::reproject_points_and_holes(old_model_object);
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
// update 3D scene
update();
@@ -3341,6 +3437,9 @@ void Plater::priv::reload_from_disk()
// collects paths of files to load
std::vector<fs::path> input_paths;
std::vector<fs::path> missing_input_paths;
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ std::vector<fs::path> replace_paths;
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
for (const SelectedVolume& v : selected_volumes) {
const ModelObject* object = model.objects[v.object_idx];
const ModelVolume* volume = object->volumes[v.volume_idx];
@@ -3348,8 +3447,28 @@ void Plater::priv::reload_from_disk()
if (!volume->source.input_file.empty()) {
if (fs::exists(volume->source.input_file))
input_paths.push_back(volume->source.input_file);
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ else {
+ // searches the source in the same folder containing the object
+ bool found = false;
+ if (!object->input_file.empty()) {
+ fs::path object_path = fs::path(object->input_file).remove_filename();
+ if (!object_path.empty()) {
+ object_path /= fs::path(volume->source.input_file).filename();
+ const std::string source_input_file = object_path.string();
+ if (fs::exists(source_input_file)) {
+ input_paths.push_back(source_input_file);
+ found = true;
+ }
+ }
+ }
+ if (!found)
+ missing_input_paths.push_back(volume->source.input_file);
+ }
+#else
else
missing_input_paths.push_back(volume->source.input_file);
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
}
else if (!object->input_file.empty() && volume->is_model_part() && !volume->name.empty() && !volume->source.is_from_builtin_objects)
missing_input_paths.push_back(volume->name);
@@ -3392,17 +3511,32 @@ void Plater::priv::reload_from_disk()
}
}
else {
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ wxString message = _L("The selected file") + " (" + from_u8(sel_filename) + ") " +
+ _L("differs from the original file") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to replace it") + " ?";
+ //wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
+ MessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
+ if (dlg.ShowModal() == wxID_YES)
+ replace_paths.push_back(sel_filename_path);
+ missing_input_paths.pop_back();
+#else
wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?";
//wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
MessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() != wxID_YES)
return;
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
}
}
std::sort(input_paths.begin(), input_paths.end());
input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end());
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ std::sort(replace_paths.begin(), replace_paths.end());
+ replace_paths.erase(std::unique(replace_paths.begin(), replace_paths.end()), replace_paths.end());
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+
std::vector<wxString> fail_list;
// load one file at a time
@@ -3497,6 +3631,20 @@ void Plater::priv::reload_from_disk()
}
}
+#if ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+ for (size_t i = 0; i < replace_paths.size(); ++i) {
+ const auto& path = replace_paths[i].string();
+ for (const SelectedVolume& sel_v : selected_volumes) {
+ ModelObject* old_model_object = model.objects[sel_v.object_idx];
+ ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
+ bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
+ if (!replace_volume_with_stl(sel_v.object_idx, sel_v.volume_idx, path, "")) {
+ fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
+ }
+ }
+ }
+#endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
+
if (!fail_list.empty()) {
wxString message = _L("Unable to reload:") + "\n";
for (const wxString& s : fail_list) {
@@ -3536,27 +3684,6 @@ void Plater::priv::reload_all_from_disk()
}
}
-void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/)
-{
- if (obj_idx < 0)
- return;
-
- // Do not fix anything when a gizmo is open. There might be issues with updates
- // and what is worse, the snapshot time would refer to the internal stack.
- if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
- return;
-
- // size_t snapshot_time = undo_redo_stack().active_snapshot_time();
- Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
-
- q->clear_before_change_mesh(obj_idx);
- ModelObject* mo = model.objects[obj_idx];
- fix_model_by_win10_sdk_gui(*mo, vol_idx);
- q->changed_mesh(obj_idx);
- // workaround to fix the issue, when PrusaSlicer lose a focus after model fixing
- q->SetFocus();
-}
-
void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_ids) {
const uint32_t triangles_to_suggest_simplify = 1000000;
@@ -3646,10 +3773,10 @@ void Plater::priv::set_current_panel(wxPanel* panel)
if (view3D->is_reload_delayed()) {
// Delayed loading of the 3D scene.
- if (this->printer_technology == ptSLA) {
+ if (printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
- this->update_restart_background_process(true, false);
+ update_restart_background_process(true, false);
} else
view3D->reload_scene(true);
}
@@ -3672,8 +3799,12 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this->background_process.is_export_scheduled();
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
- if (!model.objects.empty() && !export_in_progress && model_fits)
- this->q->reslice();
+ if (!model.objects.empty() && !export_in_progress && model_fits) {
+#if ENABLE_SEAMS_USING_MODELS
+ preview->get_canvas3d()->init_gcode_viewer();
+#endif // ENABLE_SEAMS_USING_MODELS
+ q->reslice();
+ }
// keeps current gcode preview, if any
preview->reload_print(true);
@@ -3814,20 +3945,16 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
{
- switch (this->printer_technology) {
- case ptFFF:
- this->update_fff_scene();
- break;
- case ptSLA:
- if (view3D->is_dragging())
- delayed_scene_refresh = true;
+ if (view3D->is_dragging()) // updating scene now would interfere with the gizmo dragging
+ delayed_scene_refresh = true;
+ else {
+ if (this->printer_technology == ptFFF)
+ this->update_fff_scene();
else
this->update_sla_scene();
- break;
- default: break;
}
-
}
+
void Plater::priv::on_export_began(wxCommandEvent& evt)
{
if (show_warning_dialog)
@@ -3838,6 +3965,7 @@ void Plater::priv::on_slicing_began()
clear_warnings();
notification_manager->close_notification_of_type(NotificationType::SignDetected);
notification_manager->close_notification_of_type(NotificationType::ExportFinished);
+ notification_manager->set_slicing_progress_began();
}
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
{
@@ -3913,12 +4041,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
if (evt.error()) {
std::pair<std::string, bool> message = evt.format_error_message();
if (evt.critical_error()) {
- if (q->m_tracking_popup_menu)
+ if (q->m_tracking_popup_menu) {
// We don't want to pop-up a message box when tracking a pop-up menu.
// We postpone the error message instead.
q->m_tracking_popup_menu_error_message = message.first;
- else
+ } else {
show_error(q, message.first, message.second);
+ notification_manager->set_slicing_progress_hidden();
+ }
} else
notification_manager->push_slicing_error_notification(message.first);
// this->statusbar()->set_status_text(from_u8(message.first));
@@ -3933,7 +4063,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
}
if (evt.cancelled()) {
// this->statusbar()->set_status_text(_L("Cancelled"));
- this->notification_manager->set_slicing_progress_percentage(_utf8("Slicing Cancelled."), -1);
+ this->notification_manager->set_slicing_progress_canceled(_utf8("Slicing Cancelled."));
}
this->sidebar->show_sliced_info_sizer(evt.success());
@@ -3944,17 +4074,13 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
this->object_list_changed();
// refresh preview
- switch (this->printer_technology) {
- case ptFFF:
- this->update_fff_scene();
- break;
- case ptSLA:
- if (view3D->is_dragging())
- delayed_scene_refresh = true;
+ if (view3D->is_dragging()) // updating scene now would interfere with the gizmo dragging
+ delayed_scene_refresh = true;
+ else {
+ if (this->printer_technology == ptFFF)
+ this->update_fff_scene();
else
this->update_sla_scene();
- break;
- default: break;
}
if (evt.cancelled()) {
@@ -4040,7 +4166,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
const bool is_some_full_instances = get_selection().is_single_full_instance() ||
get_selection().is_single_full_object() ||
get_selection().is_multiple_full_instance();
- menu = is_some_full_instances ? menus.object_menu() : menus.part_menu();
+ menu = is_some_full_instances ? menus.object_menu() :
+ get_selection().is_single_volume() ? menus.part_menu() : menus.multi_selection_menu();
}
}
@@ -4145,7 +4272,10 @@ void Plater::priv::init_notification_manager()
notification_manager->init();
auto cancel_callback = [this]() {
+ if (this->background_process.idle())
+ return false;
this->background_process.stop();
+ return true;
};
notification_manager->init_slicing_progress_notification(cancel_callback);
notification_manager->set_fff(printer_technology == ptFFF);
@@ -4393,11 +4523,22 @@ bool Plater::priv::can_delete_all() const
bool Plater::priv::can_fix_through_netfabb() const
{
- int obj_idx = get_selected_object_idx();
- if (obj_idx < 0)
+ std::vector<int> obj_idxs, vol_idxs;
+ sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
+
+ if (vol_idxs.empty()) {
+ for (auto obj_idx : obj_idxs)
+ if (model.objects[obj_idx]->get_mesh_errors_count() > 0)
+ return true;
return false;
+ }
+
+ int obj_idx = obj_idxs.front();
+ for (auto vol_idx : vol_idxs)
+ if (model.objects[obj_idx]->get_mesh_errors_count(vol_idx) > 0)
+ return true;
- return model.objects[obj_idx]->get_mesh_errors_count() > 0;
+ return false;
}
@@ -4518,12 +4659,13 @@ int Plater::priv::get_active_snapshot_index()
return it - ss_stack.begin();
}
-void Plater::priv::take_snapshot(const std::string& snapshot_name)
+void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRedo::SnapshotType snapshot_type)
{
if (m_prevent_snapshots > 0)
return;
assert(m_prevent_snapshots >= 0);
UndoRedo::SnapshotData snapshot_data;
+ snapshot_data.snapshot_type = snapshot_type;
snapshot_data.printer_technology = this->printer_technology;
if (this->view3D->is_layers_editing_enabled())
snapshot_data.flags |= UndoRedo::SnapshotData::VARIABLE_LAYER_EDITING_ACTIVE;
@@ -4595,8 +4737,10 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
if (printer_technology_changed) {
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
- if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L(
- "%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
+ if (!wxGetApp().check_and_save_current_preset_changes(_L("Undo / Redo is processing"),
+// format_wxstr(_L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
+ format_wxstr(_L("Switching the printer technology from %1% to %2%.\n"
+ "Some %1% presets were modified, which will be lost after switching the printer technology."), s_pt =="FFF" ? "SLA" : "FFF", s_pt), false))
// Don't switch the profiles.
return;
}
@@ -4774,7 +4918,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
-int Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
+int Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); }
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }
void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); }
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
@@ -4791,11 +4935,23 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
void Plater::new_project()
{
- if (p->save_project_if_dirty() == wxID_CANCEL)
+ if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL)
return;
+ else {
+ wxString header = _L("Creating a new project while some presets are modified.") + "\n" +
+ (saved_project == wxID_YES ? _L("You can keep presets modifications to the new project or discard them") :
+ _L("You can keep presets modifications to the new project, discard them or save changes as new presets.\n"
+ "Note, if changes will be saved than new project wouldn't keep them"));
+ using ab = UnsavedChangesDialog::ActionButtons;
+ int act_buttons = ab::KEEP;
+ if (saved_project == wxID_NO)
+ act_buttons |= ab::SAVE;
+ if (!wxGetApp().check_and_keep_current_preset_changes(_L("New Project is creating"), header, act_buttons))
+ return;
+ }
p->select_view_3D("3D");
- take_snapshot(_L("New Project"));
+ take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
Plater::SuppressSnapshots suppress(this);
reset();
reset_project_dirty_initial_presets();
@@ -4804,7 +4960,7 @@ void Plater::new_project()
void Plater::load_project()
{
- if (p->save_project_if_dirty() == wxID_CANCEL)
+ if (!wxGetApp().can_load_project())
return;
// Ask user for a project file name.
@@ -4820,7 +4976,7 @@ void Plater::load_project(const wxString& filename)
return;
// Take the Undo / Redo snapshot.
- Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()));
+ Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()), UndoRedo::SnapshotType::ProjectSeparator);
p->reset();
@@ -5100,7 +5256,8 @@ bool Plater::load_files(const wxArrayString& filenames)
switch (load_type) {
case LoadType::OpenProject: {
- load_project(from_path(*it));
+ if (wxGetApp().can_load_project())
+ load_project(from_path(*it));
break;
}
case LoadType::LoadGeometry: {
@@ -5464,11 +5621,9 @@ void Plater::export_stl(bool extended, bool selection_only)
for (const ModelVolume *v : mo->volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
- vol_mesh.repair();
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
- mesh.repair();
if (instances) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();
@@ -5478,7 +5633,6 @@ void Plater::export_stl(bool extended, bool selection_only)
mesh.merge(m);
}
}
- mesh.repair();
return mesh;
};
@@ -5838,6 +5992,8 @@ void Plater::eject_drive()
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
+void Plater::take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); }
+void Plater::take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); }
void Plater::suppress_snapshots() { p->suppress_snapshots(); }
void Plater::allow_snapshots() { p->allow_snapshots(); }
void Plater::undo() { p->undo(); }
@@ -6105,7 +6261,6 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_gcode_viewer() && result != nullptr) {
for (const CustomGCode::Item& code : result->custom_gcode_per_print_z) {
if (code.type == CustomGCode::ColorChange)
@@ -6113,14 +6268,11 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
}
}
else {
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes) {
if (code.type == CustomGCode::ColorChange)
colors.emplace_back(code.color);
}
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
return colors;
}
@@ -6229,6 +6381,7 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
p->sidebar->get_searcher().set_printer_technology(printer_technology);
p->notification_manager->set_fff(printer_technology == ptFFF);
+ p->notification_manager->set_slicing_progress_hidden();
return ret;
}
@@ -6344,7 +6497,6 @@ void Plater::suppress_background_process(const bool stop_background_process)
this->p->suppressed_backround_processing_update = true;
}
-void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
void Plater::mirror(Axis axis) { p->mirror(axis); }
void Plater::split_object() { p->split_object(); }
void Plater::split_volume() { p->split_volume(); }
@@ -6393,12 +6545,7 @@ void Plater::search(bool plater_is_active)
canvas3D()->on_char(evt);
}
else
- {
- wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
- pos.x += em_unit(this) * 40;
- pos.y += em_unit(this) * 4;
- p->sidebar->get_searcher().search_dialog->Popup(pos);
- }
+ p->sidebar->get_searcher().show_dialog();
}
void Plater::msw_rescale()
@@ -6529,9 +6676,14 @@ Mouse3DController& Plater::get_mouse3d_controller()
return p->mouse3d_controller;
}
-std::shared_ptr<NotificationManager> Plater::get_notification_manager()
+NotificationManager * Plater::get_notification_manager()
+{
+ return p->notification_manager.get();
+}
+
+const NotificationManager * Plater::get_notification_manager() const
{
- return p->notification_manager;
+ return p->notification_manager.get();
}
void Plater::init_notification_manager()
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 4a98797e5..8a8255514 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -37,6 +37,7 @@ using ModelInstancePtrs = std::vector<ModelInstance*>;
namespace UndoRedo {
class Stack;
+ enum class SnapshotType : unsigned char;
struct Snapshot;
}
@@ -140,7 +141,7 @@ public:
bool is_project_dirty() const;
void update_project_dirty_from_presets();
- int save_project_if_dirty();
+ int save_project_if_dirty(const wxString& reason);
void reset_project_dirty_after_save();
void reset_project_dirty_initial_presets();
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
@@ -235,12 +236,14 @@ public:
void schedule_background_process(bool schedule = true);
bool is_background_process_update_scheduled() const;
void suppress_background_process(const bool stop_background_process) ;
- void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
void eject_drive();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
+ void take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type);
+ void take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type);
+
void undo();
void redo();
void undo_to(int selection);
@@ -359,7 +362,9 @@ public:
void set_bed_shape() const;
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
- std::shared_ptr<NotificationManager> get_notification_manager();
+ NotificationManager * get_notification_manager();
+ const NotificationManager * get_notification_manager() const;
+
void init_notification_manager();
void bring_instance_forward();
@@ -390,6 +395,12 @@ public:
m_plater->take_snapshot(snapshot_name);
m_plater->suppress_snapshots();
}
+ TakeSnapshot(Plater *plater, const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) : m_plater(plater)
+ {
+ m_plater->take_snapshot(snapshot_name, snapshot_type);
+ m_plater->suppress_snapshots();
+ }
+
~TakeSnapshot()
{
m_plater->allow_snapshots();
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index 53c93b6b0..0687399b2 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -73,7 +73,7 @@ void PreferencesDialog::build(size_t selected_tab)
// Add "General" tab
m_optgroup_general = create_options_tab(_L("General"), tabs);
m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
- if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset")
+ if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset" || opt_key == "default_action_on_new_project")
m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard";
else
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
@@ -186,19 +186,28 @@ void PreferencesDialog::build(size_t selected_tab)
option = Option(def, "single_instance");
m_optgroup_general->append_single_option_line(option);
- def.label = L("Ask for unsaved changes when closing application");
+ def.label = L("Ask for unsaved changes when closing application or loading new project");
def.type = coBool;
- def.tooltip = L("When closing the application, always ask for unsaved changes");
+ def.tooltip = L("Always ask for unsaved changes, when: \n"
+ "- Closing PrusaSlicer while some presets are modified,\n"
+ "- Loading a new project while some presets are modified");
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_close_application") == "none" });
option = Option(def, "default_action_on_close_application");
m_optgroup_general->append_single_option_line(option);
def.label = L("Ask for unsaved changes when selecting new preset");
def.type = coBool;
- def.tooltip = L("Always ask for unsaved changes when selecting new preset");
+ def.tooltip = L("Always ask for unsaved changes when selecting new preset or resetting a preset");
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_select_preset") == "none" });
option = Option(def, "default_action_on_select_preset");
m_optgroup_general->append_single_option_line(option);
+
+ def.label = L("Ask for unsaved changes when creating new project");
+ def.type = coBool;
+ def.tooltip = L("Always ask for unsaved changes when creating new project");
+ def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_new_project") == "none" });
+ option = Option(def, "default_action_on_new_project");
+ m_optgroup_general->append_single_option_line(option);
}
#ifdef _WIN32
else {
@@ -619,8 +628,8 @@ void PreferencesDialog::create_settings_mode_widget()
m_values["old_settings_layout_mode"] = (id == 0) ? "1" : "0";
#ifdef _MSW_DARK_MODE
if (!disable_new_layout)
- m_values["new_settings_layout_mode"] = (id == 1) ? "1" : "0";
#endif
+ m_values["new_settings_layout_mode"] = (id == 1) ? "1" : "0";
m_values["dlg_settings_layout_mode"] = (id == dlg_id) ? "1" : "0";
});
id++;
diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp
index 3ca764e91..4ce806b2a 100644
--- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp
+++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp
@@ -195,8 +195,10 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type)
void ProjectDirtyStateManager::update_from_presets()
{
m_state.presets = false;
- for (const auto& [type, name] : wxGetApp().get_selected_presets()) {
- m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
+ // check switching of the presets only for exist/loaded project, but not for new
+ if (!wxGetApp().plater()->get_project_filename().IsEmpty()) {
+ for (const auto& [type, name] : wxGetApp().get_selected_presets())
+ m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
}
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
wxGetApp().mainframe->update_title();
diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 593d8241d..5fb8b0132 100644
--- a/src/slic3r/GUI/RemovableDriveManager.cpp
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -201,9 +201,7 @@ namespace search_for_drives_internal
struct stat buf;
stat(path.c_str(), &buf);
uid_t uid = buf.st_uid;
- std::string username(std::getenv("USER"));
- struct passwd *pw = getpwuid(uid);
- if (pw != 0 && pw->pw_name == username)
+ if (getuid() == uid)
out.emplace_back(DriveData{ boost::filesystem::basename(boost::filesystem::path(path)), path });
}
}
@@ -245,7 +243,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
//search_path("/Volumes/*", "/Volumes");
- std::string path(std::getenv("USER"));
+ std::string path = wxGetUserId().ToUTF8().data();
std::string pp(path);
//search /media/USERNAME/* folder
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 83d60ac6e..1e37dc372 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -24,6 +24,8 @@ using boost::optional;
namespace Slic3r {
+wxDEFINE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
+
using GUI::from_u8;
using GUI::into_u8;
@@ -289,13 +291,10 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
OptionsSearcher::OptionsSearcher()
{
- search_dialog = new SearchDialog(this);
}
OptionsSearcher::~OptionsSearcher()
{
- if (search_dialog)
- search_dialog->Destroy();
}
void OptionsSearcher::init(std::vector<InputInfo> input_values)
@@ -386,6 +385,22 @@ Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& l
return create_option(opt_key, label, type, gc);
}
+void OptionsSearcher::show_dialog()
+{
+ if (!search_dialog) {
+ search_dialog = new SearchDialog(this);
+
+ auto parent = search_dialog->GetParent();
+ wxPoint pos = parent->ClientToScreen(wxPoint(0, 0));
+ pos.x += em_unit(parent) * 40;
+ pos.y += em_unit(parent) * 4;
+
+ search_dialog->SetPosition(pos);
+ }
+
+ search_dialog->Popup();
+}
+
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};
@@ -405,7 +420,7 @@ static const std::map<const char, int> icon_idxs = {
};
SearchDialog::SearchDialog(OptionsSearcher* searcher)
- : GUI::DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+ : GUI::DPIDialog(GUI::wxGetApp().tab_panel(), wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
searcher(searcher)
{
SetFont(GUI::wxGetApp().normal_font());
@@ -506,7 +521,8 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
if (check_english)
check_english->SetValue(params.english);
- this->SetPosition(position);
+ if (position != wxDefaultPosition)
+ this->SetPosition(position);
this->ShowModal();
}
@@ -514,9 +530,16 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection)
{
if (!selection.IsOk())
return;
-
- GUI::wxGetApp().sidebar().jump_to_option(search_list_model->GetRow(selection));
this->EndModal(wxID_CLOSE);
+
+ // If call GUI::wxGetApp().sidebar.jump_to_option() directly from here,
+ // then mainframe will not have focus and found option will not be "active" (have cursor) as a result
+ // SearchDialog have to be closed and have to lose a focus
+ // and only after that jump_to_option() function can be called
+ // So, post event to plater:
+ wxCommandEvent event(wxCUSTOMEVT_JUMP_TO_OPTION);
+ event.SetInt(search_list_model->GetRow(selection));
+ wxPostEvent(GUI::wxGetApp().plater(), event);
}
void SearchDialog::OnInputText(wxCommandEvent&)
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index a942a89f8..19de58f96 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -22,6 +22,8 @@
namespace Slic3r {
+wxDECLARE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
+
namespace Search{
class SearchDialog;
@@ -134,6 +136,8 @@ public:
return o1.key < o2.key; });
}
void sort_options_by_label() { sort_options(); }
+
+ void show_dialog();
};
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 92c758d4b..3ab9b7bdc 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -10,6 +10,7 @@
#include "Gizmos/GLGizmoBase.hpp"
#include "Camera.hpp"
#include "Plater.hpp"
+#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/Model.hpp"
@@ -162,7 +163,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
needs_reset |= is_any_modifier() && !volume->is_modifier;
if (!already_contained || needs_reset) {
- wxGetApp().plater()->take_snapshot(_L("Selection-Add"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Add"), UndoRedo::SnapshotType::Selection);
if (needs_reset)
clear();
@@ -203,7 +204,7 @@ void Selection::remove(unsigned int volume_idx)
if (!contains_volume(volume_idx))
return;
- wxGetApp().plater()->take_snapshot(_L("Selection-Remove"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Remove"), UndoRedo::SnapshotType::Selection);
GLVolume* volume = (*m_volumes)[volume_idx];
@@ -235,7 +236,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection)
(as_single_selection && matches(volume_idxs)))
return;
- wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"), UndoRedo::SnapshotType::Selection);
// resets the current list if needed
if (as_single_selection)
@@ -254,7 +255,7 @@ void Selection::remove_object(unsigned int object_idx)
if (!m_valid)
return;
- wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"), UndoRedo::SnapshotType::Selection);
do_remove_object(object_idx);
@@ -272,7 +273,7 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx,
(as_single_selection && matches(volume_idxs)))
return;
- wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"), UndoRedo::SnapshotType::Selection);
// resets the current list if needed
if (as_single_selection)
@@ -291,7 +292,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i
if (!m_valid)
return;
- wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"), UndoRedo::SnapshotType::Selection);
do_remove_instance(object_idx, instance_idx);
@@ -388,7 +389,7 @@ void Selection::add_all()
if ((unsigned int)m_list.size() == count)
return;
- wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")));
+ wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")), UndoRedo::SnapshotType::Selection);
m_mode = Instance;
clear();
@@ -413,7 +414,7 @@ void Selection::remove_all()
// Not taking the snapshot with non-empty Redo stack will likely be more confusing than losing the Redo stack.
// Let's wait for user feedback.
// if (!wxGetApp().plater()->can_redo())
- wxGetApp().plater()->take_snapshot(_L("Selection-Remove All"));
+ wxGetApp().plater()->take_snapshot(_L("Selection-Remove All"), UndoRedo::SnapshotType::Selection);
m_mode = Instance;
clear();
@@ -620,24 +621,54 @@ const GLVolume* Selection::get_volume(unsigned int volume_idx) const
const BoundingBoxf3& Selection::get_bounding_box() const
{
- if (m_bounding_box_dirty)
- calc_bounding_box();
-
- return m_bounding_box;
+ if (!m_bounding_box.has_value()) {
+ std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_bounding_box);
+ *bbox = BoundingBoxf3();
+ if (m_valid) {
+ for (unsigned int i : m_list) {
+ (*bbox)->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
+ }
+ }
+ }
+ return *m_bounding_box;
}
const BoundingBoxf3& Selection::get_unscaled_instance_bounding_box() const
{
- if (m_unscaled_instance_bounding_box_dirty)
- calc_unscaled_instance_bounding_box();
- return m_unscaled_instance_bounding_box;
+ if (!m_unscaled_instance_bounding_box.has_value()) {
+ std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_unscaled_instance_bounding_box);
+ *bbox = BoundingBoxf3();
+ if (m_valid) {
+ for (unsigned int i : m_list) {
+ const GLVolume& volume = *(*m_volumes)[i];
+ if (volume.is_modifier)
+ continue;
+ Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
+ trafo.translation().z() += volume.get_sla_shift_z();
+ (*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
+ }
+ }
+ }
+ return *m_unscaled_instance_bounding_box;
}
const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const
{
- if (m_scaled_instance_bounding_box_dirty)
- calc_scaled_instance_bounding_box();
- return m_scaled_instance_bounding_box;
+ if (!m_scaled_instance_bounding_box.has_value()) {
+ std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_scaled_instance_bounding_box);
+ *bbox = BoundingBoxf3();
+ if (m_valid) {
+ for (unsigned int i : m_list) {
+ const GLVolume& volume = *(*m_volumes)[i];
+ if (volume.is_modifier)
+ continue;
+ Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
+ trafo.translation().z() += volume.get_sla_shift_z();
+ (*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
+ }
+ }
+ }
+ return *m_scaled_instance_bounding_box;
}
void Selection::start_dragging()
@@ -823,10 +854,10 @@ void Selection::flattening_rotate(const Vec3d& normal)
}
#if !DISABLE_INSTANCES_SYNCH
- // we want to synchronize z-rotation as well, otherwise the flattening behaves funny
- // when applied on one of several identical instances
+ // Apply the same transformation also to other instances,
+ // but respect their possibly diffrent z-rotation.
if (m_mode == Instance)
- synchronize_unselected_instances(SYNC_ROTATION_FULL);
+ synchronize_unselected_instances(SYNC_ROTATION_GENERAL);
#endif // !DISABLE_INSTANCES_SYNCH
this->set_bounding_boxes_dirty();
@@ -1692,52 +1723,6 @@ void Selection::do_remove_object(unsigned int object_idx)
}
}
-void Selection::calc_bounding_box() const
-{
- BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
- *bounding_box = BoundingBoxf3();
- if (m_valid) {
- for (unsigned int i : m_list) {
- bounding_box->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
- }
- }
- *const_cast<bool*>(&m_bounding_box_dirty) = false;
-}
-
-void Selection::calc_unscaled_instance_bounding_box() const
-{
- BoundingBoxf3* unscaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_unscaled_instance_bounding_box);
- *unscaled_instance_bounding_box = BoundingBoxf3();
- if (m_valid) {
- for (unsigned int i : m_list) {
- const GLVolume& volume = *(*m_volumes)[i];
- if (volume.is_modifier)
- continue;
- Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
- trafo.translation().z() += volume.get_sla_shift_z();
- unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
- }
- }
- *const_cast<bool*>(&m_unscaled_instance_bounding_box_dirty) = false;
-}
-
-void Selection::calc_scaled_instance_bounding_box() const
-{
- BoundingBoxf3* scaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_scaled_instance_bounding_box);
- *scaled_instance_bounding_box = BoundingBoxf3();
- if (m_valid) {
- for (unsigned int i : m_list) {
- const GLVolume& volume = *(*m_volumes)[i];
- if (volume.is_modifier)
- continue;
- Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
- trafo.translation().z() += volume.get_sla_shift_z();
- scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
- }
- }
- *const_cast<bool*>(&m_scaled_instance_bounding_box_dirty) = false;
-}
-
void Selection::render_selected_volumes() const
{
float color[3] = { 1.0f, 1.0f, 1.0f };
@@ -2055,10 +2040,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
v->set_instance_offset(Z, volume->get_instance_offset().z());
break;
}
- case SYNC_ROTATION_FULL:
- // rotation comes from place on face -> force given z
- v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() });
- break;
case SYNC_ROTATION_GENERAL:
// generic rotation -> update instance z with the delta of the rotation.
const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation());
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index b3304571f..dea507511 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -1,10 +1,12 @@
#ifndef slic3r_GUI_Selection_hpp_
#define slic3r_GUI_Selection_hpp_
-#include <set>
#include "libslic3r/Geometry.hpp"
#include "GLModel.hpp"
+#include <set>
+#include <optional>
+
namespace Slic3r {
class Shader;
@@ -203,14 +205,11 @@ private:
IndicesList m_list;
Cache m_cache;
Clipboard m_clipboard;
- BoundingBoxf3 m_bounding_box;
- bool m_bounding_box_dirty;
+ std::optional<BoundingBoxf3> m_bounding_box;
// Bounding box of a selection, with no instance scaling applied. This bounding box
// is useful for absolute scaling of tilted objects in world coordinate space.
- BoundingBoxf3 m_unscaled_instance_bounding_box;
- bool m_unscaled_instance_bounding_box_dirty;
- BoundingBoxf3 m_scaled_instance_bounding_box;
- bool m_scaled_instance_bounding_box_dirty;
+ std::optional<BoundingBoxf3> m_unscaled_instance_bounding_box;
+ std::optional<BoundingBoxf3> m_scaled_instance_bounding_box;
#if ENABLE_RENDER_SELECTION_CENTER
GLModel m_vbo_sphere;
@@ -359,10 +358,7 @@ private:
void do_remove_volume(unsigned int volume_idx);
void do_remove_instance(unsigned int object_idx, unsigned int instance_idx);
void do_remove_object(unsigned int object_idx);
- void calc_bounding_box() const;
- void calc_unscaled_instance_bounding_box() const;
- void calc_scaled_instance_bounding_box() const;
- void set_bounding_boxes_dirty() { m_bounding_box_dirty = true; m_unscaled_instance_bounding_box_dirty = true; m_scaled_instance_bounding_box_dirty = true; }
+ void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); }
void render_selected_volumes() const;
void render_synchronized_volumes() const;
void render_bounding_box(const BoundingBoxf3& box, float* color) const;
@@ -375,10 +371,8 @@ public:
enum SyncRotationType {
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
SYNC_ROTATION_NONE = 0,
- // Synchronize fully. Used from "place on bed" feature.
- SYNC_ROTATION_FULL = 1,
// Synchronize after rotation by an axis not parallel with Z.
- SYNC_ROTATION_GENERAL = 2,
+ SYNC_ROTATION_GENERAL = 1,
};
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
void synchronize_unselected_volumes();
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 5effa3599..c91ac4784 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -501,6 +501,8 @@ void Tab::OnActivate()
m_presets_choice->SetMinSize(ok_sz);
m_presets_choice->SetSize(ok_sz);
GetSizer()->GetItem(size_t(0))->GetSizer()->Layout();
+ if (wxGetApp().tabs_as_menu())
+ m_presets_choice->update();
}
#endif // _MSW_DARK_MODE
Refresh();
@@ -1186,11 +1188,21 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
break;
}
+ auto set_focus = [](wxWindow* win) {
+ win->SetFocus();
+#ifdef WIN32
+ if (wxTextCtrl* text = dynamic_cast<wxTextCtrl*>(win))
+ text->SetSelection(-1, -1);
+ else if (wxSpinCtrl* spin = dynamic_cast<wxSpinCtrl*>(win))
+ spin->SetSelection(-1, -1);
+#endif // WIN32
+ };
+
Field* field = get_field(opt_key);
// focused selected field
if (field)
- field->getWindow()->SetFocus();
+ set_focus(field->getWindow());
else if (category == "Single extruder MM setup") {
// When we show and hide "Single extruder MM setup" page,
// related options are still in the search list
@@ -1198,7 +1210,7 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
// as a "way" to show hidden page again
field = get_field("single_extruder_multi_material");
if (field)
- field->getWindow()->SetFocus();
+ set_focus(field->getWindow());
}
m_highlighter.init(get_custom_ctrl_with_blinking_ptr(opt_key));
@@ -1216,12 +1228,20 @@ void Tab::cache_config_diff(const std::vector<std::string>& selected_options)
void Tab::apply_config_from_cache()
{
+ bool was_applied = false;
+ // check and apply extruders count for printer preset
+ if (m_type == Preset::TYPE_PRINTER)
+ was_applied = static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache();
+
if (!m_cache_config.empty()) {
m_presets->get_edited_preset().config.apply(m_cache_config);
m_cache_config.clear();
- update_dirty();
+ was_applied = true;
}
+
+ if (was_applied)
+ update_dirty();
}
@@ -3322,10 +3342,6 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL };
}
- // check and apply extruders count for printer preset
- if (m_type == Preset::TYPE_PRINTER)
- static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache();
-
// check if there is something in the cache to move to the new selected preset
apply_config_from_cache();
@@ -3862,15 +3878,17 @@ void TabPrinter::cache_extruder_cnt()
m_cache_extruder_count = m_extruders_count;
}
-void TabPrinter::apply_extruder_cnt_from_cache()
+bool TabPrinter::apply_extruder_cnt_from_cache()
{
if (m_presets->get_edited_preset().printer_technology() == ptSLA)
- return;
+ return false;
if (m_cache_extruder_count > 0) {
m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count);
m_cache_extruder_count = 0;
+ return true;
}
+ return false;
}
bool Tab::validate_custom_gcodes()
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 74585f2aa..5b7983711 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -469,7 +469,7 @@ public:
wxSizer* create_bed_shape_widget(wxWindow* parent);
void cache_extruder_cnt();
- void apply_extruder_cnt_from_cache();
+ bool apply_extruder_cnt_from_cache();
};
class TabSLAMaterial : public Tab
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index ef480f60f..ffa47ed63 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -727,14 +727,21 @@ void DiffViewCtrl::item_value_changed(wxDataViewEvent& event)
m_empty_selection = selected_options().empty();
}
-std::vector<std::string> DiffViewCtrl::unselected_options(Preset::Type type)
+bool DiffViewCtrl::has_unselected_options()
+{
+ for (auto item : m_items_map)
+ if (!model->IsEnabledItem(item.first))
+ return true;
+
+ return false;
+}
+
+std::vector<std::string> DiffViewCtrl::options(Preset::Type type, bool selected)
{
std::vector<std::string> ret;
for (auto item : m_items_map) {
- if (item.second.opt_key == "extruders_count")
- continue;
- if (item.second.type == type && !model->IsEnabledItem(item.first))
+ if (item.second.type == type && model->IsEnabledItem(item.first) == selected)
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
}
@@ -757,20 +764,24 @@ std::vector<std::string> DiffViewCtrl::selected_options()
// UnsavedChangesDialog
//------------------------------------------
-UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header, const wxString& caption/* = wxString()*/)
- : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, (caption.IsEmpty() ? _L("PrusaSlicer is closing") : caption) + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
-{
- m_app_config_key = "default_action_on_close_application";
+static std::string none{"none"};
+UnsavedChangesDialog::UnsavedChangesDialog(const wxString& caption, const wxString& header,
+ const std::string& app_config_key, int act_buttons)
+ : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, caption + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+ m_app_config_key(app_config_key),
+ m_buttons(act_buttons)
+{
build(Preset::TYPE_INVALID, nullptr, "", header);
- const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
- if (def_action == "none")
+ const std::string& def_action = m_app_config_key.empty() ? none : wxGetApp().app_config->get(m_app_config_key);
+ if (def_action == none)
this->CenterOnScreen();
else {
- m_exit_action = def_action == ActSave ? Action::Save : Action::Discard;
- if (m_exit_action == Action::Save)
- save(nullptr);
+ m_exit_action = def_action == ActTransfer ? Action::Transfer :
+ def_action == ActSave ? Action::Save : Action::Discard;
+ if (m_exit_action != Action::Discard)
+ save(nullptr, m_exit_action == Action::Save);
}
}
@@ -782,7 +793,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection*
build(type, dependent_presets, new_selected_preset);
const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
- if (def_action == "none") {
+ if (def_action == none) {
if (wxGetApp().mainframe->is_dlg_layout() && wxGetApp().mainframe->m_settings_dialog.HasFocus())
this->SetPosition(wxGetApp().mainframe->m_settings_dialog.GetPosition());
this->CenterOnScreen();
@@ -833,7 +844,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
(*btn)->Bind(wxEVT_BUTTON, [this, close_act, dependent_presets](wxEvent&) {
update_config(close_act);
- if (close_act == Action::Save && !save(dependent_presets))
+ bool save_names_and_types = close_act == Action::Save || (close_act == Action::Transfer && ActionButtons::KEEP & m_buttons);
+ if (save_names_and_types && !save(dependent_presets, close_act == Action::Save))
return;
close(close_act);
});
@@ -842,13 +854,26 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
(*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); });
};
- const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
- if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
- dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
- switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
- add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer"));
- add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false);
- add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
+ // "Transfer" / "Keep" button
+ if (ActionButtons::TRANSFER & m_buttons) {
+ const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
+ if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
+ dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
+ switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
+ add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, switched_presets->get_edited_preset().name == new_selected_preset ? _L("Keep") : _L("Transfer"));
+ }
+ if (!m_transfer_btn && (ActionButtons::KEEP & m_buttons))
+ add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Keep"));
+
+ { // "Don't save" / "Discard" button
+ std::string btn_icon = (ActionButtons::DONT_SAVE & m_buttons) ? "" : (dependent_presets || (ActionButtons::KEEP & m_buttons)) ? "switch_presets" : "exit";
+ wxString btn_label = (ActionButtons::DONT_SAVE & m_buttons) ? _L("Don't save") : _L("Discard");
+ add_btn(&m_discard_btn, m_continue_btn_id, btn_icon, Action::Discard, btn_label, false);
+ }
+
+ // "Save" button
+ if (ActionButtons::SAVE & m_buttons)
+ add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24);
buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5);
@@ -859,34 +884,42 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
m_info_line->Hide();
- m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice"));
- m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != "none");
- m_remember_choice->Bind(wxEVT_CHECKBOX, [type, this](wxCommandEvent& evt)
- {
- if (!evt.IsChecked())
- return;
- wxString preferences_item = type == Preset::TYPE_INVALID ? _L("Ask for unsaved changes when closing application") :
- _L("Ask for unsaved changes when selecting new preset");
- wxString msg =
- _L("PrusaSlicer will remember your action.") + "\n\n" +
- (type == Preset::TYPE_INVALID ?
- _L("You will not be asked about the unsaved changes the next time you close PrusaSlicer.") :
- _L("You will not be asked about the unsaved changes the next time you switch a preset.")) + "\n\n" +
- format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
+ if (!m_app_config_key.empty()) {
+ m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice"));
+ m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != none);
+ m_remember_choice->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt)
+ {
+ if (!evt.IsChecked())
+ return;
+ wxString preferences_item = m_app_config_key == "default_action_on_new_project" ? _L("Ask for unsaved changes when creating new project") :
+ m_app_config_key == "default_action_on_select_preset" ? _L("Ask for unsaved changes when selecting new preset") :
+ _L("Ask for unsaved changes when ??closing application??") ;
+ wxString action = m_app_config_key == "default_action_on_new_project" ? _L("You will not be asked about the unsaved changes the next time you create new project") :
+ m_app_config_key == "default_action_on_select_preset" ? _L("You will not be asked about the unsaved changes the next time you switch a preset") :
+ _L("You will not be asked about the unsaved changes the next time you: \n"
+ "- close the application,\n"
+ "- load project,\n"
+ "- process Undo / Redo with change of print technologie,\n"
+ "- take/load snapshot,\n"
+ "- load config file/bundle,\n"
+ "- export config_bundle") ;
+ wxString msg = _L("PrusaSlicer will remember your action.") + "\n\n" + action + "\n\n" +
+ format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
- //wxMessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
- MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
- if (dialog.ShowModal() == wxID_CANCEL)
- m_remember_choice->SetValue(false);
- });
+ MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
+ if (dialog.ShowModal() == wxID_CANCEL)
+ m_remember_choice->SetValue(false);
+ });
+ }
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border);
- topSizer->Add(buttons, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
- topSizer->Add(m_remember_choice, 0, wxEXPAND | wxALL, border);
+ topSizer->Add(buttons, 0, wxEXPAND | wxALL, border);
+ if (m_remember_choice)
+ topSizer->Add(m_remember_choice, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, border);
update(type, dependent_presets, new_selected_preset, header);
@@ -905,10 +938,12 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name
if (action == Action::Undef)
text = _L("Some fields are too long to fit. Right mouse click reveals the full text.");
else if (action == Action::Discard)
- text = _L("All settings changes will be discarded.");
+ text = ActionButtons::DONT_SAVE & m_buttons ? _L("All settings changes will not be saved") :_L("All settings changes will be discarded.");
else {
if (preset_name.empty())
- text = action == Action::Save ? _L("Save the selected options.") : _L("Transfer the selected settings to the newly selected preset.");
+ text = action == Action::Save ? _L("Save the selected options.") :
+ ActionButtons::KEEP & m_buttons ? _L("Keep the selected settings.") :
+ _L("Transfer the selected settings to the newly selected preset.");
else
text = format_wxstr(
action == Action::Save ?
@@ -927,7 +962,7 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name
void UnsavedChangesDialog::update_config(Action action)
{
- if (!m_remember_choice->GetValue())
+ if (!m_remember_choice || !m_remember_choice->GetValue())
return;
std::string act = action == Action::Transfer ? ActTransfer :
@@ -941,7 +976,7 @@ void UnsavedChangesDialog::close(Action action)
this->EndModal(wxID_CLOSE);
}
-bool UnsavedChangesDialog::save(PresetCollection* dependent_presets)
+bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_save_preset_dialog/* = true*/)
{
names_and_types.clear();
@@ -979,7 +1014,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets)
}
- if (!types_for_save.empty()) {
+ if (show_save_preset_dialog && !types_for_save.empty()) {
SavePresetDialog save_dlg(this, types_for_save);
if (save_dlg.ShowModal() != wxID_OK) {
m_exit_action = Action::Discard;
@@ -1164,16 +1199,24 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent
PresetCollection* presets = dependent_presets;
// activate buttons and labels
- m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); });
+ if (m_save_btn)
+ m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); });
if (m_transfer_btn) {
- bool is_empty_name = type != dependent_presets->type();
- m_transfer_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name] (wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); });
+ bool is_empty_name = dependent_presets && type != dependent_presets->type();
+ m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name](wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); });
}
- m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
-
+ if (m_discard_btn)
+ m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
if (type == Preset::TYPE_INVALID) {
- m_action_line->SetLabel(header + "\n" + _L("The following presets were modified:"));
+ PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
+ int presets_cnt = 0;
+ for (Tab* tab : wxGetApp().tabs_list)
+ if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
+ presets_cnt++;
+ m_action_line->SetLabel((header.IsEmpty() ? "" : header + "\n\n") +
+ _L_PLURAL("The following preset was modified",
+ "The following presets were modified", presets_cnt));
}
else {
wxString action_msg;
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp
index 9dbaf6e99..42f93f660 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.hpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp
@@ -220,8 +220,9 @@ public:
void context_menu(wxDataViewEvent& event);
void item_value_changed(wxDataViewEvent& event);
void set_em_unit(int em) { m_em_unit = em; }
+ bool has_unselected_options();
- std::vector<std::string> unselected_options(Preset::Type type);
+ std::vector<std::string> options(Preset::Type type, bool selected);
std::vector<std::string> selected_options();
};
@@ -261,10 +262,22 @@ class UnsavedChangesDialog : public DPIDialog
Action m_exit_action {Action::Undef};
// preset names which are modified in SavePresetDialog and related types
std::vector<std::pair<std::string, Preset::Type>> names_and_types;
+ // additional action buttons used in dialog
+ int m_buttons { ActionButtons::TRANSFER | ActionButtons::SAVE };
public:
- UnsavedChangesDialog(const wxString& header, const wxString& caption = wxString());
+ // Discard and Cancel buttons are always but next buttons are optional
+ enum ActionButtons {
+ TRANSFER = 1,
+ KEEP = 2,
+ SAVE = 4,
+ DONT_SAVE = 8,
+ };
+
+ // show unsaved changes when preset is switching
UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset);
+ // show unsaved changes for all another cases
+ UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons);
~UnsavedChangesDialog() {}
void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = "");
@@ -273,7 +286,8 @@ public:
void show_info_line(Action action, std::string preset_name = "");
void update_config(Action action);
void close(Action action);
- bool save(PresetCollection* dependent_presets);
+ // save information about saved presets and their types to names_and_types and show SavePresetDialog to set the names for new presets
+ bool save(PresetCollection* dependent_presets, bool show_save_preset_dialog = true);
bool save_preset() const { return m_exit_action == Action::Save; }
bool transfer_changes() const { return m_exit_action == Action::Transfer; }
@@ -284,8 +298,10 @@ public:
// short version of the previous function, for the case, when just one preset is modified
std::string get_preset_name() { return names_and_types[0].first; }
- std::vector<std::string> get_unselected_options(Preset::Type type) { return m_tree->unselected_options(type); }
+ std::vector<std::string> get_unselected_options(Preset::Type type) { return m_tree->options(type, false); }
+ std::vector<std::string> get_selected_options (Preset::Type type) { return m_tree->options(type, true); }
std::vector<std::string> get_selected_options() { return m_tree->selected_options(); }
+ bool has_unselected_options() { return m_tree->has_unselected_options(); }
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index a3d6384e0..5e703a37b 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -672,10 +672,9 @@ void ModeButton::focus_button(const bool focus)
Slic3r::GUI::wxGetApp().normal_font();
SetFont(new_font);
-//#ifdef _WIN32
-// GetParent()->Refresh();
-//#else
-#ifndef _WIN32
+#ifdef _WIN32
+ GetParent()->Refresh(); // force redraw a background of the selected mode button
+#else
SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT :
#if defined (__linux__) && defined (__WXGTK3__)
wxSYS_COLOUR_GRAYTEXT
diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp
index 2fbd0d937..30c81f6f7 100644
--- a/src/slic3r/Utils/FixModelByWin10.cpp
+++ b/src/slic3r/Utils/FixModelByWin10.cpp
@@ -318,7 +318,10 @@ public:
const char* what() const throw() { return "Model repair has been canceled"; }
};
-void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
+// returt FALSE, if fixing was canceled
+// fix_result is empty, if fixing finished successfully
+// fix_result containes a message if fixing failed
+bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
{
std::mutex mutex;
std::condition_variable condition;
@@ -337,11 +340,6 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
else
volumes.emplace_back(model_object.volumes[volume_idx]);
- // Open a progress dialog.
- wxProgressDialog progress_dialog(
- _L("Model fixing"),
- _L("Exporting model") + "...",
- 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing
// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
bool success = false;
@@ -404,6 +402,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
}
for (size_t i = 0; i < volumes.size(); ++ i) {
volumes[i]->set_mesh(std::move(meshes_repaired[i]));
+ volumes[i]->calculate_convex_hull();
volumes[i]->set_new_unique_id();
}
model_object.invalidate_bounding_box();
@@ -423,21 +422,23 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
});
while (! finished) {
condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; });
- if (! progress_dialog.Update(progress.percent, _(progress.message)))
+ // decrease progress.percent value to avoid closing of the progress dialog
+ if (!progress_dialog.Update(progress.percent-1, msg_header + _(progress.message)))
canceled = true;
+ else
+ progress_dialog.Fit();
progress.updated = false;
}
if (canceled) {
// Nothing to show.
} else if (success) {
- Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repaired successfully"), _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK);
- dlg.ShowModal();
+ fix_result = "";
} else {
- Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repair failed:") + " \n" + _(progress.message), _L("Model Repair by the Netfabb service"), wxICON_ERROR | wxOK);
- dlg.ShowModal();
+ fix_result = progress.message;
}
worker_thread.join();
+ return !canceled;
}
} // namespace Slic3r
diff --git a/src/slic3r/Utils/FixModelByWin10.hpp b/src/slic3r/Utils/FixModelByWin10.hpp
index 8e4766467..a1ef4511b 100644
--- a/src/slic3r/Utils/FixModelByWin10.hpp
+++ b/src/slic3r/Utils/FixModelByWin10.hpp
@@ -3,6 +3,8 @@
#include <string>
+class wxProgressDialog;
+
namespace Slic3r {
class Model;
@@ -12,12 +14,14 @@ class Print;
#ifdef HAS_WIN10SDK
extern bool is_windows10();
-extern void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx);
+// returt false, if fixing was canceled
+extern bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dlg, const wxString& msg_header, std::string& fix_result);
#else /* HAS_WIN10SDK */
inline bool is_windows10() { return false; }
-inline void fix_model_by_win10_sdk_gui(ModelObject &, int) {}
+// returt false, if fixing was canceled
+inline bool fix_model_by_win10_sdk_gui(ModelObject&, int, wxProgressDialog&, const wxString&, std::string&) { return false; }
#endif /* HAS_WIN10SDK */
diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp
index 29d474dca..97c6cb2a5 100644
--- a/src/slic3r/Utils/PresetUpdater.cpp
+++ b/src/slic3r/Utils/PresetUpdater.cpp
@@ -720,8 +720,13 @@ void PresetUpdater::slic3r_update_notify()
}
}
-static void reload_configs_update_gui()
+static bool reload_configs_update_gui()
{
+ wxString header = _L("Configuration Updates causes a lost of preset modification.\n"
+ "So, check unsaved changes and save them if necessary.");
+ if (!GUI::wxGetApp().check_and_save_current_preset_changes(_L("Updater is processing"), header, false ))
+ return false;
+
// Reload global configuration
auto* app_config = GUI::wxGetApp().app_config;
// System profiles should not trigger any substitutions, user profiles may trigger substitutions, but these substitutions
@@ -730,7 +735,8 @@ static void reload_configs_update_gui()
GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem);
GUI::wxGetApp().load_current_presets();
GUI::wxGetApp().plater()->set_bed_shape();
- GUI::wxGetApp().update_wizard_from_config();
+
+ return true;
}
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params) const
@@ -803,9 +809,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
- if (! p->perform_updates(std::move(updates)))
+ if (! p->perform_updates(std::move(updates)) ||
+ ! reload_configs_update_gui())
return R_INCOMPAT_EXIT;
- reload_configs_update_gui();
return R_UPDATE_INSTALLED;
}
else {
@@ -833,9 +839,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
- if (! p->perform_updates(std::move(updates)))
+ if (! p->perform_updates(std::move(updates)) ||
+ ! reload_configs_update_gui())
return R_ALL_CANCELED;
- reload_configs_update_gui();
return R_UPDATE_INSTALLED;
}
else {
@@ -886,8 +892,8 @@ void PresetUpdater::on_update_notification_confirm()
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
- if (p->perform_updates(std::move(p->waiting_updates))) {
- reload_configs_update_gui();
+ if (p->perform_updates(std::move(p->waiting_updates)) &&
+ reload_configs_update_gui()) {
p->has_waiting_updates = false;
}
}
diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp
index 6d6753ea2..f5d418fa7 100644
--- a/src/slic3r/Utils/UndoRedo.cpp
+++ b/src/slic3r/Utils/UndoRedo.cpp
@@ -556,6 +556,12 @@ public:
// Snapshot history (names with timestamps).
const std::vector<Snapshot>& snapshots() const { return m_snapshots; }
+ const Snapshot& snapshot(size_t time) const {
+ const auto it = std::lower_bound(m_snapshots.cbegin(), m_snapshots.cend(), UndoRedo::Snapshot(time));
+ assert(it != m_snapshots.end() && it->timestamp == time);
+ return *it;
+ }
+
// Timestamp of the active snapshot.
size_t active_snapshot_time() const { return m_active_snapshot_time; }
bool temp_snapshot_active() const { return m_snapshots.back().timestamp == m_active_snapshot_time && ! m_snapshots.back().is_topmost_captured(); }
@@ -1097,48 +1103,9 @@ bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, siz
const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); }
const std::vector<Snapshot>& Stack::snapshots() const { return pimpl->snapshots(); }
+const Snapshot& Stack::snapshot(size_t time) const { return pimpl->snapshot(time); }
size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); }
bool Stack::temp_snapshot_active() const { return pimpl->temp_snapshot_active(); }
} // namespace UndoRedo
} // namespace Slic3r
-
-
-//FIXME we should have unit tests for testing serialization of basic types as DynamicPrintConfig.
-#if 0
-#include "libslic3r/Config.hpp"
-#include "libslic3r/PrintConfig.hpp"
-namespace Slic3r {
- bool test_dynamic_print_config_serialization() {
- FullPrintConfig full_print_config;
- DynamicPrintConfig cfg;
- cfg.apply(full_print_config, false);
-
- std::string serialized;
- try {
- std::ostringstream ss;
- cereal::BinaryOutputArchive oarchive(ss);
- oarchive(cfg);
- serialized = ss.str();
- } catch (std::runtime_error e) {
- e.what();
- }
-
- DynamicPrintConfig cfg2;
- try {
- std::stringstream ss(serialized);
- cereal::BinaryInputArchive iarchive(ss);
- iarchive(cfg2);
- } catch (std::runtime_error e) {
- e.what();
- }
-
- if (cfg == cfg2) {
- printf("Yes!\n");
- return true;
- }
- printf("No!\n");
- return false;
- }
-} // namespace Slic3r
-#endif
diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp
index e120c4f79..d29c0e979 100644
--- a/src/slic3r/Utils/UndoRedo.hpp
+++ b/src/slic3r/Utils/UndoRedo.hpp
@@ -24,6 +24,23 @@ namespace GUI {
namespace UndoRedo {
+enum class SnapshotType : unsigned char {
+ // Some action modifying project state.
+ Action,
+ // Selection change at the Plater.
+ Selection,
+ // New project, Reset project, Load project ...
+ ProjectSeparator,
+ // Entering a Gizmo, which opens a secondary Undo / Redo stack.
+ EnteringGizmo,
+ // Leaving a Gizmo, which closes a secondary Undo / Redo stack.
+ // No action modifying a project state was done between EnteringGizmo / LeavingGizmo.
+ LeavingGizmoNoAction,
+ // Leaving a Gizmo, which closes a secondary Undo / Redo stack.
+ // Some action modifying a project state was done between EnteringGizmo / LeavingGizmo.
+ LeavingGizmoWithAction,
+};
+
// Data structure to be stored with each snapshot.
// Storing short data (bit masks, ints) with each snapshot instead of being serialized into the Undo / Redo stack
// is likely cheaper in term of both the runtime and memory allocation.
@@ -34,6 +51,7 @@ struct SnapshotData
// Constructor is defined in .cpp due to the forward declaration of enum PrinterTechnology.
SnapshotData();
+ SnapshotType snapshot_type;
PrinterTechnology printer_technology;
// Bitmap of Flags (see the Flags enum).
unsigned int flags;
@@ -122,10 +140,13 @@ public:
// There is one additional snapshot taken at the very end, which indicates the current unnamed state.
const std::vector<Snapshot>& snapshots() const;
+ const Snapshot& snapshot(size_t time) const;
+
// Timestamp of the active snapshot. One of the snapshots of this->snapshots() shall have Snapshot::timestamp equal to this->active_snapshot_time().
- // The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore
- // the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations.
+ // The active snapshot may be a special placeholder "@@@ Topmost @@@" indicating an uncaptured current state,
+ // or the active snapshot may be an active state to which the application state was undoed or redoed.
size_t active_snapshot_time() const;
+ const Snapshot& active_snapshot() const { return this->snapshot(this->active_snapshot_time()); }
// Temporary snapshot is active if the topmost snapshot is active and it has not been captured yet.
// In that case the Undo action will capture the last snapshot.
bool temp_snapshot_active() const;
diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp
index f5424dfd9..32e31c264 100644
--- a/tests/fff_print/test_data.cpp
+++ b/tests/fff_print/test_data.cpp
@@ -58,12 +58,13 @@ TriangleMesh mesh(TestMesh m)
break;
case TestMesh::cube_with_concave_hole:
mesh = TriangleMesh(
- { {-10,-10,-5}, {-10,-10,5}, {-10,10,-5}, {-10,10,5}, {10,-10,-5}, {10,-10,5}, {-5,-5,-5}, {5,-5,-5}, {5,5,-5}, {5,10,-5}, {-5,5,-5}, Vec3d(3.06161699911402e-16,5,-5), {5,0,-5}, {0,0,-5}, {10,5,-5}, {5,10,5}, {-5,-5,5}, {5,0,5}, {5,-5,5}, {-5,5,5}, {10,5,5}, {5,5,5}, Vec3d(3.06161699911402e-16,5,5), {0,0,5} },
+ { {-10,-10,-5}, {-10,-10,5}, {-10,10,-5}, {-10,10,5}, {10,-10,-5}, {10,-10,5}, {-5,-5,-5}, {5,-5,-5}, {5,5,-5}, {5,10,-5}, {-5,5,-5}, Vec3f(3.06161699911402e-16f,5,-5),
+ {5,0,-5}, {0,0,-5}, {10,5,-5}, {5,10,5}, {-5,-5,5}, {5,0,5}, {5,-5,5}, {-5,5,5}, {10,5,5}, {5,5,5}, Vec3f(3.06161699911402e-16f,5,5), {0,0,5} },
{ {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {10,2,11}, {11,12,13}, {0,2,10}, {0,10,6}, {0,6,4}, {11,2,8}, {4,7,12}, {4,12,8}, {12,11,8}, {14,4,8}, {2,3,9}, {9,3,15}, {16,1,5}, {17,18,5}, {19,3,16}, {20,21,5}, {18,16,5}, {3,1,16}, {22,3,19}, {21,3,22}, {21,17,5}, {21,22,17}, {21,15,3}, {23,17,22}, {5,4,14}, {20,5,14}, {20,14,21}, {21,14,8}, {9,15,21}, {8,9,21}, {10,19,16}, {6,10,16}, {11,22,19}, {10,11,19}, {13,23,11}, {11,23,22}, {23,13,12}, {17,23,12}, {17,12,18}, {18,12,7}, {18,7,16}, {16,7,6} });
break;
case TestMesh::V:
mesh = TriangleMesh(
- { {-14,0,20}, {-14,15,20}, {0,0,0}, {0,15,0}, {-4,0,20}, {-4,15,20}, {5,0,7.14286}, {10,0,0}, {24,0,20}, {14,0,20}, {10,15,0}, {5,15,7.14286}, {14,15,20}, {24,15,20} },
+ { {-14,0,20}, {-14,15,20}, {0,0,0}, {0,15,0}, {-4,0,20}, {-4,15,20}, {5,0,7.14286f}, {10,0,0}, {24,0,20}, {14,0,20}, {10,15,0}, {5,15,7.14286f}, {14,15,20}, {24,15,20} },
{ {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {4,0,2}, {6,4,2}, {7,6,2}, {8,9,7}, {9,6,7}, {2,3,7}, {7,3,10}, {1,5,3}, {3,5,11}, {11,12,13}, {11,13,3}, {3,13,10}, {5,4,6}, {11,5,6}, {6,9,11}, {11,9,12}, {12,9,8}, {13,12,8}, {8,7,10}, {13,8,10} });
break;
case TestMesh::L:
@@ -88,7 +89,7 @@ TriangleMesh mesh(TestMesh m)
break;
case TestMesh::ipadstand:
mesh = TriangleMesh(
- { Vec3d(17.4344673156738,-2.69879599481136e-16,9.5), {14.2814798355103,10,9.5}, {0,0,9.5}, {31.7159481048584,10,9.5}, Vec3d(62.2344741821289,2.06667568800577e-16,20), {31.7159481048584,10,20}, Vec3d(17.4344673156738,-2.69879599481136e-16,20), {62.2344741821289,10,20}, {98.2079696655273,10,0}, Vec3d(98.2079696655273,8.56525380796383e-16,10), {98.2079696655273,0,0}, {98.2079696655273,10,20}, {98.2079696655273,0,20}, Vec3d(81.6609649658203,-4.39753856997999e-16,10), {90.0549850463867,10,10}, {78.5079803466797,10,10}, Vec3d(93.2079696655273,8.56525380796383e-16,10), {14.2814798355103,10,20}, {0,0,20}, Vec3d(87.4344711303711,2.81343962782118e-15,20), {84.2814788818359,10,20}, {0,10,20}, {0,0,0}, {0,10,0}, Vec3d(62.2344741821289,2.06667568800577e-16,30), {66.9609756469727,10,30}, {62.2344741821289,10,30}, Vec3d(70.1139602661133,8.5525763717214e-16,30), {67.7053375244141,10,28.7107200622559}, Vec3d(71.6787109375,1.24046736339707e-15,27.2897701263428) },
+ { Vec3f(17.4344673156738,-2.69879599481136e-16,9.5), {14.2814798355103,10,9.5}, {0,0,9.5}, {31.7159481048584,10,9.5}, Vec3f(62.2344741821289,2.06667568800577e-16,20), {31.7159481048584,10,20}, Vec3f(17.4344673156738,-2.69879599481136e-16,20), {62.2344741821289,10,20}, {98.2079696655273,10,0}, Vec3f(98.2079696655273,8.56525380796383e-16,10), {98.2079696655273,0,0}, {98.2079696655273,10,20}, {98.2079696655273,0,20}, Vec3f(81.6609649658203,-4.39753856997999e-16,10), {90.0549850463867,10,10}, {78.5079803466797,10,10}, Vec3f(93.2079696655273,8.56525380796383e-16,10), {14.2814798355103,10,20}, {0,0,20}, Vec3f(87.4344711303711,2.81343962782118e-15,20), {84.2814788818359,10,20}, {0,10,20}, {0,0,0}, {0,10,0}, Vec3f(62.2344741821289,2.06667568800577e-16,30), {66.9609756469727,10,30}, {62.2344741821289,10,30}, Vec3f(70.1139602661133,8.5525763717214e-16,30), {67.7053375244141,10,28.7107200622559}, Vec3f(71.6787109375,1.24046736339707e-15,27.2897701263428) },
{ {0,1,2}, {1,0,3}, {4,5,6}, {5,4,7}, {8,9,10}, {9,11,12}, {11,9,8}, {13,14,15}, {14,13,16}, {17,2,1}, {2,17,18}, {19,11,20}, {11,19,12}, {17,21,18}, {21,2,18}, {2,21,22}, {22,21,23}, {8,22,23}, {22,8,10}, {24,25,26}, {25,24,27}, {23,1,8}, {1,23,21}, {1,21,17}, {5,15,3}, {15,5,7}, {15,7,28}, {28,7,26}, {28,26,25}, {8,14,11}, {14,8,3}, {3,8,1}, {14,3,15}, {11,14,20}, {26,4,24}, {4,26,7}, {12,16,9}, {16,12,19}, {29,4,13}, {4,29,24}, {24,29,27}, {9,22,10}, {22,9,0}, {0,9,16}, {0,16,13}, {0,13,6}, {6,13,4}, {2,22,0}, {19,14,16}, {14,19,20}, {15,29,13}, {29,25,27}, {25,29,15}, {25,15,28}, {6,3,0}, {3,6,5} });
break;
case TestMesh::A:
@@ -98,7 +99,37 @@ TriangleMesh mesh(TestMesh m)
break;
case TestMesh::gt2_teeth:
mesh = TriangleMesh(
- { {15.8899993896484,19.444055557251,2.67489433288574}, {15.9129991531372,19.1590557098389,2.67489433288574}, {15.9039993286133,19.1500549316406,2.67489433288574}, {15.9489994049072,19.2490558624268,2.67489433288574}, {15.9579992294312,19.3570556640625,2.67489433288574}, {15.8819999694824,18.690055847168,2.67489433288574}, {15.8319997787476,17.7460556030273,2.67489433288574}, {15.8489999771118,18.819055557251,2.67489433288574}, {15.8589992523193,17.7190551757812,2.67489433288574}, {15.8769998550415,19.0490550994873,2.67489433288574}, {15.7529993057251,17.8080558776855,2.67489433288574}, {15.7869997024536,19.5010547637939,2.67489433288574}, {14.0329990386963,18.7170543670654,2.67489433288574}, {13.9599990844727,18.7460556030273,2.67489433288574}, {13.9869995117188,20.2840557098389,2.67489433288574}, {14.2029991149902,20.149055480957,2.67489433288574}, {14.1939992904663,19.9560546875,2.67489433288574}, {14.1939992904663,20.1670551300049,2.67489433288574}, {14.2119998931885,20.0590553283691,2.67489433288574}, {12.1899995803833,19.1840553283691,2.67489433288574}, {12.096999168396,19.1950550079346,2.67489433288574}, {12.1099996566772,20.6690559387207,2.67489433288574}, {11.382999420166,19.9750556945801,2.67489433288574}, {11.2599992752075,19.2490558624268,2.67489433288574}, {11.2369995117188,19.9320545196533,2.67489433288574}, {11.5349998474121,20.0640544891357,2.67489433288574}, {11.6259994506836,20.1550559997559,2.67489433288574}, {11.6829986572266,20.2390556335449,2.67489433288574}, {11.7369995117188,20.3570556640625,2.67489433288574}, {11.8449993133545,20.645055770874,2.67489433288574}, {11.7729988098145,20.4640560150146,2.67489433288574}, {11.7799987792969,20.5370559692383,9.41389465332031}, {11.7639999389648,20.4470558166504,2.67489433288574}, {11.9559993743896,20.6810550689697,2.67489433288574}, {12.3079996109009,20.6020545959473,2.67489433288574}, {12.1959991455078,19.1860542297363,2.67489433288574}, {12.2059993743896,20.6540546417236,2.67489433288574}, {12.3489990234375,20.3740558624268,2.67489433288574}, {12.3579998016357,20.2750549316406,2.67489433288574}, {12.3669996261597,20.266056060791,2.67489433288574}, {12.3849992752075,20.1670551300049,2.67489433288574}, {12.4269990921021,20.0680541992188,2.67489433288574}, {12.5029993057251,19.9540557861328,2.67489433288574}, {12.6169996261597,19.8550548553467,2.67489433288574}, {12.7449989318848,19.7800559997559,2.67489433288574}, {12.7629995346069,19.7800559997559,2.67489433288574}, {12.8799991607666,19.7350559234619,2.67489433288574}, {13.0369997024536,19.7250556945801,2.67489433288574}, {13.0149993896484,19.0340557098389,2.67489433288574}, {11.1699991226196,19.2580547332764,2.67489433288574}, {11.0959987640381,19.2580547332764,2.67489433288574}, {11.1209993362427,19.9230556488037,2.67489433288574}, {13.0599994659424,19.024055480957,2.67489433288574}, {14.9049997329712,18.3170547485352,2.67489433288574}, {14.8779993057251,18.3400554656982,2.67489433288574}, {14.8779993057251,19.149055480957,2.67489433288574}, {13.3039989471436,19.77805519104,2.67489433288574}, {13.1589994430542,18.9890556335449,2.67489433288574}, {13.1559991836548,19.7350559234619,2.67489433288574}, {13.4269990921021,19.8600559234619,2.67489433288574}, {13.5339994430542,19.9700546264648,2.67389440536499}, {13.6359996795654,20.1220550537109,2.67489433288574}, {13.6359996795654,20.1400547027588,2.67489433288574}, {13.6719989776611,20.2210559844971,2.67489433288574}, {13.6899995803833,20.2300548553467,2.67489433288574}, {13.7509994506836,20.3010559082031,2.67489433288574}, {13.8539991378784,20.3180541992188,2.67489433288574}, {14.8329992294312,18.3580551147461,2.67489433288574}, {14.1849994659424,19.8530559539795,2.67489433288574}, {14.0769996643066,18.7000541687012,2.67489433288574}, {14.1099996566772,20.2400550842285,2.67489433288574}, {14.2009992599487,19.6230545043945,2.67489433288574}, {14.2729997634888,19.4670543670654,2.67489433288574}, {14.3379993438721,19.3790550231934,2.67489433288574}, {14.4549999237061,19.2770557403564,2.67489433288574}, {14.5899991989136,19.2040557861328,2.67489433288574}, {14.6079998016357,19.2040557861328,2.67489433288574}, {14.7209997177124,19.1600551605225,2.67489433288574}, {15.1379995346069,19.210054397583,2.67489433288574}, {14.9949998855591,18.2680549621582,2.67489433288574}, {15.0029993057251,19.1580543518066,2.67489433288574}, {15.2369995117188,19.2760543823242,2.67489433288574}, {15.3779993057251,19.4060554504395,2.67489433288574}, {15.4539995193481,19.520055770874,2.67489433288574}, {15.471999168396,19.52805519104,2.67489433288574}, {15.5449991226196,19.5830554962158,2.67489433288574}, {15.6529998779297,19.573055267334,2.67489433288574}, {15.7059993743896,17.8360557556152,2.67489433288574}, {15.9449996948242,18.5560550689697,2.67489433288574}, {15.8589992523193,18.9380550384521,2.67489433288574}, {14.9589996337891,18.2950553894043,2.67489433288574}, {15.7779998779297,19.5100555419922,2.67489433288574}, {14.0049991607666,20.2750549316406,2.67489433288574}, {12.3489990234375,20.5000553131104,2.67489433288574}, {13.0689992904663,19.0150547027588,2.67489433288574}, {13.0999994277954,19.0100555419922,2.67489433288574}, {15.9489994049072,19.3670558929443,9.41489505767822}, {15.9489994049072,19.2490558624268,9.41489505767822}, {15.75,17.8080558776855,9.41489505767822}, {15.6639995574951,19.5710544586182,9.41489505767822}, {15.5709991455078,17.9260559082031,9.41489505767822}, {15.8769998550415,18.690055847168,9.41489505767822}, {15.8499994277954,18.8170547485352,9.41489505767822}, {15.9459991455078,18.5520553588867,9.41489505767822}, {15.914999961853,17.6890544891357,9.41489505767822}, {15.3999996185303,19.4290542602539,9.41489505767822}, {15.3099994659424,19.339054107666,9.41489505767822}, {15.3729991912842,18.0440559387207,9.41489505767822}, {15.4579992294312,19.5170555114746,9.41489505767822}, {15.5469999313354,19.5820541381836,9.41489505767822}, {13.2309989929199,19.7610549926758,9.41489505767822}, {13.168999671936,19.7360553741455,9.41489505767822}, {13.096999168396,19.0140552520752,9.41489505767822}, {13.1999988555908,18.9870548248291,9.41489505767822}, {15.1399993896484,19.2080554962158,9.41489505767822}, {15.0159997940063,19.1600551605225,9.41489505767822}, {14.9859991073608,18.2770557403564,9.41489505767822}, {15.1749992370605,18.1690559387207,9.41489505767822}, {15.9039993286133,19.1320552825928,9.41489505767822}, {15.8949995040894,19.4460544586182,9.41489505767822}, {15.8769998550415,19.0420551300049,9.41489505767822}, {12.2169990539551,20.6500549316406,9.41489505767822}, {11.9379997253418,20.6810550689697,9.41489505767822}, {11.8629989624023,19.2130546569824,9.41489505767822}, {12.096999168396,19.1950550079346,9.41489505767822}, {14.1669998168945,18.6640548706055,9.41489505767822}, {14.1039991378784,20.2460556030273,9.41489505767822}, {13.9849996566772,18.7360553741455,9.41489505767822}, {14.7349996566772,19.1590557098389,9.41489505767822}, {14.5849990844727,19.2050552368164,9.41489505767822}, {14.5719995498657,18.4850559234619,9.41489505767822}, {14.1939992904663,19.6760559082031,9.41489505767822}, {14.1849994659424,19.9330558776855,9.41489505767822}, {14.1759996414185,18.6640548706055,9.41489505767822}, {14.261999130249,19.4890556335449,9.41489505767822}, {14.3539991378784,19.3610553741455,9.41489505767822}, {14.3559989929199,18.5830554962158,9.41489505767822}, {11.6039991378784,20.1250553131104,9.41489505767822}, {11.5209999084473,20.0520553588867,9.41489505767822}, {11.4209995269775,19.2480545043945,9.41489505767822}, {11.6989994049072,20.2690544128418,9.41389465332031}, {11.7609996795654,20.4310550689697,9.41489505767822}, {11.8359994888306,19.2130546569824,9.41489505767822}, {14.1889991760254,20.1710548400879,9.41489505767822}, {13.9689998626709,20.2840557098389,9.41489505767822}, {13.8739995956421,20.315055847168,9.41489505767822}, {13.7799997329712,18.8080558776855,9.41489505767822}, {13.9869995117188,20.2750549316406,9.41489505767822}, {12.3129997253418,20.5980548858643,9.41489505767822}, {12.3399991989136,20.5090560913086,9.41489505767822}, {12.3489990234375,20.3830547332764,9.41489505767822}, {12.3599996566772,20.2680549621582,9.41489505767822}, {12.3849992752075,20.1850547790527,9.41489505767822}, {12.3849992752075,20.1670551300049,9.41489505767822}, {12.4249992370605,20.065055847168,9.41489505767822}, {12.4729995727539,19.1350555419922,9.41489505767822}, {14.4399995803833,19.2900543212891,9.41489505767822}, {14.3649997711182,18.5740547180176,9.41489505767822}, {13.5729999542236,20.0310554504395,9.41489505767822}, {13.4889993667603,19.9140548706055,9.41489505767822}, {13.5639991760254,18.8710556030273,9.41489505767822}, {13.6389999389648,20.1310558319092,9.41489505767822}, {13.6719989776611,20.2130546569824,9.41489505767822}, {13.75,20.3020553588867,9.41489505767822}, {12.7399997711182,19.7810554504395,9.41489505767822}, {12.6189994812012,19.8520545959473,9.41489505767822}, {12.5799999237061,19.1200542449951,9.41489505767822}, {12.8349990844727,19.069055557251,9.41489505767822}, {11.2669992446899,19.9350547790527,9.41489505767822}, {11.1029987335205,19.9230556488037,9.41489505767822}, {11.0209999084473,19.2600555419922,9.41489505767822}, {11.3819999694824,19.9710559844971,9.41489505767822}, {13.418999671936,19.8530559539795,9.41489505767822}, {13.4329996109009,18.9160556793213,9.41489505767822}, {11.8399991989136,20.6430549621582,9.41489505767822}, {13.3119993209839,19.7800559997559,9.41489505767822}, {15.2189998626709,19.2600555419922,9.41489505767822}, {15.1839990615845,18.1600551605225,9.41489505767822}, {15.3639993667603,18.0520553588867,9.41489505767822}, {13.0189990997314,19.7250556945801,9.41489505767822}, {12.8949995040894,19.7350559234619,9.41489505767822}, {15.9039993286133,19.1500549316406,9.41489505767822}, {15.7699995040894,19.5140552520752,9.41489505767822}, {15.8589992523193,18.9340553283691,9.41489505767822}, {14.1939992904663,19.9510555267334,9.41489505767822}, {14.2119998931885,20.0630550384521,9.41489505767822}, {14.8589992523193,19.149055480957,9.41489505767822}, {14.8159999847412,18.3670558929443,9.41489505767822}, {14.8959999084473,18.3220558166504,9.41489505767822}, {12.5189990997314,19.9360542297363,9.41489505767822}, {11.0209999084473,19.9290542602539,9.41489505767822}, {11.0209999084473,19.2530555725098,2.67489433288574}, {11.0209999084473,19.9300556182861,2.67489433288574}, {15.9799995422363,18.505931854248,5.58724021911621}, {15.9799995422363,18.5044555664062,9.41489505767822}, {15.9799995422363,18.5041732788086,2.67489433288574}, {15.9799995422363,18.1684837341309,2.67489433288574}, {15.9799995422363,18.1288299560547,9.41489505767822}, {15.9799995422363,17.9876575469971,2.67489433288574}, {15.9799995422363,17.6247596740723,3.91620373725891}, {15.9799995422363,17.6247596740723,2.67489433288574}, {15.9799995422363,17.6254329681396,4.32245063781738}, {15.9799995422363,17.8920269012451,9.41489505767822}, {15.9799995422363,17.8795108795166,2.67489433288574}, {15.9799995422363,17.629810333252,4.58585262298584}, {15.9799995422363,17.6336059570312,5.27938556671143}, {15.9799995422363,17.8311748504639,2.67489433288574}, {15.9799995422363,17.638355255127,9.41489505767822}, {15.9799995422363,17.6346111297607,5.98653984069824}, {15.9799995422363,17.8728256225586,2.67489433288574}, {15.9799995422363,18.2221603393555,2.67489433288574} },
+ { {15.8899993896484,19.444055557251,2.67489433288574}, {15.9129991531372,19.1590557098389,2.67489433288574}, {15.9039993286133,19.1500549316406,2.67489433288574}, {15.9489994049072,19.2490558624268,2.67489433288574},
+ {15.9579992294312,19.3570556640625,2.67489433288574}, {15.8819999694824,18.690055847168,2.67489433288574}, {15.8319997787476,17.7460556030273,2.67489433288574}, {15.8489999771118,18.819055557251,2.67489433288574},
+ {15.8589992523193,17.7190551757812,2.67489433288574}, {15.8769998550415,19.0490550994873,2.67489433288574}, {15.7529993057251,17.8080558776855,2.67489433288574}, {15.7869997024536,19.5010547637939,2.67489433288574},
+ {14.0329990386963,18.7170543670654,2.67489433288574}, {13.9599990844727,18.7460556030273,2.67489433288574}, {13.9869995117188,20.2840557098389,2.67489433288574}, {14.2029991149902,20.149055480957,2.67489433288574},
+ {14.1939992904663,19.9560546875,2.67489433288574}, {14.1939992904663,20.1670551300049,2.67489433288574}, {14.2119998931885,20.0590553283691,2.67489433288574}, {12.1899995803833,19.1840553283691,2.67489433288574},
+ {12.096999168396,19.1950550079346,2.67489433288574}, {12.1099996566772,20.6690559387207,2.67489433288574}, {11.382999420166,19.9750556945801,2.67489433288574}, {11.2599992752075,19.2490558624268,2.67489433288574},
+ {11.2369995117188,19.9320545196533,2.67489433288574}, {11.5349998474121,20.0640544891357,2.67489433288574}, {11.6259994506836,20.1550559997559,2.67489433288574}, {11.6829986572266,20.2390556335449,2.67489433288574},
+ {11.7369995117188,20.3570556640625,2.67489433288574}, {11.8449993133545,20.645055770874,2.67489433288574}, {11.7729988098145,20.4640560150146,2.67489433288574}, {11.7799987792969,20.5370559692383,9.41389465332031},
+ {11.7639999389648,20.4470558166504,2.67489433288574}, {11.9559993743896,20.6810550689697,2.67489433288574}, {12.3079996109009,20.6020545959473,2.67489433288574}, {12.1959991455078,19.1860542297363,2.67489433288574},
+ {12.2059993743896,20.6540546417236,2.67489433288574}, {12.3489990234375,20.3740558624268,2.67489433288574}, {12.3579998016357,20.2750549316406,2.67489433288574}, {12.3669996261597,20.266056060791,2.67489433288574},
+ {12.3849992752075,20.1670551300049,2.67489433288574}, {12.4269990921021,20.0680541992188,2.67489433288574}, {12.5029993057251,19.9540557861328,2.67489433288574}, {12.6169996261597,19.8550548553467,2.67489433288574},
+ {12.7449989318848,19.7800559997559,2.67489433288574}, {12.7629995346069,19.7800559997559,2.67489433288574}, {12.8799991607666,19.7350559234619,2.67489433288574}, {13.0369997024536,19.7250556945801,2.67489433288574},
+ {13.0149993896484,19.0340557098389,2.67489433288574}, {11.1699991226196,19.2580547332764,2.67489433288574}, {11.0959987640381,19.2580547332764,2.67489433288574}, {11.1209993362427,19.9230556488037,2.67489433288574},
+ {13.0599994659424,19.024055480957,2.67489433288574}, {14.9049997329712,18.3170547485352,2.67489433288574}, {14.8779993057251,18.3400554656982,2.67489433288574}, {14.8779993057251,19.149055480957,2.67489433288574},
+ {13.3039989471436,19.77805519104,2.67489433288574}, {13.1589994430542,18.9890556335449,2.67489433288574}, {13.1559991836548,19.7350559234619,2.67489433288574}, {13.4269990921021,19.8600559234619,2.67489433288574},
+ {13.5339994430542,19.9700546264648,2.67389440536499}, {13.6359996795654,20.1220550537109,2.67489433288574}, {13.6359996795654,20.1400547027588,2.67489433288574}, {13.6719989776611,20.2210559844971,2.67489433288574},
+ {13.6899995803833,20.2300548553467,2.67489433288574}, {13.7509994506836,20.3010559082031,2.67489433288574}, {13.8539991378784,20.3180541992188,2.67489433288574}, {14.8329992294312,18.3580551147461,2.67489433288574},
+ {14.1849994659424,19.8530559539795,2.67489433288574}, {14.0769996643066,18.7000541687012,2.67489433288574}, {14.1099996566772,20.2400550842285,2.67489433288574}, {14.2009992599487,19.6230545043945,2.67489433288574},
+ {14.2729997634888,19.4670543670654,2.67489433288574}, {14.3379993438721,19.3790550231934,2.67489433288574}, {14.4549999237061,19.2770557403564,2.67489433288574}, {14.5899991989136,19.2040557861328,2.67489433288574},
+ {14.6079998016357,19.2040557861328,2.67489433288574}, {14.7209997177124,19.1600551605225,2.67489433288574}, {15.1379995346069,19.210054397583,2.67489433288574}, {14.9949998855591,18.2680549621582,2.67489433288574},
+ {15.0029993057251,19.1580543518066,2.67489433288574}, {15.2369995117188,19.2760543823242,2.67489433288574}, {15.3779993057251,19.4060554504395,2.67489433288574}, {15.4539995193481,19.520055770874,2.67489433288574},
+ {15.471999168396,19.52805519104,2.67489433288574}, {15.5449991226196,19.5830554962158,2.67489433288574}, {15.6529998779297,19.573055267334,2.67489433288574}, {15.7059993743896,17.8360557556152,2.67489433288574},
+ {15.9449996948242,18.5560550689697,2.67489433288574}, {15.8589992523193,18.9380550384521,2.67489433288574}, {14.9589996337891,18.2950553894043,2.67489433288574}, {15.7779998779297,19.5100555419922,2.67489433288574},
+ {14.0049991607666,20.2750549316406,2.67489433288574}, {12.3489990234375,20.5000553131104,2.67489433288574}, {13.0689992904663,19.0150547027588,2.67489433288574}, {13.0999994277954,19.0100555419922,2.67489433288574},
+ {15.9489994049072,19.3670558929443,9.41489505767822}, {15.9489994049072,19.2490558624268,9.41489505767822}, {15.75,17.8080558776855,9.41489505767822}, {15.6639995574951,19.5710544586182,9.41489505767822},
+ {15.5709991455078,17.9260559082031,9.41489505767822}, {15.8769998550415,18.690055847168,9.41489505767822}, {15.8499994277954,18.8170547485352,9.41489505767822}, {15.9459991455078,18.5520553588867,9.41489505767822},
+ {15.914999961853,17.6890544891357,9.41489505767822}, {15.3999996185303,19.4290542602539,9.41489505767822}, {15.3099994659424,19.339054107666,9.41489505767822}, {15.3729991912842,18.0440559387207,9.41489505767822},
+ {15.4579992294312,19.5170555114746,9.41489505767822}, {15.5469999313354,19.5820541381836,9.41489505767822}, {13.2309989929199,19.7610549926758,9.41489505767822}, {13.168999671936,19.7360553741455,9.41489505767822},
+ {13.096999168396,19.0140552520752,9.41489505767822}, {13.1999988555908,18.9870548248291,9.41489505767822}, {15.1399993896484,19.2080554962158,9.41489505767822}, {15.0159997940063,19.1600551605225,9.41489505767822},
+ {14.9859991073608,18.2770557403564,9.41489505767822}, {15.1749992370605,18.1690559387207,9.41489505767822}, {15.9039993286133,19.1320552825928,9.41489505767822}, {15.8949995040894,19.4460544586182,9.41489505767822},
+ {15.8769998550415,19.0420551300049,9.41489505767822}, {12.2169990539551,20.6500549316406,9.41489505767822}, {11.9379997253418,20.6810550689697,9.41489505767822}, {11.8629989624023,19.2130546569824,9.41489505767822}, {12.096999168396,19.1950550079346,9.41489505767822}, {14.1669998168945,18.6640548706055,9.41489505767822}, {14.1039991378784,20.2460556030273,9.41489505767822}, {13.9849996566772,18.7360553741455,9.41489505767822}, {14.7349996566772,19.1590557098389,9.41489505767822}, {14.5849990844727,19.2050552368164,9.41489505767822}, {14.5719995498657,18.4850559234619,9.41489505767822}, {14.1939992904663,19.6760559082031,9.41489505767822}, {14.1849994659424,19.9330558776855,9.41489505767822}, {14.1759996414185,18.6640548706055,9.41489505767822}, {14.261999130249,19.4890556335449,9.41489505767822}, {14.3539991378784,19.3610553741455,9.41489505767822}, {14.3559989929199,18.5830554962158,9.41489505767822}, {11.6039991378784,20.1250553131104,9.41489505767822}, {11.5209999084473,20.0520553588867,9.41489505767822}, {11.4209995269775,19.2480545043945,9.41489505767822}, {11.6989994049072,20.2690544128418,9.41389465332031}, {11.7609996795654,20.4310550689697,9.41489505767822}, {11.8359994888306,19.2130546569824,9.41489505767822}, {14.1889991760254,20.1710548400879,9.41489505767822}, {13.9689998626709,20.2840557098389,9.41489505767822}, {13.8739995956421,20.315055847168,9.41489505767822}, {13.7799997329712,18.8080558776855,9.41489505767822}, {13.9869995117188,20.2750549316406,9.41489505767822}, {12.3129997253418,20.5980548858643,9.41489505767822}, {12.3399991989136,20.5090560913086,9.41489505767822}, {12.3489990234375,20.3830547332764,9.41489505767822}, {12.3599996566772,20.2680549621582,9.41489505767822}, {12.3849992752075,20.1850547790527,9.41489505767822}, {12.3849992752075,20.1670551300049,9.41489505767822}, {12.4249992370605,20.065055847168,9.41489505767822}, {12.4729995727539,19.1350555419922,9.41489505767822}, {14.4399995803833,19.2900543212891,9.41489505767822}, {14.3649997711182,18.5740547180176,9.41489505767822}, {13.5729999542236,20.0310554504395,9.41489505767822}, {13.4889993667603,19.9140548706055,9.41489505767822}, {13.5639991760254,18.8710556030273,9.41489505767822}, {13.6389999389648,20.1310558319092,9.41489505767822}, {13.6719989776611,20.2130546569824,9.41489505767822}, {13.75,20.3020553588867,9.41489505767822}, {12.7399997711182,19.7810554504395,9.41489505767822}, {12.6189994812012,19.8520545959473,9.41489505767822}, {12.5799999237061,19.1200542449951,9.41489505767822}, {12.8349990844727,19.069055557251,9.41489505767822}, {11.2669992446899,19.9350547790527,9.41489505767822}, {11.1029987335205,19.9230556488037,9.41489505767822}, {11.0209999084473,19.2600555419922,9.41489505767822}, {11.3819999694824,19.9710559844971,9.41489505767822}, {13.418999671936,19.8530559539795,9.41489505767822}, {13.4329996109009,18.9160556793213,9.41489505767822}, {11.8399991989136,20.6430549621582,9.41489505767822}, {13.3119993209839,19.7800559997559,9.41489505767822}, {15.2189998626709,19.2600555419922,9.41489505767822}, {15.1839990615845,18.1600551605225,9.41489505767822}, {15.3639993667603,18.0520553588867,9.41489505767822}, {13.0189990997314,19.7250556945801,9.41489505767822}, {12.8949995040894,19.7350559234619,9.41489505767822}, {15.9039993286133,19.1500549316406,9.41489505767822}, {15.7699995040894,19.5140552520752,9.41489505767822}, {15.8589992523193,18.9340553283691,9.41489505767822}, {14.1939992904663,19.9510555267334,9.41489505767822}, {14.2119998931885,20.0630550384521,9.41489505767822}, {14.8589992523193,19.149055480957,9.41489505767822}, {14.8159999847412,18.3670558929443,9.41489505767822}, {14.8959999084473,18.3220558166504,9.41489505767822}, {12.5189990997314,19.9360542297363,9.41489505767822}, {11.0209999084473,19.9290542602539,9.41489505767822}, {11.0209999084473,19.2530555725098,2.67489433288574}, {11.0209999084473,19.9300556182861,2.67489433288574}, {15.9799995422363,18.505931854248,5.58724021911621}, {15.9799995422363,18.5044555664062,9.41489505767822}, {15.9799995422363,18.5041732788086,2.67489433288574}, {15.9799995422363,18.1684837341309,2.67489433288574}, {15.9799995422363,18.1288299560547,9.41489505767822}, {15.9799995422363,17.9876575469971,2.67489433288574}, {15.9799995422363,17.6247596740723,3.91620373725891}, {15.9799995422363,17.6247596740723,2.67489433288574}, {15.9799995422363,17.6254329681396,4.32245063781738}, {15.9799995422363,17.8920269012451,9.41489505767822}, {15.9799995422363,17.8795108795166,2.67489433288574}, {15.9799995422363,17.629810333252,4.58585262298584}, {15.9799995422363,17.6336059570312,5.27938556671143}, {15.9799995422363,17.8311748504639,2.67489433288574}, {15.9799995422363,17.638355255127,9.41489505767822}, {15.9799995422363,17.6346111297607,5.98653984069824}, {15.9799995422363,17.8728256225586,2.67489433288574}, {15.9799995422363,18.2221603393555,2.67489433288574} },
{ {0,1,2}, {0,3,1}, {0,4,3}, {5,6,7}, {8,6,5}, {2,9,0}, {6,10,11}, {12,13,14}, {15,16,17}, {18,16,15}, {19,20,21}, {22,23,24}, {25,23,22}, {26,23,25}, {27,23,26}, {28,23,27}, {29,30,31}, {29,32,30}, {29,28,32}, {33,28,29}, {33,23,28}, {21,23,33}, {20,23,21}, {34,35,36}, {37,35,34}, {38,35,37}, {39,35,38}, {40,35,39}, {41,35,40}, {42,35,41}, {43,35,42}, {44,35,43}, {45,35,44}, {46,35,45}, {47,35,46}, {48,35,47}, {49,50,51}, {52,48,47}, {23,49,24}, {53,54,55}, {56,57,58}, {59,57,56}, {60,57,59}, {61,57,60}, {62,57,61}, {63,57,62}, {64,57,63}, {65,57,64}, {66,57,65}, {13,57,66}, {54,67,55}, {68,69,70}, {71,69,68}, {72,69,71}, {73,69,72}, {74,69,73}, {75,69,74}, {76,69,75}, {77,69,76}, {67,69,77}, {70,16,68}, {70,17,16}, {78,79,80}, {81,79,78}, {82,79,81}, {83,79,82}, {84,79,83}, {85,79,84}, {86,79,85}, {87,79,86}, {88,8,5}, {11,7,6}, {11,89,7}, {11,9,89}, {11,0,9}, {55,90,53}, {55,79,90}, {55,80,79}, {91,11,10}, {92,69,12}, {92,70,69}, {34,93,37}, {47,94,52}, {47,95,94}, {47,57,95}, {47,58,57}, {51,24,49}, {21,35,19}, {21,36,35}, {14,92,12}, {86,10,87}, {86,91,10}, {77,55,67}, {66,14,13}, {96,97,4}, {98,99,100}, {101,102,98}, {103,101,98}, {104,103,98}, {105,106,107}, {108,105,107}, {109,108,107}, {100,109,107}, {110,111,112}, {113,110,112}, {114,115,116}, {117,114,116}, {118,119,120}, {121,122,123}, {124,121,123}, {125,126,127}, {128,129,130}, {131,132,133}, {71,131,133}, {134,71,133}, {135,134,133}, {136,135,133}, {137,138,139}, {140,137,139}, {141,140,139}, {142,31,141}, {142,141,139}, {143,126,132}, {144,145,146}, {147,144,146}, {127,147,146}, {148,121,124}, {149,148,124}, {150,149,124}, {151,150,124}, {152,151,124}, {153,152,124}, {154,153,124}, {155,154,124}, {129,156,157}, {130,129,157}, {158,159,160}, {161,158,160}, {162,161,160}, {163,162,160}, {146,163,160}, {164,165,166}, {167,164,166}, {168,169,170}, {171,168,170}, {139,171,170}, {159,172,173}, {123,174,142}, {175,110,113}, {173,175,113}, {106,176,177}, {178,106,177}, {179,180,167}, {112,179,167}, {175,173,172}, {119,118,181}, {119,181,97}, {119,97,96}, {182,98,102}, {182,102,183}, {182,183,120}, {182,120,119}, {143,132,184}, {184,185,143}, {147,127,126}, {174,123,122}, {159,173,160}, {126,125,133}, {126,133,132}, {186,187,188}, {186,188,116}, {186,116,115}, {99,98,182}, {109,100,99}, {106,178,107}, {114,117,177}, {114,177,176}, {128,130,187}, {128,187,186}, {135,136,157}, {135,157,156}, {163,146,145}, {164,167,180}, {179,112,111}, {171,139,138}, {189,155,166}, {189,166,165}, {149,150,93}, {154,155,189}, {31,142,174}, {114,176,78}, {81,78,176}, {7,89,183}, {89,9,120}, {89,120,183}, {78,80,114}, {176,106,81}, {88,5,103}, {183,102,7}, {118,120,9}, {9,2,181}, {9,181,118}, {115,114,80}, {82,81,106}, {101,103,5}, {102,101,5}, {5,7,102}, {97,181,2}, {2,1,97}, {1,3,97}, {80,55,115}, {172,159,59}, {59,56,172}, {3,4,97}, {4,0,96}, {105,108,82}, {186,115,55}, {82,106,105}, {83,82,108}, {60,59,159}, {175,172,56}, {119,96,0}, {0,11,119}, {108,109,84}, {84,83,108}, {55,77,186}, {56,58,110}, {56,110,175}, {60,159,158}, {11,91,182}, {182,119,11}, {91,86,182}, {85,84,109}, {86,85,99}, {128,186,77}, {58,111,110}, {158,161,60}, {26,25,137}, {138,137,25}, {99,182,86}, {109,99,85}, {77,76,128}, {58,47,111}, {61,60,161}, {137,140,26}, {27,26,140}, {25,22,138}, {129,128,76}, {76,75,129}, {75,74,129}, {74,73,156}, {73,72,135}, {68,16,184}, {68,184,132}, {16,18,185}, {161,162,62}, {62,61,161}, {179,111,47}, {171,138,22}, {156,129,74}, {135,156,73}, {134,135,72}, {72,71,134}, {68,132,131}, {185,184,16}, {18,15,185}, {63,62,162}, {28,27,140}, {22,24,171}, {71,68,131}, {15,17,143}, {15,143,185}, {17,70,143}, {70,92,126}, {162,163,64}, {64,63,162}, {180,179,47}, {47,46,180}, {140,141,28}, {168,171,24}, {126,143,70}, {92,14,147}, {147,126,92}, {14,66,144}, {14,144,147}, {65,64,163}, {66,65,145}, {46,45,180}, {32,28,141}, {24,51,168}, {145,144,66}, {163,145,65}, {164,180,45}, {45,44,164}, {44,43,164}, {43,42,165}, {38,37,151}, {150,151,37}, {37,93,150}, {141,31,30}, {30,32,141}, {169,168,51}, {165,164,43}, {189,165,42}, {42,41,189}, {40,39,152}, {40,152,153}, {151,152,39}, {39,38,151}, {93,34,149}, {154,189,41}, {153,154,41}, {41,40,153}, {148,149,34}, {34,36,148}, {36,21,121}, {31,174,29}, {121,148,36}, {21,33,122}, {21,122,121}, {33,29,122}, {174,122,29}, {116,188,53}, {104,98,10}, {87,10,98}, {98,100,87}, {79,87,100}, {79,100,107}, {90,79,107}, {90,107,178}, {178,177,90}, {53,90,177}, {53,177,117}, {117,116,53}, {54,53,188}, {54,188,187}, {67,54,187}, {67,187,130}, {69,67,130}, {69,130,157}, {12,69,157}, {12,157,136}, {136,133,12}, {12,133,125}, {125,127,12}, {13,12,127}, {127,146,13}, {57,13,146}, {57,146,160}, {95,57,160}, {95,160,173}, {173,113,95}, {94,95,113}, {113,112,94}, {52,94,112}, {48,52,112}, {112,167,48}, {35,48,167}, {35,167,166}, {19,35,166}, {139,170,50}, {50,49,139}, {166,155,19}, {20,19,155}, {155,124,20}, {23,20,124}, {23,124,123}, {49,23,123}, {49,123,142}, {142,139,49}, {190,191,170}, {192,191,190}, {191,192,51}, {191,51,50}, {170,169,190}, {169,51,192}, {169,192,190}, {170,191,50}, {193,194,195}, {196,197,198}, {199,200,201}, {198,202,203}, {204,201,200}, {205,204,200}, {206,207,208}, {206,208,205}, {206,205,200}, {207,206,209}, {207,209,203}, {207,203,202}, {202,198,197}, {197,196,210}, {197,210,195}, {197,195,194}, {8,88,195}, {8,195,210}, {210,196,8}, {196,198,8}, {198,203,8}, {203,209,8}, {209,206,8}, {206,200,8}, {202,197,104}, {207,202,104}, {103,104,197}, {103,197,194}, {193,195,88}, {88,103,194}, {88,194,193}, {200,199,8}, {199,201,8}, {204,205,6}, {6,8,201}, {6,201,204}, {10,6,205}, {10,205,208}, {104,10,208}, {104,208,207} });
break;
case TestMesh::pyramid:
@@ -108,22 +139,33 @@ TriangleMesh mesh(TestMesh m)
break;
case TestMesh::two_hollow_squares:
mesh = TriangleMesh(
- { {66.7133483886719,104.286666870117,0}, {66.7133483886719,95.7133331298828,0}, {65.6666870117188,94.6666717529297,0}, {75.2866821289062,95.7133331298828,0}, {76.3333435058594,105.333335876465,0}, {76.3333435058594,94.6666717529297,0}, {65.6666870117188,105.33332824707,0}, {75.2866821289062,104.286666870117,0}, {71.1066818237305,104.58666229248,2.79999995231628}, {66.4133529663086,104.58666229248,2.79999995231628}, {75.5866851806641,104.58666229248,2.79999995231628}, {66.4133529663086,99.8933334350586,2.79999995231628}, {66.4133529663086,95.4133377075195,2.79999995231628}, {71.1066818237305,95.4133377075195,2.79999995231628}, {75.5866851806641,95.4133377075195,2.79999995231628}, {75.5866851806641,100.106666564941,2.79999995231628}, {74.5400161743164,103.540000915527,2.79999995231628}, {70.0320129394531,103.540000915527,2.79999995231628}, {67.4600067138672,103.540000915527,2.79999995231628}, {67.4600067138672,100.968002319336,2.79999995231628}, {67.4600067138672,96.4599990844727,2.79999995231628}, {74.5400161743164,99.0319976806641,2.79999995231628}, {74.5400161743164,96.4599990844727,2.79999995231628}, {70.0320129394531,96.4599990844727,2.79999995231628}, {123.666717529297,94.6666717529297,0}, {134.333312988281,94.6666717529297,0}, {124.413360595703,95.4133377075195,2.79999995231628}, {129.106674194336,95.4133377075195,2.79999995231628}, {133.586669921875,95.4133377075195,2.79999995231628}, {123.666717529297,105.33332824707,0}, {124.413360595703,104.58666229248,2.79999995231628}, {124.413360595703,99.8933334350586,2.79999995231628}, {134.333312988281,105.33332824707,0}, {129.106674194336,104.58666229248,2.79999995231628}, {133.586669921875,104.58666229248,2.79999995231628}, {133.586669921875,100.106666564941,2.79999995231628}, {124.713317871094,104.286666870117,0}, {124.713317871094,95.7133331298828,0}, {133.286712646484,95.7133331298828,0}, {133.286712646484,104.286666870117,0}, {132.540023803711,103.540000915527,2.79999995231628}, {128.032028198242,103.540008544922,2.79999995231628}, {125.460006713867,103.540000915527,2.79999995231628}, {125.460006713867,100.968002319336,2.79999995231628}, {125.460006713867,96.4599990844727,2.79999995231628}, {132.540023803711,99.0319976806641,2.79999995231628}, {132.540023803711,96.4599990844727,2.79999995231628}, {128.032028198242,96.4599990844727,2.79999995231628} },
+ { {66.7133483886719f,104.286666870117f,0}, {66.7133483886719f,95.7133331298828f,0}, {65.6666870117188f,94.6666717529297f,0}, {75.2866821289062f,95.7133331298828f,0}, {76.3333435058594f,105.333335876465f,0},
+ {76.3333435058594f,94.6666717529297f,0}, {65.6666870117188f,105.33332824707f,0}, {75.2866821289062f,104.286666870117f,0}, {71.1066818237305f,104.58666229248f,2.79999995231628f}, {66.4133529663086f,104.58666229248f,2.79999995231628f},
+ {75.5866851806641f,104.58666229248f,2.79999995231628f}, {66.4133529663086f,99.8933334350586f,2.79999995231628f}, {66.4133529663086f,95.4133377075195f,2.79999995231628f}, {71.1066818237305f,95.4133377075195f,2.79999995231628f},
+ {75.5866851806641f,95.4133377075195f,2.79999995231628f}, {75.5866851806641f,100.106666564941f,2.79999995231628f}, {74.5400161743164f,103.540000915527f,2.79999995231628f}, {70.0320129394531f,103.540000915527f,2.79999995231628f},
+ {67.4600067138672f,103.540000915527f,2.79999995231628f}, {67.4600067138672f,100.968002319336f,2.79999995231628f}, {67.4600067138672f,96.4599990844727f,2.79999995231628f}, {74.5400161743164f,99.0319976806641f,2.79999995231628f},
+ {74.5400161743164f,96.4599990844727f,2.79999995231628f}, {70.0320129394531f,96.4599990844727f,2.79999995231628f}, {123.666717529297f,94.6666717529297f,0}, {134.333312988281f,94.6666717529297f,0}, {124.413360595703f,95.4133377075195f,2.79999995231628f},
+ {129.106674194336f,95.4133377075195f,2.79999995231628f}, {133.586669921875f,95.4133377075195f,2.79999995231628f}, {123.666717529297f,105.33332824707f,0}, {124.413360595703f,104.58666229248f,2.79999995231628f},
+ {124.413360595703f,99.8933334350586f,2.79999995231628f}, {134.333312988281f,105.33332824707f,0}, {129.106674194336f,104.58666229248f,2.79999995231628f}, {133.586669921875f,104.58666229248f,2.79999995231628f},
+ {133.586669921875f,100.106666564941f,2.79999995231628f}, {124.713317871094f,104.286666870117f,0}, {124.713317871094f,95.7133331298828f,0}, {133.286712646484f,95.7133331298828f,0}, {133.286712646484f,104.286666870117f,0},
+ {132.540023803711f,103.540000915527f,2.79999995231628f}, {128.032028198242f,103.540008544922f,2.79999995231628f}, {125.460006713867f,103.540000915527f,2.79999995231628f}, {125.460006713867f,100.968002319336f,2.79999995231628f},
+ {125.460006713867f,96.4599990844727f,2.79999995231628f}, {132.540023803711f,99.0319976806641f,2.79999995231628f}, {132.540023803711f,96.4599990844727f,2.79999995231628f}, {128.032028198242f,96.4599990844727f,2.79999995231628f} },
{ {0,1,2}, {3,4,5}, {6,4,0}, {6,0,2}, {2,1,5}, {7,4,3}, {1,3,5}, {0,4,7}, {4,6,8}, {6,9,8}, {4,8,10}, {6,2,9}, {2,11,9}, {2,12,11}, {2,5,12}, {5,13,12}, {5,14,13}, {4,10,15}, {5,4,14}, {4,15,14}, {7,16,17}, {0,7,18}, {7,17,18}, {1,19,20}, {1,0,19}, {0,18,19}, {7,3,21}, {3,22,21}, {7,21,16}, {3,23,22}, {3,1,23}, {1,20,23}, {24,25,26}, {25,27,26}, {25,28,27}, {29,24,30}, {24,31,30}, {24,26,31}, {32,29,33}, {29,30,33}, {32,33,34}, {32,34,35}, {25,32,28}, {32,35,28}, {36,37,24}, {38,32,25}, {29,32,36}, {29,36,24}, {24,37,25}, {39,32,38}, {37,38,25}, {36,32,39}, {39,40,41}, {36,39,42}, {39,41,42}, {37,43,44}, {37,36,43}, {36,42,43}, {39,38,45}, {38,46,45}, {39,45,40}, {38,47,46}, {38,37,47}, {37,44,47}, {16,8,9}, {16,10,8}, {10,16,15}, {15,16,21}, {22,15,21}, {15,22,14}, {22,23,14}, {23,20,14}, {17,16,9}, {18,17,9}, {19,18,9}, {19,9,11}, {19,11,20}, {13,14,20}, {20,11,12}, {13,20,12}, {41,40,30}, {42,41,30}, {43,42,30}, {43,30,31}, {43,31,44}, {27,28,44}, {44,31,26}, {27,44,26}, {40,33,30}, {40,34,33}, {34,40,35}, {35,40,45}, {46,35,45}, {35,46,28}, {46,47,28}, {47,44,28} });
break;
case TestMesh::small_dorito:
mesh = TriangleMesh(
- { {6.00058937072754,-22.9982089996338,0}, {22.0010242462158,-49.9998741149902,0}, {-9.99957847595215,-49.999870300293,0}, {6.00071382522583,-32.2371635437012,28.0019245147705}, {11.1670551300049,-37.9727020263672,18.9601669311523}, {6.00060224533081,-26.5392456054688,10.7321853637695} },
+ { {6.00058937072754f,-22.9982089996338f,0}, {22.0010242462158f,-49.9998741149902f,0}, {-9.99957847595215f,-49.999870300293f,0}, {6.00071382522583f,-32.2371635437012f,28.0019245147705f},
+ {11.1670551300049f,-37.9727020263672f,18.9601669311523f}, {6.00060224533081f,-26.5392456054688f,10.7321853637695f} },
{ {0,1,2}, {3,4,5}, {2,1,4}, {2,4,3}, {2,3,5}, {2,5,0}, {5,4,1}, {5,1,0} });
break;
case TestMesh::bridge:
mesh = TriangleMesh(
- { {75,84.5,8}, {125,84.5,8}, {75,94.5,8}, {120,84.5,5}, {125,94.5,8}, {75,84.5,0}, {80,84.5,5}, {125,84.5,0}, {125,94.5,0}, {80,94.5,5}, {75,94.5,0}, {120,94.5,5}, {120,84.5,0}, {80,94.5,0}, {80,84.5,0}, {120,94.5,0} },
+ { {75,84.5f,8}, {125,84.5f,8}, {75,94.5f,8}, {120,84.5f,5}, {125,94.5f,8}, {75,84.5f,0}, {80,84.5f,5}, {125,84.5f,0}, {125,94.5f,0}, {80,94.5f,5}, {75,94.5f,0}, {120,94.5f,5}, {120,84.5f,0}, {80,94.5f,0}, {80,84.5f,0}, {120,94.5f,0} },
{ {0,1,2}, {1,0,3}, {2,1,4}, {2,5,0}, {0,6,3}, {1,3,7}, {1,8,4}, {4,9,2}, {10,5,2}, {5,6,0}, {6,11,3}, {3,12,7}, {7,8,1}, {4,8,11}, {4,11,9}, {9,10,2}, {10,13,5}, {14,6,5}, {9,11,6}, {11,12,3}, {12,8,7}, {11,8,15}, {13,10,9}, {5,13,14}, {14,13,6}, {6,13,9}, {15,12,11}, {15,8,12} });
break;
case TestMesh::bridge_with_hole:
mesh = TriangleMesh(
- { {75,69.5,8}, {80,76.9091644287109,8}, {75,94.5,8}, {125,69.5,8}, {120,76.9091644287109,8}, {120,87.0908355712891,8}, {80,87.0908355712891,8}, {125,94.5,8}, {80,87.0908355712891,5}, {120,87.0908355712891,5}, {125,94.5,0}, {120,69.5,0}, {120,94.5,0}, {125,69.5,0}, {120,94.5,5}, {80,94.5,5}, {80,94.5,0}, {75,94.5,0}, {80,69.5,5}, {80,69.5,0}, {80,76.9091644287109,5}, {120,69.5,5}, {75,69.5,0}, {120,76.9091644287109,5} },
+ { {75,69.5f,8}, {80,76.9091644287109f,8}, {75,94.5f,8}, {125,69.5f,8}, {120,76.9091644287109f,8}, {120,87.0908355712891f,8}, {80,87.0908355712891f,8}, {125,94.5f,8}, {80,87.0908355712891f,5}, {120,87.0908355712891f,5}, {125,94.5f,0}, {120,69.5f,0}, {120,94.5f,0}, {125,69.5f,0}, {120,94.5f,5}, {80,94.5f,5}, {80,94.5f,0}, {75,94.5f,0}, {80,69.5f,5}, {80,69.5f,0}, {80,76.9091644287109f,5}, {120,69.5,5}, {75,69.5f,0}, {120,76.9091644287109f,5} },
{ {0,1,2}, {1,0,3}, {1,3,4}, {4,3,5}, {2,6,7}, {6,2,1}, {7,6,5}, {7,5,3}, {5,8,9}, {8,5,6}, {10,11,12}, {11,10,13}, {14,8,15}, {8,14,9}, {2,16,17}, {16,2,15}, {15,2,14}, {14,10,12}, {10,14,7}, {7,14,2}, {16,18,19}, {18,16,20}, {20,16,1}, {1,16,8}, {8,16,15}, {6,1,8}, {3,11,13}, {11,3,21}, {21,3,18}, {18,22,19}, {22,18,0}, {0,18,3}, {16,22,17}, {22,16,19}, {2,22,0}, {22,2,17}, {5,23,4}, {23,11,21}, {11,23,12}, {12,23,9}, {9,23,5}, {12,9,14}, {23,18,20}, {18,23,21}, {10,3,13}, {3,10,7}, {1,23,20}, {23,1,4} });
break;
case TestMesh::step:
@@ -133,7 +175,7 @@ TriangleMesh mesh(TestMesh m)
break;
case TestMesh::slopy_cube:
mesh = TriangleMesh(
- { {-10,-10,0}, {-10,-10,20}, {-10,10,0}, {-10,10,20}, {0,-10,10}, {10,-10,0}, {2.92893,-10,10}, {10,-10,2.92893}, {0,-10,20}, {10,10,0}, {0,10,10}, {0,10,20}, {2.92893,10,10}, {10,10,2.92893} },
+ { {-10,-10,0}, {-10,-10,20}, {-10,10,0}, {-10,10,20}, {0,-10,10}, {10,-10,0}, {2.92893f,-10,10}, {10,-10,2.92893f}, {0,-10,20}, {10,10,0}, {0,10,10}, {0,10,20}, {2.92893f,10,10}, {10,10,2.92893f} },
{ {0,1,2}, {2,1,3}, {4,0,5}, {4,1,0}, {6,4,7}, {7,4,5}, {4,8,1}, {0,2,5}, {5,2,9}, {2,10,9}, {10,3,11}, {2,3,10}, {9,10,12}, {13,9,12}, {3,1,8}, {11,3,8}, {10,11,8}, {4,10,8}, {6,12,10}, {4,6,10}, {7,13,12}, {6,7,12}, {7,5,9}, {13,7,9} });
break;
default:
@@ -141,7 +183,6 @@ TriangleMesh mesh(TestMesh m)
break;
}
- mesh.repair();
return mesh;
}
diff --git a/tests/fff_print/test_trianglemesh.cpp b/tests/fff_print/test_trianglemesh.cpp
index fa6237c8b..6faaf1584 100644
--- a/tests/fff_print/test_trianglemesh.cpp
+++ b/tests/fff_print/test_trianglemesh.cpp
@@ -17,12 +17,13 @@
using namespace Slic3r;
using namespace std;
+static inline TriangleMesh make_cube() { return make_cube(20., 20, 20); }
+
SCENARIO( "TriangleMesh: Basic mesh statistics") {
GIVEN( "A 20mm cube, built from constexpr std::array" ) {
- std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
+ std::vector<Vec3f> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
- TriangleMesh cube(vertices, facets);
- cube.repair();
+ TriangleMesh cube(vertices, facets);
THEN( "Volume is appropriate for 20mm square cube.") {
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
@@ -68,64 +69,11 @@ SCENARIO( "TriangleMesh: Basic mesh statistics") {
}
}
- GIVEN( "A 20mm cube with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
-
- TriangleMesh cube(vertices, facets);
- cube.repair();
-
- THEN( "Volume is appropriate for 20mm square cube.") {
- REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
- }
-
- THEN( "Vertices array matches input.") {
- for (size_t i = 0U; i < cube.its.vertices.size(); i++) {
- REQUIRE(cube.its.vertices.at(i) == vertices.at(i).cast<float>());
- }
- for (size_t i = 0U; i < vertices.size(); i++) {
- REQUIRE(vertices.at(i).cast<float>() == cube.its.vertices.at(i));
- }
- }
- THEN( "Vertex count matches vertex array size.") {
- REQUIRE(cube.facets_count() == facets.size());
- }
-
- THEN( "Facet array matches input.") {
- for (size_t i = 0U; i < cube.its.indices.size(); i++) {
- REQUIRE(cube.its.indices.at(i) == facets.at(i));
- }
-
- for (size_t i = 0U; i < facets.size(); i++) {
- REQUIRE(facets.at(i) == cube.its.indices.at(i));
- }
- }
- THEN( "Facet count matches facet array size.") {
- REQUIRE(cube.facets_count() == facets.size());
- }
-
-#if 0
- THEN( "Number of normals is equal to the number of facets.") {
- REQUIRE(cube.normals().size() == facets.size());
- }
-#endif
-
- THEN( "center() returns the center of the object.") {
- REQUIRE(cube.center() == Vec3d(10.0,10.0,10.0));
- }
-
- THEN( "Size of cube is (20,20,20)") {
- REQUIRE(cube.size() == Vec3d(20,20,20));
- }
- }
}
SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
GIVEN( "A 20mm cube with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
- TriangleMesh cube(vertices, facets);
- cube.repair();
+ auto cube = make_cube();
WHEN( "The cube is scaled 200% uniformly") {
cube.scale(2.0);
@@ -134,7 +82,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
}
}
WHEN( "The resulting cube is scaled 200% in the X direction") {
- cube.scale(Vec3d(2.0, 1, 1));
+ cube.scale(Vec3f(2.0, 1, 1));
THEN( "The volume is doubled.") {
REQUIRE(abs(cube.volume() - 2*20.0*20.0*20.0) < 1e-2);
}
@@ -144,7 +92,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
}
WHEN( "The cube is scaled 25% in the X direction") {
- cube.scale(Vec3d(0.25, 1, 1));
+ cube.scale(Vec3f(0.25, 1, 1));
THEN( "The volume is 25% of the previous volume.") {
REQUIRE(abs(cube.volume() - 0.25*20.0*20.0*20.0) < 1e-2);
}
@@ -177,7 +125,10 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
cube.translate(5.0, 10.0, 0.0);
cube.align_to_origin();
THEN( "The third vertex is located at 0,0,0") {
- REQUIRE(cube.its.vertices.at(2) == Vec3f(0.0, 0.0, 0.0));
+ REQUIRE(cube.its.vertices.at(2) == Vec3f::Zero());
+ }
+ THEN( "Size is OK") {
+ REQUIRE(cube.stats().size == Vec3f(20.f, 20.f, 20.f));
}
}
}
@@ -185,11 +136,8 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
SCENARIO( "TriangleMesh: slice behavior.") {
GIVEN( "A 20mm cube with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
- TriangleMesh cube(vertices, facets);
- cube.repair();
-
+ auto cube = make_cube();
+
WHEN("Cube is sliced with z = [0+EPSILON,2,4,8,6,8,10,12,14,16,18,20]") {
std::vector<double> z { 0+EPSILON,2,4,8,6,8,10,12,14,16,18,20 };
std::vector<ExPolygons> result = cube.slice(z);
@@ -206,12 +154,12 @@ SCENARIO( "TriangleMesh: slice behavior.") {
}
}
GIVEN( "A STL with an irregular shape.") {
- const std::vector<Vec3d> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
+ const std::vector<Vec3f> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
const std::vector<Vec3i> facets {{0,1,2},{2,1,3},{1,0,4},{5,1,4},{0,2,4},{4,2,6},{7,6,8},{4,6,7},{9,4,7},{7,8,10},{2,3,6},{11,3,12},{7,12,9},{13,12,7},{6,3,11},{11,12,13},{3,1,5},{12,3,5},{5,4,9},{12,5,9},{13,7,10},{14,13,10},{8,15,10},{10,15,14},{6,11,8},{8,11,15},{15,11,13},{14,15,13}};
- TriangleMesh cube(vertices, facets);
- cube.repair();
+ auto cube = make_cube();
WHEN(" a top tangent plane is sliced") {
+ // At Z = 10 we have a top horizontal surface.
std::vector<ExPolygons> slices = cube.slice({5.0, 10.0});
THEN( "its area is included") {
REQUIRE(slices.at(0).at(0).area() > 0);
@@ -240,9 +188,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
THEN("The mesh volume is 20*20*20") {
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
}
- THEN("The resulting mesh is in the repaired state.") {
- REQUIRE(cube.repaired == true);
- }
THEN("There are 12 facets.") {
REQUIRE(cube.its.indices.size() == 12);
}
@@ -266,9 +211,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
THEN("Resulting mesh has 2*PI/angle * 4 facets") {
REQUIRE(cyl.its.indices.size() == (2*PI/angle)*4);
}
- THEN("The resulting mesh is in the repaired state.") {
- REQUIRE(cyl.repaired == true);
- }
THEN( "The mesh volume is approximately 10pi * 10^2") {
REQUIRE(abs(cyl.volume() - (10.0 * M_PI * std::pow(10,2))) < 1);
}
@@ -283,9 +225,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, 10.f)); } ) == 1);
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, -10.f)); } ) == 1);
}
- THEN("The resulting mesh is in the repaired state.") {
- REQUIRE(sph.repaired == true);
- }
THEN( "The mesh volume is approximately 4/3 * pi * 10^3") {
REQUIRE(abs(sph.volume() - (4.0/3.0 * M_PI * std::pow(10,3))) < 1); // 1% tolerance?
}
@@ -295,32 +234,25 @@ SCENARIO( "make_xxx functions produce meshes.") {
SCENARIO( "TriangleMesh: split functionality.") {
GIVEN( "A 20mm cube with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
-
- TriangleMesh cube(vertices, facets);
- cube.repair();
+ auto cube = make_cube();
WHEN( "The mesh is split into its component parts.") {
- std::vector<TriangleMesh*> meshes = cube.split();
+ std::vector<TriangleMesh> meshes = cube.split();
THEN(" The bounding box statistics are propagated to the split copies") {
REQUIRE(meshes.size() == 1);
- REQUIRE((meshes.at(0)->bounding_box() == cube.bounding_box()));
+ REQUIRE((meshes.front().bounding_box() == cube.bounding_box()));
}
}
}
GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
-
- TriangleMesh cube(vertices, facets);
- cube.repair();
- TriangleMesh cube2(vertices, facets);
- cube2.repair();
+ auto cube = make_cube();
+ TriangleMesh cube2(cube);
cube.merge(cube2);
- cube.repair();
WHEN( "The combined mesh is split") {
- std::vector<TriangleMesh*> meshes = cube.split();
+ THEN( "Number of faces is 2x the source.") {
+ REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
+ }
+ std::vector<TriangleMesh> meshes = cube.split();
THEN( "Two meshes are in the output vector.") {
REQUIRE(meshes.size() == 2);
}
@@ -330,19 +262,13 @@ SCENARIO( "TriangleMesh: split functionality.") {
SCENARIO( "TriangleMesh: Mesh merge functions") {
GIVEN( "Two 20mm cubes, each with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
-
- TriangleMesh cube(vertices, facets);
- cube.repair();
- TriangleMesh cube2(vertices, facets);
- cube2.repair();
+ auto cube = make_cube();
+ TriangleMesh cube2(cube);
WHEN( "The two meshes are merged") {
cube.merge(cube2);
- cube.repair();
THEN( "There are twice as many facets in the merged mesh as the original.") {
- REQUIRE(cube.stl.stats.number_of_facets == 2 * cube2.stl.stats.number_of_facets);
+ REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
}
}
}
@@ -350,11 +276,7 @@ SCENARIO( "TriangleMesh: Mesh merge functions") {
SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
GIVEN( "A 20mm cube with one corner on the origin") {
- const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
- const std::vector<Vec3i> facets { {0,1,2}, {0,2,3}, {4,5,6}, {4,6,7}, {0,4,7}, {0,7,1}, {1,7,6}, {1,6,2}, {2,6,5}, {2,5,3}, {4,0,3}, {4,3,5} };
-
- TriangleMesh cube(vertices, facets);
- cube.repair();
+ auto cube = make_cube();
WHEN( "Object is cut at the bottom") {
indexed_triangle_set upper {};
indexed_triangle_set lower {};
@@ -384,7 +306,6 @@ TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
TriangleMesh mesh;
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/100_000.stl");
- mesh.repair();
config.set("layer_height", 500);
config.set("first_layer_height", 250);
@@ -412,7 +333,6 @@ TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
TriangleMesh mesh;
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/10_000.stl");
- mesh.repair();
config.set("layer_height", 500);
config.set("first_layer_height", 250);
diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp
index 5ab000d04..0ebe47a07 100644
--- a/tests/libslic3r/test_3mf.cpp
+++ b/tests/libslic3r/test_3mf.cpp
@@ -65,10 +65,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
// compare meshes
TriangleMesh src_mesh = src_model.mesh();
- src_mesh.repair();
-
TriangleMesh dst_mesh = dst_model.mesh();
- dst_mesh.repair();
bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size();
if (res) {
diff --git a/tests/libslic3r/test_aabbindirect.cpp b/tests/libslic3r/test_aabbindirect.cpp
index c0792a943..3b834c442 100644
--- a/tests/libslic3r/test_aabbindirect.cpp
+++ b/tests/libslic3r/test_aabbindirect.cpp
@@ -9,7 +9,6 @@ using namespace Slic3r;
TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]")
{
TriangleMesh tmesh = make_cube(1., 1., 1.);
- tmesh.repair();
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(tmesh.its.vertices, tmesh.its.indices);
REQUIRE(! tree.empty());
diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp
index bbf76ea18..3cbd21a40 100644
--- a/tests/libslic3r/test_clipper_utils.cpp
+++ b/tests/libslic3r/test_clipper_utils.cpp
@@ -99,7 +99,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
- Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
+ Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1);
}
@@ -117,7 +117,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
GIVEN("Clipper bug #126") {
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
- Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
+ Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1);
}
diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp
index 97729ac8e..213105084 100644
--- a/tests/libslic3r/test_config.cpp
+++ b/tests/libslic3r/test_config.cpp
@@ -3,6 +3,11 @@
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/LocalesUtils.hpp"
+#include <cereal/types/polymorphic.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/archives/binary.hpp>
+
using namespace Slic3r;
SCENARIO("Generic config validation performs as expected.", "[Config]") {
@@ -202,3 +207,33 @@ SCENARIO("Config ini load/save interface", "[Config]") {
}
}
}
+
+SCENARIO("DynamicPrintConfig serialization", "[Config]") {
+ WHEN("DynamicPrintConfig is serialized and deserialized") {
+ FullPrintConfig full_print_config;
+ DynamicPrintConfig cfg;
+ cfg.apply(full_print_config, false);
+
+ std::string serialized;
+ try {
+ std::ostringstream ss;
+ cereal::BinaryOutputArchive oarchive(ss);
+ oarchive(cfg);
+ serialized = ss.str();
+ } catch (std::runtime_error e) {
+ e.what();
+ }
+
+ THEN("Config object contains ini file options.") {
+ DynamicPrintConfig cfg2;
+ try {
+ std::stringstream ss(serialized);
+ cereal::BinaryInputArchive iarchive(ss);
+ iarchive(cfg2);
+ } catch (std::runtime_error e) {
+ e.what();
+ }
+ REQUIRE(cfg == cfg2);
+ }
+ }
+}
diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp
index 1f5ca3845..ad4f8f296 100644
--- a/tests/libslic3r/test_hollowing.cpp
+++ b/tests/libslic3r/test_hollowing.cpp
@@ -13,7 +13,6 @@ TEST_CASE("Hollow two overlapping spheres") {
sphere2.translate( 5.f, 0.f, 0.f);
sphere1.merge(sphere2);
- sphere1.require_shared_vertices();
sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles);
diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp
index a0b27f6d8..3553697ac 100644
--- a/tests/libslic3r/test_marchingsquares.cpp
+++ b/tests/libslic3r/test_marchingsquares.cpp
@@ -319,7 +319,6 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
mesh.translate(tr.x(), tr.y(), tr.z());
bb = mesh.bounding_box();
- assert(mesh.has_shared_vertices());
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
sla::RasterBase::Resolution res{2560, 1440};
diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp
index d26d6e2a2..db8c5e93e 100644
--- a/tests/sla_print/sla_print_tests.cpp
+++ b/tests/sla_print/sla_print_tests.cpp
@@ -57,7 +57,6 @@ TEST_CASE("Support point generator should be deterministic if seeded",
auto layer_h = 0.05f;
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
- assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, slicegrid, CLOSING_RADIUS);
point_gen.seed(0);
diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp
index b56909280..a69e09412 100644
--- a/tests/sla_print/sla_raycast_tests.cpp
+++ b/tests/sla_print/sla_raycast_tests.cpp
@@ -63,7 +63,6 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]")
sla::DrainHoles holes = { sla::DrainHole{p, normal, radius, hole_length} };
cube.merge(*cube_inside);
- cube.require_shared_vertices();
sla::IndexedMesh emesh{cube};
emesh.load_holes(holes);
diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp
index e160504de..f2cf7e834 100644
--- a/tests/sla_print/sla_supptgen_tests.cpp
+++ b/tests/sla_print/sla_supptgen_tests.cpp
@@ -13,7 +13,6 @@ TEST_CASE("Overhanging point should be supported", "[SupGen]") {
// Pyramid with 45 deg slope
TriangleMesh mesh = make_pyramid(10.f, 10.f);
mesh.rotate_y(float(PI));
- mesh.require_shared_vertices();
mesh.WriteOBJFile("Pyramid.obj");
sla::SupportPoints pts = calc_support_pts(mesh);
@@ -56,7 +55,6 @@ TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") {
TriangleMesh mesh = make_cube(width, depth, height);
mesh.translate(0., 0., 5.); // lift up
- mesh.require_shared_vertices();
mesh.WriteOBJFile("Cuboid.obj");
sla::SupportPointGenerator::Config cfg;
@@ -83,7 +81,6 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") {
TriangleMesh mesh = make_prism(width, depth, height);
mesh.rotate_y(float(PI)); // rotate on its back
mesh.translate(0., 0., height);
- mesh.require_shared_vertices();
mesh.WriteOBJFile("Prism.obj");
sla::SupportPointGenerator::Config cfg;
@@ -115,7 +112,6 @@ TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowe
auto h = float(bb.max.z() - bb.min.z());
Vec3f mv = bb.center().cast<float>() - Vec3f{0.f, 0.f, 0.5f * h};
mesh.translate(-mv);
- mesh.require_shared_vertices();
sla::SupportPointGenerator::Config cfg;
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
@@ -132,7 +128,6 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]")
TriangleMesh mesh_high = center_around_bb(make_cube(width, depth, height));
mesh_high.translate(0., 0., 10.); // lift up
mesh.merge(mesh_high);
- mesh.require_shared_vertices();
mesh.WriteOBJFile("parallel_plates.obj");
diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp
index be9bf9741..1082df200 100644
--- a/tests/sla_print/sla_test_utils.cpp
+++ b/tests/sla_print/sla_test_utils.cpp
@@ -74,8 +74,6 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices, const Sup
byproducts.supporttree.retrieve_full_mesh(its);
TriangleMesh m{its};
m.merge(byproducts.input_mesh);
- m.repair();
- m.require_shared_vertices();
m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" +
byproducts.obj_fname).c_str());
}
@@ -95,7 +93,6 @@ void test_supports(const std::string &obj_filename,
sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg);
REQUIRE(interior);
mesh.merge(TriangleMesh{sla::get_mesh(*interior)});
- mesh.require_shared_vertices();
}
auto bb = mesh.bounding_box();
@@ -105,7 +102,6 @@ void test_supports(const std::string &obj_filename,
auto layer_h = 0.05f;
out.slicegrid = grid(float(gnd), float(zmax), layer_h);
- assert(mesh.has_shared_vertices());
out.model_slices = slice_mesh_ex(mesh.its, out.slicegrid, CLOSING_RADIUS);
sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{});
@@ -283,8 +279,10 @@ void test_concave_hull(const ExPolygons &polys) {
_test_concave_hull(waffl, polys);
}
+//FIXME this functionality is gone after TriangleMesh refactoring to get rid of admesh.
void check_validity(const TriangleMesh &input_mesh, int flags)
{
+ /*
TriangleMesh mesh{input_mesh};
if (flags & ASSUME_NO_EMPTY) {
@@ -292,20 +290,18 @@ void check_validity(const TriangleMesh &input_mesh, int flags)
} else if (mesh.empty())
return; // If it can be empty and it is, there is nothing left to do.
- REQUIRE(stl_validate(&mesh.stl));
-
bool do_update_shared_vertices = false;
mesh.repair(do_update_shared_vertices);
if (flags & ASSUME_NO_REPAIR) {
- REQUIRE_FALSE(mesh.needed_repair());
+ REQUIRE_FALSE(mesh.repaired());
}
if (flags & ASSUME_MANIFOLD) {
- mesh.require_shared_vertices();
if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj");
REQUIRE(mesh.is_manifold());
}
+ */
}
void check_raster_transformations(sla::RasterBase::Orientation o, sla::RasterBase::TMirroring mirroring)
@@ -420,53 +416,6 @@ double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd)
return error;
}
-
-// Make a 3D pyramid
-TriangleMesh make_pyramid(float base, float height)
-{
- float a = base / 2.f;
-
- TriangleMesh mesh(
- {
- {-a, -a, 0}, {a, -a, 0}, {a, a, 0},
- {-a, a, 0}, {0.f, 0.f, height}
- },
- {
- {0, 1, 2},
- {0, 2, 3},
- {0, 1, 4},
- {1, 2, 4},
- {2, 3, 4},
- {3, 0, 4}
- });
-
- mesh.repair();
-
- return mesh;
-}
-
- TriangleMesh make_prism(double width, double length, double height)
-{
- // We need two upward facing triangles
-
- double x = width / 2., y = length / 2.;
-
- TriangleMesh mesh(
- {
- {-x, -y, 0.}, {x, -y, 0.}, {0., -y, height},
- {-x, y, 0.}, {x, y, 0.}, {0., y, height},
- },
- {
- {0, 1, 2}, // side 1
- {4, 3, 5}, // side 2
- {1, 4, 2}, {2, 4, 5}, // roof 1
- {0, 2, 5}, {0, 5, 3}, // roof 2
- {3, 4, 1}, {3, 1, 0} // bottom
- });
-
- return mesh;
-}
-
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg)
@@ -474,7 +423,6 @@ sla::SupportPoints calc_support_pts(
// Prepare the slice grid and the slices
auto bb = cast<float>(mesh.bounding_box());
std::vector<float> heights = grid(bb.min.z(), bb.max.z(), 0.1f);
- assert(mesh.has_shared_vertices());
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, heights, CLOSING_RADIUS);
// Prepare the support point calculator
diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp
index d10a85b25..2264ad856 100644
--- a/tests/sla_print/sla_test_utils.hpp
+++ b/tests/sla_print/sla_test_utils.hpp
@@ -185,11 +185,6 @@ long raster_pxsum(const sla::RasterGrayscaleAA &raster);
double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd);
-// Make a 3D pyramid
-TriangleMesh make_pyramid(float base, float height);
-
-TriangleMesh make_prism(double width, double length, double height);
-
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg = {});
diff --git a/version.inc b/version.inc
index d981d620a..b7e941bb4 100644
--- a/version.inc
+++ b/version.inc
@@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
-set(SLIC3R_VERSION "2.4.0-alpha1")
+set(SLIC3R_VERSION "2.4.0-alpha2")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,4,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")
diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t
index 4013a1f83..453cc9218 100644
--- a/xs/t/01_trianglemesh.t
+++ b/xs/t/01_trianglemesh.t
@@ -4,10 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
-use Test::More tests => 49;
-
-is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
- 'hello world';
+use Test::More tests => 5;
my $cube = {
vertices => [ [20,20,0], [20,0,0], [0,0,0], [0,20,0], [20,20,20], [0,20,20], [0,0,20], [20,0,20] ],
@@ -17,12 +14,10 @@ my $cube = {
{
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
- $m->repair;
my ($vertices, $facets) = ($m->vertices, $m->facets);
is_deeply $vertices, $cube->{vertices}, 'vertices arrayref roundtrip';
is_deeply $facets, $cube->{facets}, 'facets arrayref roundtrip';
- is scalar(@{$m->normals}), scalar(@$facets), 'normals returns the right number of items';
{
my $m2 = $m->clone;
@@ -34,109 +29,6 @@ my $cube = {
{
my $stats = $m->stats;
is $stats->{number_of_facets}, scalar(@{ $cube->{facets} }), 'stats.number_of_facets';
- ok abs($stats->{volume} - 20*20*20) < 1E-2, 'stats.volume';
- }
-
- $m->scale(2);
- ok abs($m->stats->{volume} - 40*40*40) < 1E-2, 'scale';
-
- $m->scale_xyz(Slic3r::Pointf3->new(2,1,1));
- ok abs($m->stats->{volume} - 2*40*40*40) < 1E-2, 'scale_xyz';
-
- $m->translate(5,10,0);
- is_deeply $m->vertices->[0], [85,50,0], 'translate';
-
- $m->align_to_origin;
- is_deeply $m->vertices->[2], [0,0,0], 'align_to_origin';
-
- is_deeply $m->size, [80,40,40], 'size';
-
- $m->scale_xyz(Slic3r::Pointf3->new(0.5,1,1));
- $m->rotate(45, Slic3r::Point->new(20,20));
- ok abs($m->size->[0] - sqrt(2)*40) < 1E-4, 'rotate';
-
- {
- my $meshes = $m->split;
- is scalar(@$meshes), 1, 'split';
- isa_ok $meshes->[0], 'Slic3r::TriangleMesh', 'split';
- is_deeply $m->bb3, $meshes->[0]->bb3, 'split populates stats';
- }
-
- my $m2 = Slic3r::TriangleMesh->new;
- $m2->ReadFromPerl($cube->{vertices}, $cube->{facets});
- $m2->repair;
- $m->merge($m2);
- $m->repair;
- is $m->stats->{number_of_facets}, 2 * $m2->stats->{number_of_facets}, 'merge';
-
- {
- my $meshes = $m->split;
- is scalar(@$meshes), 2, 'split';
- }
-}
-
-{
- my $m = Slic3r::TriangleMesh->new;
- $m->ReadFromPerl($cube->{vertices}, $cube->{facets});
- $m->repair;
- # The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
- # open intervals at the bottom end, closed at the top end.
- my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20);
- my $result = $m->slice(\@z);
- my $SCALING_FACTOR = 0.000001;
- for my $i (0..$#z) {
- is scalar(@{$result->[$i]}), 1, "number of returned polygons per layer (z = " . $z[$i] . ")";
- is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon';
- }
-}
-
-{
- my $m = Slic3r::TriangleMesh->new;
- $m->ReadFromPerl(
- [ [0,0,0],[0,0,20],[0,5,0],[0,5,20],[50,0,0],[50,0,20],[15,5,0],[35,5,0],[15,20,0],[50,5,0],[35,20,0],[15,5,10],[50,5,20],[35,5,10],[35,20,10],[15,20,10] ],
- [ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
- );
- $m->repair;
- {
- # at Z = 10 we have a top horizontal surface
- my $slices = $m->slice([ 5, 10 ]);
- is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area';
- }
- $m->mirror_z;
- {
- # this second test also checks that performing a second slice on a mesh after
- # a transformation works properly (shared_vertices is correctly invalidated);
- # at Z = -10 we have a bottom horizontal surface
- # (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
- # open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice).
- my $slices = $m->slice([ -5, -10+0.00001 ]);
- is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
- }
-}
-
-{
- my $m = Slic3r::TriangleMesh->new;
- $m->ReadFromPerl($cube->{vertices}, $cube->{facets});
- $m->repair;
- {
- my $upper = Slic3r::TriangleMesh->new;
- my $lower = Slic3r::TriangleMesh->new;
- $m->cut(0, $upper, $lower);
- $upper->repair; $lower->repair;
- is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane';
- is $lower->facets_count, 0, 'lower mesh has no facets';
- }
- {
- my $upper = Slic3r::TriangleMesh->new;
- my $lower = Slic3r::TriangleMesh->new;
- $m->cut(10, $upper, $lower);
- #$upper->repair; $lower->repair;
- # we expect:
- # 2 facets on external horizontal surfaces
- # 3 facets on each side = 12 facets
- # 6 facets on the triangulated side (8 vertices)
- is $upper->facets_count, 2+12+6, 'upper mesh has the expected number of facets';
- is $lower->facets_count, 2+12+6, 'lower mesh has the expected number of facets';
}
}
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index e7a171efd..34795681e 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -10,7 +10,6 @@
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "libslic3r/Format/OBJ.hpp"
-#include "libslic3r/Format/PRUS.hpp"
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/PresetBundle.hpp"
%}
@@ -89,8 +88,6 @@
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
- void print_info() const;
-
bool store_stl(char *path, bool binary)
%code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %};
@@ -213,7 +210,6 @@ ModelMaterial::attributes()
%code%{ THIS->origin_translation = *point; %};
void ensure_on_bed();
- bool needed_repair() const;
int materials_count() const;
int facets_count();
void center_around_origin();
@@ -224,13 +220,6 @@ ModelMaterial::attributes()
%code{% THIS->rotate(angle, *axis); %};
void mirror(Axis axis);
- ModelObjectPtrs* split_object()
- %code%{
- RETVAL = new ModelObjectPtrs(); // leak?
- THIS->split(RETVAL);
- %};
-
- void print_info() const;
};
diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp
index 2b07c78ee..105fe0f05 100644
--- a/xs/xsp/TriangleMesh.xsp
+++ b/xs/xsp/TriangleMesh.xsp
@@ -11,14 +11,11 @@
~TriangleMesh();
Clone<TriangleMesh> clone()
%code{% RETVAL = THIS; %};
- void ReadSTLFile(char* input_file);
void write_ascii(char* output_file);
void write_binary(char* output_file);
- void repair();
- void WriteOBJFile(char* output_file);
void scale(float factor);
void scale_xyz(Vec3d* versor)
- %code{% THIS->scale(*versor); %};
+ %code{% THIS->scale(versor->cast<float>()); %};
void translate(float x, float y, float z);
void rotate_x(float angle);
void rotate_y(float angle);
@@ -28,16 +25,13 @@
void mirror_z();
void align_to_origin();
void rotate(double angle, Point* center);
- TriangleMeshPtrs split();
void merge(TriangleMesh* mesh)
%code{% THIS->merge(*mesh); %};
- ExPolygons horizontal_projection();
Clone<Polygon> convex_hull();
Clone<BoundingBoxf3> bounding_box();
Clone<Vec3d> center()
%code{% RETVAL = THIS->bounding_box().center(); %};
int facets_count();
- void reset_repair_stats();
%{
@@ -46,51 +40,40 @@ TriangleMesh::ReadFromPerl(vertices, facets)
SV* vertices
SV* facets
CODE:
- stl_file &stl = THIS->stl;
- stl.stats.type = inmemory;
-
- // count facets and allocate memory
- AV* facets_av = (AV*)SvRV(facets);
- stl.stats.number_of_facets = av_len(facets_av)+1;
- stl.stats.original_num_facets = stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- // read geometry
- AV* vertices_av = (AV*)SvRV(vertices);
- for (int i = 0; i < stl.stats.number_of_facets; i++) {
- AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0));
- stl_facet facet;
- facet.normal(0) = 0;
- facet.normal(1) = 0;
- facet.normal(2) = 0;
- for (unsigned int v = 0; v <= 2; v++) {
- AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0));
- facet.vertex[v](0) = SvNV(*av_fetch(vertex_av, 0, 0));
- facet.vertex[v](1) = SvNV(*av_fetch(vertex_av, 1, 0));
- facet.vertex[v](2) = SvNV(*av_fetch(vertex_av, 2, 0));
+ std::vector<Slic3r::Vec3f> out_vertices;
+ {
+ AV* vertices_av = (AV*)SvRV(vertices);
+ int number_of_vertices = av_len(vertices_av) + 1;
+ out_vertices.reserve(number_of_vertices);
+ for (int i = 0; i < number_of_vertices; ++ i) {
+ AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, i, 0));
+ out_vertices.push_back(Slic3r::Vec3f(SvNV(*av_fetch(vertex_av, 0, 0)), SvNV(*av_fetch(vertex_av, 1, 0)), SvNV(*av_fetch(vertex_av, 2, 0))));
+ }
+ }
+ std::vector<Slic3r::Vec3i> out_indices;
+ {
+ AV* facets_av = (AV*)SvRV(facets);
+ int number_of_facets = av_len(facets_av) + 1;
+ out_indices.reserve(number_of_facets);
+ for (int i = 0; i < number_of_facets; ++ i) {
+ AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0));
+ out_indices.push_back(Slic3r::Vec3i(SvIV(*av_fetch(facet_av, 0, 0)), SvIV(*av_fetch(facet_av, 1, 0)), SvIV(*av_fetch(facet_av, 2, 0))));
}
- facet.extra[0] = 0;
- facet.extra[1] = 0;
-
- stl.facet_start[i] = facet;
}
-
- stl_get_size(&stl);
+ *THIS = TriangleMesh(std::move(out_vertices), std::move(out_indices));
SV*
TriangleMesh::stats()
CODE:
HV* hv = newHV();
- (void)hv_stores( hv, "number_of_facets", newSViv(THIS->stl.stats.number_of_facets) );
- (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stl.stats.number_of_parts) );
- (void)hv_stores( hv, "volume", newSVnv(THIS->stl.stats.volume) );
- (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stl.stats.degenerate_facets) );
- (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stl.stats.edges_fixed) );
- (void)hv_stores( hv, "facets_removed", newSViv(THIS->stl.stats.facets_removed) );
- (void)hv_stores( hv, "facets_added", newSViv(THIS->stl.stats.facets_added) );
- (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) );
- (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stl.stats.backwards_edges) );
- (void)hv_stores( hv, "normals_fixed", newSViv(THIS->stl.stats.normals_fixed) );
+ (void)hv_stores( hv, "number_of_facets", newSViv(THIS->facets_count()) );
+ (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stats().number_of_parts) );
+ (void)hv_stores( hv, "volume", newSVnv(THIS->stats().volume) );
+ (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stats().degenerate_facets) );
+ (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stats().edges_fixed) );
+ (void)hv_stores( hv, "facets_removed", newSViv(THIS->stats().facets_removed) );
+ (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stats().facets_reversed) );
+ (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stats().backwards_edges) );
RETVAL = (SV*)newRV_noinc((SV*)hv);
OUTPUT:
RETVAL
@@ -98,9 +81,6 @@ TriangleMesh::stats()
SV*
TriangleMesh::vertices()
CODE:
- if (!THIS->repaired) CONFESS("vertices() requires repair()");
- THIS->require_shared_vertices();
-
// vertices
AV* vertices = newAV();
av_extend(vertices, THIS->its.vertices.size());
@@ -120,13 +100,10 @@ TriangleMesh::vertices()
SV*
TriangleMesh::facets()
CODE:
- if (!THIS->repaired) CONFESS("facets() requires repair()");
- THIS->require_shared_vertices();
-
// facets
AV* facets = newAV();
- av_extend(facets, THIS->stl.stats.number_of_facets);
- for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) {
+ av_extend(facets, THIS->facets_count());
+ for (int i = 0; i < THIS->facets_count(); i++) {
AV* facet = newAV();
av_store(facets, i, newRV_noinc((SV*)facet));
av_extend(facet, 2);
@@ -140,34 +117,13 @@ TriangleMesh::facets()
RETVAL
SV*
-TriangleMesh::normals()
- CODE:
- if (!THIS->repaired) CONFESS("normals() requires repair()");
-
- // normals
- AV* normals = newAV();
- av_extend(normals, THIS->stl.stats.number_of_facets);
- for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) {
- AV* facet = newAV();
- av_store(normals, i, newRV_noinc((SV*)facet));
- av_extend(facet, 2);
- av_store(facet, 0, newSVnv(THIS->stl.facet_start[i].normal(0)));
- av_store(facet, 1, newSVnv(THIS->stl.facet_start[i].normal(1)));
- av_store(facet, 2, newSVnv(THIS->stl.facet_start[i].normal(2)));
- }
-
- RETVAL = newRV_noinc((SV*)normals);
- OUTPUT:
- RETVAL
-
-SV*
TriangleMesh::size()
CODE:
AV* size = newAV();
av_extend(size, 2);
- av_store(size, 0, newSVnv(THIS->stl.stats.size(0)));
- av_store(size, 1, newSVnv(THIS->stl.stats.size(1)));
- av_store(size, 2, newSVnv(THIS->stl.stats.size(2)));
+ av_store(size, 0, newSVnv(THIS->stats().size(0)));
+ av_store(size, 1, newSVnv(THIS->stats().size(1)));
+ av_store(size, 2, newSVnv(THIS->stats().size(2)));
RETVAL = newRV_noinc((SV*)size);
OUTPUT:
RETVAL
@@ -176,8 +132,6 @@ SV*
TriangleMesh::slice(z)
std::vector<double> z
CODE:
- THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
-
// convert doubles to floats
std::vector<float> z_f = cast<float>(z);
@@ -206,7 +160,6 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh)
TriangleMesh* upper_mesh;
TriangleMesh* lower_mesh;
CODE:
- THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
indexed_triangle_set upper, lower;
cut_mesh(THIS->its, z, upper_mesh ? &upper : nullptr, lower_mesh ? &lower : nullptr);
if (upper_mesh)
@@ -217,12 +170,12 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh)
std::vector<double>
TriangleMesh::bb3()
CODE:
- RETVAL.push_back(THIS->stl.stats.min(0));
- RETVAL.push_back(THIS->stl.stats.min(1));
- RETVAL.push_back(THIS->stl.stats.max(0));
- RETVAL.push_back(THIS->stl.stats.max(1));
- RETVAL.push_back(THIS->stl.stats.min(2));
- RETVAL.push_back(THIS->stl.stats.max(2));
+ RETVAL.push_back(THIS->stats().min(0));
+ RETVAL.push_back(THIS->stats().min(1));
+ RETVAL.push_back(THIS->stats().max(0));
+ RETVAL.push_back(THIS->stats().max(1));
+ RETVAL.push_back(THIS->stats().min(2));
+ RETVAL.push_back(THIS->stats().max(2));
OUTPUT:
RETVAL
@@ -250,16 +203,3 @@ sphere(double rho)
%}
};
-
-%package{Slic3r::TriangleMesh};
-
-%{
-PROTOTYPES: DISABLE
-
-std::string
-hello_world()
- CODE:
- RETVAL = "Hello world!";
- OUTPUT:
- RETVAL
-%}
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index 54e686ae3..ca26750dc 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -239,7 +239,6 @@ SupportLayerPtrs* T_PTR_ARRAYREF_PTR
# we return these types whenever we want the items to be returned
# by reference and not marked ::Ref because they're newly allocated
# and not referenced by any Perl object
-TriangleMeshPtrs T_PTR_ARRAYREF
INPUT
diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt
index 2d364628e..f9e61c6a0 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -163,7 +163,6 @@
%typemap{Surfaces};
%typemap{Polygons*};
%typemap{TriangleMesh*};
-%typemap{TriangleMeshPtrs};
%typemap{Model*};
%typemap{Ref<Model>}{simple};
%typemap{Clone<Model>}{simple};