Merge branch 'master' into fs_emboss

# Conflicts:
#	src/libslic3r/Format/3mf.cpp
#	src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
This commit is contained in:
Filip Sykala 2021-10-07 08:28:17 +02:00
commit 3bd9fc07d2
213 changed files with 11755 additions and 7841 deletions

View File

@ -139,7 +139,6 @@ sub mesh {
my $mesh = Slic3r::TriangleMesh->new; my $mesh = Slic3r::TriangleMesh->new;
$mesh->ReadFromPerl($vertices, $facets); $mesh->ReadFromPerl($vertices, $facets);
$mesh->repair;
$mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz}; $mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz};
$mesh->translate(@{$params{translate}}) if $params{translate}; $mesh->translate(@{$params{translate}}) if $params{translate};
return $mesh; return $mesh;

View File

@ -48,6 +48,7 @@
# enabled_tags = ... # enabled_tags = ...
# disabled_tags = ... # disabled_tags = ...
# supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX; # supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX;
# and all filament types: PLA; PET; ABS; ASA; FLEX; HIPS; EDGE; NGEN; NYLON; PVA; PC; PP; PEI; PEEK; PEKK; POM; PSU; PVDF; SCAFF;
# Tags are case sensitive. # Tags are case sensitive.
# FFF is affirmative for both one or more extruder printers. # FFF is affirmative for both one or more extruder printers.
# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) # Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;)

View File

@ -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>

After

Width:  |  Height:  |  Size: 781 B

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
height="200"
width="200"
sodipodi:docname="notification_info.svg"
xml:space="preserve"
enable-background="new 0 0 100 100"
viewBox="0 0 200 200"
y="0px"
x="0px"
id="warning"
version="1.0"><metadata
id="metadata19"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs17" /><sodipodi:namedview
inkscape:current-layer="warning"
inkscape:window-maximized="1"
inkscape:window-y="-11"
inkscape:window-x="-11"
inkscape:cy="117.65848"
inkscape:cx="108.69674"
inkscape:zoom="5.04"
showgrid="false"
id="namedview15"
inkscape:window-height="2066"
inkscape:window-width="3840"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff"
inkscape:document-rotation="0" />
<g
id="g8"
transform="matrix(2,0,0,2,0.17117674,0.68464711)"
style="fill:#ed6b21;fill-opacity:1"><path
id="path6"
d="m 40.1,43.5 c 4.5,-1.2 8.2,-1.7 11,-1.7 1.7,0 3,0.3 4,0.9 0.9,0.6 1.4,1.5 1.4,2.8 0,0.5 -0.1,1.1 -0.2,1.6 l -5,23.9 c -0.1,0.7 -0.2,1.1 -0.2,1.4 0,1 0.4,1.7 1.3,2 0.8,0.3 2.4,0.5 4.6,0.5 l -0.2,1.8 c -4.3,1.1 -7.8,1.6 -10.5,1.6 -1.9,0 -3.3,-0.4 -4.4,-1.1 -1,-0.7 -1.6,-1.8 -1.6,-3.3 0,-0.7 0.1,-1.6 0.3,-2.7 l 4.6,-22 c 0.2,-1 0.3,-1.7 0.3,-2 0,-0.7 -0.3,-1.2 -0.9,-1.4 C 44,45.5 42.4,45.4 39.7,45.4 Z M 59.2,28.4 c 0,1.7 -0.5,3.1 -1.6,4.2 -1.1,1.1 -2.4,1.6 -4,1.6 -1.5,0 -2.8,-0.5 -3.8,-1.5 -1,-1 -1.5,-2.3 -1.5,-3.9 0,-1.8 0.5,-3.2 1.6,-4.2 1.1,-1 2.4,-1.5 4,-1.5 1.5,0 2.8,0.5 3.8,1.4 1,0.9 1.5,2.2 1.5,3.9 z"
class="st1"
style="fill:#ed6b21;fill-opacity:1" /></g><path
id="path853"
d="m 16.939336,52.048564 c 0.24576,-0.15442 19.08533,-11.001046 41.86572,-24.103596 L 100.22396,4.1221459 142.03917,28.183626 183.85441,52.245107 V 100.734 149.2229 l -41.81468,24.06212 -41.81467,24.06214 -41.816284,-24.06216 -41.81626,-24.06216 -0.05,-48.44676 -0.05,-48.446739 z M 63.764716,163.5414 c 19.88816,11.44426 36.295164,20.80774 36.460014,20.80774 0.16481,0 16.57129,-9.36332 36.45875,-20.80738 l 36.15903,-20.80738 V 100.73468 58.734992 L 136.68178,37.925746 C 116.79341,26.480662 100.38715,17.116638 100.22347,17.116804 100.05968,17.116972 83.653536,26.48106 63.765126,37.925887 l -36.16072,20.808776 v 41.999497 41.9995 z"
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413" /><path
id="path855"
d="m 89.952923,157.02042 c -4.242772,-0.58044 -7.113036,-2.32486 -8.380448,-5.09326 -0.626294,-1.368 -0.807444,-3.89074 -0.464458,-6.46814 0.1277,-0.95968 2.419926,-12.20538 5.093816,-24.99044 2.67389,-12.78508 4.908156,-23.917618 4.965016,-24.738958 0.115,-1.6621 -0.189601,-2.54084 -1.114721,-3.21606 -0.792772,-0.57856 -2.966462,-0.9222 -6.787666,-1.07302 -3.481347,-0.1374 -3.555832,-0.1494 -3.458944,-0.5573 0.05446,-0.22926 0.206042,-0.97334 0.336845,-1.6535 0.1308,-0.68016 0.338118,-1.31832 0.4607,-1.41814 0.28243,-0.22998 6.4844,-1.66782 9.46857,-2.19514 5.863452,-1.0361 12.531417,-1.47854 15.428597,-1.02374 5.36116,0.84161 7.82828,3.38728 7.50956,7.74872 -0.0602,0.82388 -2.44364,12.614038 -5.2965,26.200338 -3.08108,14.67312 -5.23336,25.36606 -5.30112,26.33702 -0.24186,3.46504 0.94802,4.64856 5.32416,5.29562 1.15984,0.1716 3.06554,0.31182 4.23488,0.31182 h 2.1261 l -0.1262,1.04166 c -0.0694,0.57292 -0.174,1.37074 -0.23232,1.77292 l -0.1062,0.73126 -2.51448,0.59446 c -8.31236,1.96515 -17.063901,2.95498 -21.165277,2.39389 z"
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413" /><path
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598"
d="m 103.80449,68.371429 c -4.85388,-1.747057 -7.401434,-6.318219 -6.705139,-12.031262 0.68816,-5.646301 5.025269,-9.262423 11.052899,-9.215502 4.20991,0.03277 7.4957,1.946636 9.16606,5.33893 0.89045,1.808402 0.93519,2.048665 0.92966,4.992684 -0.007,3.886952 -0.60744,5.58627 -2.81498,7.970671 -2.18267,2.35755 -4.19782,3.227419 -7.70164,3.324532 -1.90628,0.05284 -3.02496,-0.05544 -3.92686,-0.380053 z"
id="path859" /><path
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598"
d="m 88.159837,156.51971 c -4.689261,-1.3224 -6.765722,-3.65088 -6.999725,-7.84929 -0.143463,-2.57397 -0.367006,-1.35766 5.236874,-28.49422 2.02374,-9.79989 3.976706,-19.42817 4.339925,-21.396181 1.189159,-6.443166 0.608576,-7.052275 -7.07692,-7.424629 -1.896078,-0.09186 -3.506416,-0.262489 -3.578529,-0.37917 -0.07211,-0.116682 0.01749,-0.855929 0.199113,-1.642772 0.380336,-1.647704 6.64e-4,-1.47536 6.265635,-2.844151 5.6684,-1.238451 9.941962,-1.750196 14.87169,-1.780835 6.11275,-0.03799 8.21381,0.630882 10.35608,3.296884 0.84162,1.047378 0.86697,1.161816 0.83925,3.788072 -0.0245,2.316986 -0.5437,5.179915 -3.59555,19.824542 -7.12659,34.19777 -7.24867,34.91174 -6.23405,36.46024 0.25629,0.39114 0.76211,0.90521 1.12406,1.14236 1.05449,0.69093 4.30303,1.25008 7.29332,1.25534 l 2.76816,0.005 -0.20111,1.61344 c -0.11061,0.88739 -0.33162,1.69456 -0.49112,1.79371 -0.42321,0.26308 -8.03018,1.8551 -11.29784,2.36446 -4.118692,0.64201 -11.953193,0.79357 -13.819263,0.26733 z"
id="path861" /><path
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.204365"
d="M 14.442311,50.587986 C 14.695444,50.428934 34.100201,39.256909 57.564008,25.761283 L 100.22547,1.2237759 143.29514,26.0071 l 43.0697,24.783326 v 49.943554 49.94357 l -43.06912,24.78398 -43.06911,24.78401 -43.07077,-24.78403 -43.070753,-24.78402 -0.0515,-49.90017 -0.0515,-49.900133 z M 62.672458,165.4256 c 20.484805,11.78759 37.384012,21.43197 37.553812,21.43197 0.16975,0 17.06843,-9.64422 37.55251,-21.4316 l 37.2438,-21.4316 V 100.73468 57.475007 L 137.77703,36.041484 c -20.48502,-11.788437 -37.38347,-21.433381 -37.55206,-21.43321 -0.1687,1.73e-4 -17.067028,9.645183 -37.55209,21.433355 L 25.427333,57.474668 v 43.259482 43.25948 z"
id="path853-2" /><path
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.18254"
d="M 23.59806,55.943454 C 23.824159,55.801388 41.156564,45.822492 62.114522,33.768146 l 38.105398,-21.916997 38.46998,22.136562 38.47002,22.136563 v 44.609776 44.60979 l -38.4695,22.13715 -38.46949,22.13717 -38.470985,-22.13719 -38.470959,-22.13718 -0.046,-44.57102 -0.046,-44.571001 z M 66.67741,158.51686 c 18.297107,10.52872 33.39156,19.14312 33.54322,19.14312 0.15162,0 15.24558,-8.61425 33.54204,-19.14279 L 167.02898,139.3744 V 100.73468 62.094968 L 133.7611,42.950461 C 115.4638,32.420984 100.37005,23.806082 100.21947,23.806235 100.06878,23.806389 84.975124,32.42135 66.677787,42.950591 L 33.409924,62.094665 v 38.639535 38.63954 z"
id="path853-0" /></svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -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>

After

Width:  |  Height:  |  Size: 781 B

File diff suppressed because it is too large Load Diff

View File

@ -7,21 +7,25 @@ src/slic3r/GUI/ButtonsDescription.cpp
src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DesktopIntegrationDialog.cpp
src/slic3r/GUI/DoubleSlider.cpp src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtraRenderers.cpp src/slic3r/GUI/ExtraRenderers.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/GUI/Field.cpp src/slic3r/GUI/Field.cpp
src/slic3r/GUI/FirmwareDialog.cpp src/slic3r/GUI/FirmwareDialog.cpp
src/slic3r/GUI/GalleryDialog.cpp
src/slic3r/GUI/GCodeViewer.cpp src/slic3r/GUI/GCodeViewer.cpp
src/slic3r/GUI/GLCanvas3D.cpp src/slic3r/GUI/GLCanvas3D.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmosManager.cpp src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@ -34,9 +38,12 @@ src/slic3r/GUI/GUI_ObjectList.cpp
src/slic3r/GUI/GUI_ObjectManipulation.cpp src/slic3r/GUI/GUI_ObjectManipulation.cpp
src/slic3r/GUI/GUI_ObjectSettings.cpp src/slic3r/GUI/GUI_ObjectSettings.cpp
src/slic3r/GUI/GUI_Preview.cpp src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/HintNotification.cpp
src/slic3r/GUI/ImGuiWrapper.cpp src/slic3r/GUI/ImGuiWrapper.cpp
src/slic3r/GUI/Jobs/ArrangeJob.cpp src/slic3r/GUI/Jobs/ArrangeJob.cpp
src/slic3r/GUI/Jobs/FillBedJob.cpp
src/slic3r/GUI/Jobs/Job.cpp src/slic3r/GUI/Jobs/Job.cpp
src/slic3r/GUI/Jobs/PlaterJob.cpp
src/slic3r/GUI/Jobs/RotoptimizeJob.cpp src/slic3r/GUI/Jobs/RotoptimizeJob.cpp
src/slic3r/GUI/Jobs/SLAImportJob.cpp src/slic3r/GUI/Jobs/SLAImportJob.cpp
src/slic3r/GUI/KBShortcutsDialog.cpp src/slic3r/GUI/KBShortcutsDialog.cpp
@ -59,6 +66,7 @@ src/slic3r/GUI/RammingChart.cpp
src/slic3r/GUI/SavePresetDialog.cpp src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/Search.cpp src/slic3r/GUI/Search.cpp
src/slic3r/GUI/Selection.cpp src/slic3r/GUI/Selection.cpp
src/slic3r/GUI/SendSystemInfoDialog.cpp
src/slic3r/GUI/SysInfoDialog.cpp src/slic3r/GUI/SysInfoDialog.cpp
src/slic3r/GUI/Tab.cpp src/slic3r/GUI/Tab.cpp
src/slic3r/GUI/Tab.hpp src/slic3r/GUI/Tab.hpp
@ -74,6 +82,7 @@ src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/PresetUpdater.cpp src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/Http.cpp src/slic3r/Utils/Http.cpp
src/slic3r/Utils/Process.cpp src/slic3r/Utils/Process.cpp
src/slic3r/Utils/Repetier.cpp
src/libslic3r/GCode.cpp src/libslic3r/GCode.cpp
src/libslic3r/ExtrusionEntity.cpp src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp src/libslic3r/Flow.cpp

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.4.0-alpha0 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-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-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-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
@ -11,9 +12,11 @@ min_slic3r_version = 2.4.0-alpha0
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-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. 1.3.0-alpha0 Disabled thick bridges, updated support settings.
min_slic3r_version = 2.3.2-alpha0 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.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
1.3.0 Added SL1S SPEED profiles. 1.3.0 Added SL1S SPEED profiles.
min_slic3r_version = 2.3.0-rc1 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.8 Added multiple add:north and Extrudr filament profiles.
1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI. 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.6 Added filament profile for "Prusament PC Blend Carbon Fiber".

View File

@ -5,7 +5,7 @@
name = Prusa Research name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # 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? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ 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% changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -120,7 +120,7 @@ technology = SLA
family = SL1 family = SL1
bed_model = sl1_bed.stl bed_model = sl1_bed.stl
bed_texture = sl1.svg 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] [printer_model:SL1S]
name = Original Prusa SL1S SPEED name = Original Prusa SL1S SPEED
@ -129,7 +129,7 @@ technology = SLA
family = SL1 family = SL1
bed_model = sl1s_bed.stl bed_model = sl1s_bed.stl
bed_texture = sl1s.svg 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 # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@ -181,7 +181,7 @@ max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0 max_volumetric_speed = 0
min_skirt_length = 4 min_skirt_length = 4
notes = notes =
overhangs = 0 overhangs = 1
only_retract_when_crossing_perimeters = 0 only_retract_when_crossing_perimeters = 0
ooze_prevention = 0 ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode 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 bottom_solid_min_thickness = 0.8
single_extruder_multi_material_priming = 0 single_extruder_multi_material_priming = 0
thick_bridges = 1 thick_bridges = 1
overhangs = 0
[print:*soluble_support*] [print:*soluble_support*]
overhangs = 1 overhangs = 1
@ -460,10 +461,10 @@ bridge_flow_ratio = 1
bridge_speed = 20 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 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 layer_height = 0.1
perimeter_acceleration = 800 perimeter_acceleration = 600
top_solid_layers = 9 top_solid_layers = 9
support_material_contact_distance = 0.17 support_material_contact_distance = 0.17
raft_contact_distance = 0.17 raft_contact_distance = 0.15
[print:*0.15mm*] [print:*0.15mm*]
inherits = *common* inherits = *common*
@ -619,6 +620,7 @@ support_material_contact_distance = 0.1
raft_contact_distance = 0.2 raft_contact_distance = 0.2
top_solid_infill_speed = 40 top_solid_infill_speed = 40
thick_bridges = 1 thick_bridges = 1
overhangs = 0
## MMU1 specific ## MMU1 specific
[print:0.15mm OPTIMAL SOLUBLE FULL] [print:0.15mm OPTIMAL SOLUBLE FULL]
@ -704,7 +706,7 @@ small_perimeter_speed = 15
solid_infill_speed = 40 solid_infill_speed = 40
top_solid_infill_speed = 30 top_solid_infill_speed = 30
support_material_contact_distance = 0.08 support_material_contact_distance = 0.08
raft_contact_distance = 0.08 raft_contact_distance = 0.07
## MK2 - 0.6mm nozzle ## MK2 - 0.6mm nozzle
@ -772,6 +774,7 @@ single_extruder_multi_material_priming = 0
inherits = 0.35mm FAST 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 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 single_extruder_multi_material_priming = 0
overhangs = 0
## MK2.5 - MMU2 specific ## MK2.5 - MMU2 specific
@ -1013,7 +1016,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
fill_pattern = grid fill_pattern = grid
fill_density = 20% fill_density = 20%
support_material_contact_distance = 0.08 support_material_contact_distance = 0.08
raft_contact_distance = 0.08 raft_contact_distance = 0.07
## MK3 - 0.6mm nozzle ## MK3 - 0.6mm nozzle
@ -1028,7 +1031,7 @@ perimeter_speed = 45
solid_infill_speed = 70 solid_infill_speed = 70
top_solid_infill_speed = 45 top_solid_infill_speed = 45
support_material_contact_distance = 0.22 support_material_contact_distance = 0.22
raft_contact_distance = 0.22 raft_contact_distance = 0.2
bridge_flow_ratio = 1 bridge_flow_ratio = 1
[print:0.20mm DETAIL @0.6 nozzle MK3] [print:0.20mm DETAIL @0.6 nozzle MK3]
@ -1042,7 +1045,7 @@ perimeter_speed = 45
solid_infill_speed = 70 solid_infill_speed = 70
top_solid_infill_speed = 45 top_solid_infill_speed = 45
support_material_contact_distance = 0.22 support_material_contact_distance = 0.22
raft_contact_distance = 0.22 raft_contact_distance = 0.2
bridge_flow_ratio = 1 bridge_flow_ratio = 1
[print:0.30mm QUALITY @0.6 nozzle MK3] [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_pattern = grid
fill_density = 20% fill_density = 20%
support_material_contact_distance = 0.08 support_material_contact_distance = 0.08
raft_contact_distance = 0.08 raft_contact_distance = 0.07
# MINI - 0.6mm nozzle # MINI - 0.6mm nozzle
@ -4496,7 +4499,31 @@ initial_exposure_time = 35
material_type = Tough material_type = Tough
material_vendor = Monocure 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] [sla_material:Prusa Orange Tough @0.025]
inherits = *common 0.025* inherits = *common 0.025*
exposure_time = 6 exposure_time = 6
@ -5185,7 +5212,30 @@ initial_exposure_time = 35
material_type = Tough material_type = Tough
material_vendor = Zortrax 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] [sla_material:Prusa Beige Tough @0.05]
inherits = *common 0.05* inherits = *common 0.05*
@ -5447,7 +5497,30 @@ initial_exposure_time = 50
material_type = Tough material_type = Tough
material_vendor = BlueCast 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] [sla_material:Prusa Orange Tough @0.1]
inherits = *common 0.1* inherits = *common 0.1*
@ -5530,6 +5603,31 @@ material_vendor = Made for Prusa
## 0.025 SL1S ## 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] [sla_material:Prusa Orange Tough @0.025 SL1S]
inherits = *0.025_sl1s* inherits = *0.025_sl1s*
exposure_time = 1.8 exposure_time = 1.8
@ -5644,6 +5742,31 @@ material_vendor = Peopoly
## 0.05 SL1S ## 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] [sla_material:Prusa Orange Tough @0.05 SL1S]
inherits = *0.05_sl1s* inherits = *0.05_sl1s*
exposure_time = 2 exposure_time = 2
@ -5758,6 +5881,31 @@ material_vendor = Peopoly
## 0.1 SL1S ## 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] [sla_material:Prusa Orange Tough @0.1 SL1S]
inherits = *0.1_sl1s* inherits = *0.1_sl1s*
exposure_time = 2.6 exposure_time = 2.6
@ -6573,7 +6721,7 @@ nozzle_diameter = 0.6
max_layer_height = 0.40 max_layer_height = 0.40
min_layer_height = 0.15 min_layer_height = 0.15
default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI
retract_length = 3.5 retract_length = 3.2
retract_before_travel = 1.5 retract_before_travel = 1.5
[printer:Original Prusa MINI & MINI+ 0.8 nozzle] [printer:Original Prusa MINI & MINI+ 0.8 nozzle]

View File

@ -34,9 +34,7 @@ void main()
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
float width = 1.5 * i_scales.x; 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);
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);
vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz; 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); intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);

View File

@ -212,8 +212,7 @@ int main(const int argc, const char *argv[])
return -1; return -1;
} }
mesh.repair(); if (mesh.empty()) {
if (mesh.facets_count() == 0) {
std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl; std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
return -1; return -1;
} }

View File

@ -24,7 +24,6 @@ int main(const int argc, const char * argv[])
TriangleMesh input; TriangleMesh input;
input.ReadSTLFile(argv[1]); input.ReadSTLFile(argv[1]);
input.repair();
Benchmark bench; Benchmark bench;

View File

@ -65,7 +65,7 @@ void CSGDisplay::render_scene()
glFlush(); glFlush();
} }
void Scene::set_print(uqptr<SLAPrint> &&print) void Scene::set_print(std::unique_ptr<SLAPrint> &&print)
{ {
m_print = std::move(print); m_print = std::move(print);
@ -85,7 +85,7 @@ void CSGDisplay::SceneCache::clear()
primitives.clear(); primitives.clear();
} }
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh) std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
{ {
auto p = std::make_shared<Primitive>(); auto p = std::make_shared<Primitive>();
p->load_mesh(mesh); p->load_mesh(mesh);
@ -94,7 +94,7 @@ shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
return p; return p;
} }
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh, std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
OpenCSG::Operation o, OpenCSG::Operation o,
unsigned c) unsigned c)
{ {
@ -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()); this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
int vertices_count = 0; 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]; const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++j) 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_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()); interior.transform(po->trafo().inverse());
mshinst.merge(interior); mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst); mi->transform_mesh(&mshinst);
@ -417,14 +416,12 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>(); auto center = bb.center().cast<float>();
mshinst.translate(-center); mshinst.translate(-center);
mshinst.require_shared_vertices();
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection, m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
m_csgsettings.get_convexity()); m_csgsettings.get_convexity());
} }
for (const sla::DrainHole &holept : holedata) { for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
holemesh.require_shared_vertices();
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1); m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
} }
} }

View File

@ -17,11 +17,6 @@ class SLAPrint;
namespace GL { namespace GL {
// Simple shorthands for smart pointers
template<class T> using shptr = std::shared_ptr<T>;
template<class T> using uqptr = std::unique_ptr<T>;
template<class T> using wkptr = std::weak_ptr<T>;
template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>; template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
// remove empty weak pointers from a vector // remove empty weak pointers from a vector
@ -61,7 +56,7 @@ public:
}; };
private: private:
vector<wkptr<Listener>> m_listeners; vector<std::weak_ptr<Listener>> m_listeners;
public: public:
virtual ~MouseInput() = default; virtual ~MouseInput() = default;
@ -95,7 +90,7 @@ public:
call(&Listener::on_moved_to, m_listeners, x, y); call(&Listener::on_moved_to, m_listeners, x, y);
} }
void add_listener(shptr<Listener> listener) void add_listener(std::shared_ptr<Listener> listener)
{ {
m_listeners.emplace_back(listener); m_listeners.emplace_back(listener);
cleanup(m_listeners); cleanup(m_listeners);
@ -322,7 +317,7 @@ public:
// The scene is a wrapper around SLAPrint which holds the data to be visualized. // The scene is a wrapper around SLAPrint which holds the data to be visualized.
class Scene class Scene
{ {
uqptr<SLAPrint> m_print; std::unique_ptr<SLAPrint> m_print;
public: public:
// Subscribers will be notified if the model is changed. This might be a // Subscribers will be notified if the model is changed. This might be a
@ -340,19 +335,19 @@ public:
Scene(); Scene();
~Scene(); ~Scene();
void set_print(uqptr<SLAPrint> &&print); void set_print(std::unique_ptr<SLAPrint> &&print);
const SLAPrint * get_print() const { return m_print.get(); } const SLAPrint * get_print() const { return m_print.get(); }
BoundingBoxf3 get_bounding_box() const; BoundingBoxf3 get_bounding_box() const;
void add_listener(shptr<Listener> listener) void add_listener(std::shared_ptr<Listener> listener)
{ {
m_listeners.emplace_back(listener); m_listeners.emplace_back(listener);
cleanup(m_listeners); cleanup(m_listeners);
} }
private: private:
vector<wkptr<Listener>> m_listeners; vector<std::weak_ptr<Listener>> m_listeners;
}; };
// The basic Display. This is almost just an interface but will do all the // The basic Display. This is almost just an interface but will do all the
@ -366,20 +361,20 @@ protected:
Vec2i m_size; Vec2i m_size;
bool m_initialized = false; bool m_initialized = false;
shptr<Camera> m_camera; std::shared_ptr<Camera> m_camera;
FpsCounter m_fps_counter; FpsCounter m_fps_counter;
public: public:
explicit Display(shptr<Camera> camera = nullptr) explicit Display(std::shared_ptr<Camera> camera = nullptr)
: m_camera(camera ? camera : std::make_shared<PerspectiveCamera>()) : m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
{} {}
~Display() override; ~Display() override;
shptr<const Camera> get_camera() const { return m_camera; } std::shared_ptr<const Camera> get_camera() const { return m_camera; }
shptr<Camera> get_camera() { return m_camera; } std::shared_ptr<Camera> get_camera() { return m_camera; }
void set_camera(shptr<Camera> cam) { m_camera = cam; } void set_camera(std::shared_ptr<Camera> cam) { m_camera = cam; }
virtual void swap_buffers() = 0; virtual void swap_buffers() = 0;
virtual void set_active(long width, long height); virtual void set_active(long width, long height);
@ -410,14 +405,14 @@ protected:
// Cache the renderable primitives. These will be fetched when the scene // Cache the renderable primitives. These will be fetched when the scene
// is modified. // is modified.
struct SceneCache { struct SceneCache {
vector<shptr<Primitive>> primitives; vector<std::shared_ptr<Primitive>> primitives;
vector<Primitive *> primitives_free; vector<Primitive *> primitives_free;
vector<OpenCSG::Primitive *> primitives_csg; vector<OpenCSG::Primitive *> primitives_csg;
void clear(); void clear();
shptr<Primitive> add_mesh(const TriangleMesh &mesh); std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh);
shptr<Primitive> add_mesh(const TriangleMesh &mesh, std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh,
OpenCSG::Operation op, OpenCSG::Operation op,
unsigned covexity); unsigned covexity);
} m_scene_cache; } m_scene_cache;
@ -446,13 +441,13 @@ class Controller : public std::enable_shared_from_this<Controller>,
Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
bool m_left_btn = false, m_right_btn = false; bool m_left_btn = false, m_right_btn = false;
shptr<Scene> m_scene; std::shared_ptr<Scene> m_scene;
vector<wkptr<Display>> m_displays; vector<std::weak_ptr<Display>> m_displays;
// Call a method of Camera on all the cameras of the attached displays // Call a method of Camera on all the cameras of the attached displays
template<class F, class...Args> template<class F, class...Args>
void call_cameras(F &&f, Args&&... args) { void call_cameras(F &&f, Args&&... args) {
for (wkptr<Display> &l : m_displays) for (std::weak_ptr<Display> &l : m_displays)
if (auto disp = l.lock()) if (auto cam = disp->get_camera()) if (auto disp = l.lock()) if (auto cam = disp->get_camera())
(cam.get()->*f)(std::forward<Args>(args)...); (cam.get()->*f)(std::forward<Args>(args)...);
} }
@ -460,7 +455,7 @@ class Controller : public std::enable_shared_from_this<Controller>,
public: public:
// Set the scene that will be controlled. // Set the scene that will be controlled.
void set_scene(shptr<Scene> scene) void set_scene(std::shared_ptr<Scene> scene)
{ {
m_scene = scene; m_scene = scene;
m_scene->add_listener(shared_from_this()); m_scene->add_listener(shared_from_this());
@ -468,7 +463,7 @@ public:
const Scene * get_scene() const { return m_scene.get(); } const Scene * get_scene() const { return m_scene.get(); }
void add_display(shptr<Display> disp) void add_display(std::shared_ptr<Display> disp)
{ {
m_displays.emplace_back(disp); m_displays.emplace_back(disp);
cleanup(m_displays); cleanup(m_displays);

View File

@ -43,7 +43,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse()); interior.transform(po->trafo().inverse());
mshinst.merge(interior); mshinst.merge(interior);
mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst); mi->transform_mesh(&mshinst);
@ -51,15 +50,11 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast<float>(); auto center = bb.center().cast<float>();
mshinst.translate(-center); mshinst.translate(-center);
mshinst.require_shared_vertices();
add_mesh(mshinst); add_mesh(mshinst);
} }
for (const sla::DrainHole &holept : holedata) { for (const sla::DrainHole &holept : holedata)
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
holemesh.require_shared_vertices();
add_mesh(holemesh);
}
} }
repaint(); repaint();

View File

@ -12,7 +12,7 @@ class CSGVolume: public Volume
class ShaderCSGDisplay: public Display { class ShaderCSGDisplay: public Display {
protected: protected:
vector<shptr<CSGVolume>> m_volumes; vector<std::shared_ptr<CSGVolume>> m_volumes;
void add_mesh(const TriangleMesh &mesh); void add_mesh(const TriangleMesh &mesh);
public: public:

View File

@ -34,7 +34,7 @@ using namespace Slic3r::GL;
class Renderer { class Renderer {
protected: protected:
wxGLCanvas *m_canvas; wxGLCanvas *m_canvas;
shptr<wxGLContext> m_context; std::shared_ptr<wxGLContext> m_context;
public: public:
Renderer(wxGLCanvas *c): m_canvas{c} { Renderer(wxGLCanvas *c): m_canvas{c} {
@ -86,16 +86,16 @@ public:
class Canvas: public wxGLCanvas class Canvas: public wxGLCanvas
{ {
// One display is active at a time, the OCSGRenderer by default. // One display is active at a time, the OCSGRenderer by default.
shptr<Slic3r::GL::Display> m_display; std::shared_ptr<Slic3r::GL::Display> m_display;
public: public:
template<class...Args> template<class...Args>
Canvas(Args &&...args): wxGLCanvas(std::forward<Args>(args)...) {} Canvas(Args &&...args): wxGLCanvas(std::forward<Args>(args)...) {}
shptr<Slic3r::GL::Display> get_display() const { return m_display; } std::shared_ptr<Slic3r::GL::Display> get_display() const { return m_display; }
void set_display(shptr<Slic3r::GL::Display> d) { m_display = d; } void set_display(std::shared_ptr<Slic3r::GL::Display> d) { m_display = d; }
}; };
// Enumerate possible mouse events, we will record them. // Enumerate possible mouse events, we will record them.
@ -197,14 +197,14 @@ public:
class MyFrame: public wxFrame class MyFrame: public wxFrame
{ {
// Instantiate the 3D engine. // Instantiate the 3D engine.
shptr<Scene> m_scene; // Model std::shared_ptr<Scene> m_scene; // Model
shptr<Canvas> m_canvas; // Views store std::shared_ptr<Canvas> m_canvas; // Views store
shptr<OCSGRenderer> m_ocsgdisplay; // View std::shared_ptr<OCSGRenderer> m_ocsgdisplay; // View
shptr<ShaderCSGRenderer> m_shadercsg_display; // Another view std::shared_ptr<ShaderCSGRenderer> m_shadercsg_display; // Another view
shptr<Controller> m_ctl; // Controller std::shared_ptr<Controller> m_ctl; // Controller
// Add a status bar with progress indication. // Add a status bar with progress indication.
shptr<Slic3r::GUI::ProgressStatusBar> m_stbar; std::shared_ptr<Slic3r::GUI::ProgressStatusBar> m_stbar;
RecorderMouseInput m_mouse; RecorderMouseInput m_mouse;
@ -237,7 +237,7 @@ class MyFrame: public wxFrame
} }
}; };
uqptr<SLAJob> m_ui_job; std::unique_ptr<SLAJob> m_ui_job;
// To keep track of the running average of measured fps values. // To keep track of the running average of measured fps values.
double m_fps_avg = 0.; double m_fps_avg = 0.;

View File

@ -397,7 +397,7 @@ int CLI::run(int argc, char **argv)
TriangleMesh mesh = model.mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); 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; size_t i = 0;
for (TriangleMesh* m : meshes) { for (TriangleMesh* m : meshes) {
Model out; Model out;

View File

@ -239,6 +239,7 @@ private:
return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b; 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) static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)
{ {
// Facet a's neighbor is facet 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].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 */ 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. // 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_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; 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) void stl_check_facets_nearby(stl_file *stl, float tolerance)
{ {
if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) assert(stl->stats.connected_facets_3_edge <= stl->stats.connected_facets_2_edge);
&& (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) assert(stl->stats.connected_facets_2_edge <= stl->stats.connected_facets_1_edge);
&& (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { 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. // No need to check any further. All facets are connected.
return; return;
}
HashTableEdges hash_table(stl->stats.number_of_facets); HashTableEdges hash_table(stl->stats.number_of_facets);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { 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 */ /* Update list of connected edges */
stl_neighbors &neighbors = stl->neighbors_start[facet_number]; stl_neighbors &neighbors = stl->neighbors_start[facet_number];
// Update statistics on unconnected triangle edges. // Update statistics on unconnected triangle edges.
switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) { switch (neighbors.num_neighbors()) {
case 0: // Facet has 3 neighbors case 3: -- stl->stats.connected_facets_3_edge; // fall through
-- stl->stats.connected_facets_3_edge; case 2: -- stl->stats.connected_facets_2_edge; // fall through
-- stl->stats.connected_facets_2_edge; case 1: -- stl->stats.connected_facets_1_edge; // fall through
-- stl->stats.connected_facets_1_edge; case 0: break;
break; default: assert(false);
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);
} }
if (facet_number < int(-- stl->stats.number_of_facets)) { 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) auto remove_degenerate = [stl, remove_facet](int facet)
{ {
// Update statistics on face connectivity. // Update statistics on face connectivity after one edge was disconnected on the facet "facet_num".
auto stl_update_connects_remove_1 = [stl](int facet_num) { auto 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].num_neighbors()) {
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: assert(false); break;
case 0: // Facet has 3 neighbors case 1: -- stl->stats.connected_facets_1_edge; break;
-- stl->stats.connected_facets_3_edge; break; case 2: -- stl->stats.connected_facets_2_edge; break;
case 1: // Facet has 2 neighbors case 3: -- stl->stats.connected_facets_3_edge; break;
-- stl->stats.connected_facets_2_edge; break; default: assert(false);
case 2: // Facet has 1 neighbor
-- stl->stats.connected_facets_1_edge; break;
case 3: // Facet has 0 neighbors
break;
default:
assert(false);
} }
}; };
@ -604,9 +590,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
// Update statistics on edge connectivity. // Update statistics on edge connectivity.
if ((neighbor[0] == -1) && (neighbor[1] != -1)) 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)) 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[0] >= 0) {
if (neighbor[1] >= 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]; stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
} }
if (neighbor[2] >= 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; stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
} }
@ -652,11 +638,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
++ i; ++ i;
if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) { 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;) for (uint32_t i = 0; i < stl->stats.number_of_facets;)
if (stl->neighbors_start[i].neighbor[0] == -1 && if (stl->neighbors_start[i].num_neighbors() == 0) {
stl->neighbors_start[i].neighbor[1] == -1 &&
stl->neighbors_start[i].neighbor[2] == -1) {
// This facet is completely unconnected. Remove it. // This facet is completely unconnected. Remove it.
remove_facet(i); remove_facet(i);
assert(stl_validate(stl)); assert(stl_validate(stl));

View File

@ -79,8 +79,7 @@ struct stl_neighbors {
which_vertex_not[1] = -1; which_vertex_not[1] = -1;
which_vertex_not[2] = -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->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
// Index of a neighbor facet. // Index of a neighbor facet.
int neighbor[3]; int neighbor[3];
@ -92,28 +91,44 @@ struct stl_stats {
stl_stats() { memset(&header, 0, 81); } stl_stats() { memset(&header, 0, 81); }
char header[81]; char header[81];
stl_type type = (stl_type)0; 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; uint32_t number_of_facets = 0;
// Bounding box.
stl_vertex max = stl_vertex::Zero(); stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero(); stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero(); stl_vertex size = stl_vertex::Zero();
float bounding_diameter = 0.f; float bounding_diameter = 0.f;
float shortest_edge = 0.f; float shortest_edge = 0.f;
// After repair, the volume shall always be positive.
float volume = -1.f; 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; int connected_edges = 0;
// Faces with >=1, >=2 and 3 edges connected to another face.
int connected_facets_1_edge = 0; int connected_facets_1_edge = 0;
int connected_facets_2_edge = 0; int connected_facets_2_edge = 0;
int connected_facets_3_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_1_bad_edge = 0;
int facets_w_2_bad_edge = 0; int facets_w_2_bad_edge = 0;
int facets_w_3_bad_edge = 0; int facets_w_3_bad_edge = 0;
// Number of faces read form an STL file.
int original_num_facets = 0; int original_num_facets = 0;
// Number of edges connected one to another by snapping their end vertices.
int edges_fixed = 0; int edges_fixed = 0;
// Number of faces removed because they were degenerated.
int degenerate_facets = 0; int degenerate_facets = 0;
// Total number of facets removed: Degenerate faces and unconnected faces.
int facets_removed = 0; int facets_removed = 0;
// Number of faces added by hole filling.
int facets_added = 0; 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; int facets_reversed = 0;
// Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
int backwards_edges = 0; int backwards_edges = 0;
// Number of triangles, which were flipped during the fixing process.
int normals_fixed = 0; int normals_fixed = 0;
// Number of connected triangle patches.
int number_of_parts = 0; int number_of_parts = 0;
void clear() { *this = stl_stats(); } void clear() { *this = stl_stats(); }
@ -140,8 +155,6 @@ struct stl_file {
struct indexed_triangle_set struct indexed_triangle_set
{ {
indexed_triangle_set() {}
void clear() { indices.clear(); vertices.clear(); } void clear() { indices.clear(); vertices.clear(); }
size_t memsize() const { size_t memsize() const {
@ -150,8 +163,6 @@ struct indexed_triangle_set
std::vector<stl_triangle_vertex_indices> indices; std::vector<stl_triangle_vertex_indices> indices;
std::vector<stl_vertex> vertices; std::vector<stl_vertex> vertices;
//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
//std::vector<stl_normal> normals
bool empty() const { return indices.empty() || vertices.empty(); } 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); 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> 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) { for (stl_vertex &v_dst : its.vertices) {
stl_vertex v_src = v_dst; stl_vertex v_src = v_dst;

View File

@ -205,11 +205,12 @@ bool stl_write_quad_object(stl_file *stl, char *file)
fprintf(fp, "CQUAD\n"); fprintf(fp, "CQUAD\n");
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
switch (stl->neighbors_start[i].num_neighbors_missing()) { switch (stl->neighbors_start[i].num_neighbors()) {
case 0: color = connect_color; break; case 0:
case 1: color = uncon_1_color; break; default: color = uncon_3_color; break;
case 2: color = uncon_2_color; break; case 1: color = uncon_2_color; break;
default: color = uncon_3_color; 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[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)); 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));

View File

@ -149,10 +149,11 @@ namespace ImGui
const wchar_t CustomSupportsMarker = 0x1D; const wchar_t CustomSupportsMarker = 0x1D;
const wchar_t CustomSeamMarker = 0x1E; const wchar_t CustomSeamMarker = 0x1E;
const wchar_t MmuSegmentationMarker = 0x1F; const wchar_t MmuSegmentationMarker = 0x1F;
// Do not forget use following letters only in wstring
const wchar_t DocumentationButton = 0x2600; const wchar_t DocumentationButton = 0x2600;
const wchar_t DocumentationHoverButton = 0x2601; const wchar_t DocumentationHoverButton = 0x2601;
const wchar_t ClippyMarker = 0x2602; const wchar_t ClippyMarker = 0x2602;
const wchar_t InfoMarker = 0x2603;
// void MyFunction(const char* name, const MyMatrix44& v); // void MyFunction(const char* name, const MyMatrix44& v);
} }

View File

@ -140,11 +140,17 @@ void AppConfig::set_defaults()
if (get("default_action_on_select_preset").empty()) if (get("default_action_on_select_preset").empty())
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save" 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()) if (get("color_mapinulation_panel").empty())
set("color_mapinulation_panel", "0"); set("color_mapinulation_panel", "0");
if (get("order_volumes").empty()) if (get("order_volumes").empty())
set("order_volumes", "1"); set("order_volumes", "1");
if (get("clear_undo_redo_stack_on_new_project").empty())
set("clear_undo_redo_stack_on_new_project", "1");
} }
else { else {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -12,7 +12,7 @@ namespace Slic3r {
#ifdef WIN32 #ifdef WIN32
//only dll name with .dll suffix - currently case sensitive //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) bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
{ {

View File

@ -80,8 +80,6 @@ add_library(libslic3r STATIC
Format/OBJ.hpp Format/OBJ.hpp
Format/objparser.cpp Format/objparser.cpp
Format/objparser.hpp Format/objparser.hpp
Format/PRUS.cpp
Format/PRUS.hpp
Format/STL.cpp Format/STL.cpp
Format/STL.hpp Format/STL.hpp
Format/SL1.hpp Format/SL1.hpp

View File

@ -126,6 +126,45 @@ ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
return PolyTreeToExPolygons(std::move(polytree)); 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. // Offset outside by 10um, one by one.
template<typename PathsProvider> template<typename PathsProvider>
static ClipperLib::Paths safety_offset(PathsProvider &&paths) 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); } { 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) 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); } { 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) 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); } { 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) 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); } { 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) 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); } { 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) 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); } { 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) 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) Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(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) Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(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) Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonsProvider(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) Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(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) Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(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) 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 // convert Polylines to Lines
Lines retval; Lines retval;
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) 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; return retval;
} }

View File

@ -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::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::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::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::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::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); 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::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::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::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::ExPolygon &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &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); 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. // 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::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::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); 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::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::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::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::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &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); Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);

View File

@ -740,7 +740,11 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
} }
// Load the config keys from the given string. // Load the config keys from the given string.
#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) 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) if (str == nullptr)
return 0; return 0;

View File

@ -2015,6 +2015,10 @@ public:
// Set all the nullable values to nils. // Set all the nullable values to nils.
void null_nullables(); 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: private:
// Set a configuration value from a string. // 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); bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);

View File

@ -92,7 +92,7 @@ bool ExPolygon::contains(const Line &line) const
bool ExPolygon::contains(const Polyline &polyline) 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 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 bool ExPolygon::contains(const Point &point) const
{ {
if (!this->contour.contains(point)) return false; if (! this->contour.contains(point))
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { return false;
if (it->contains(point)) return false; for (const Polygon &hole : this->holes)
} if (hole.contains(point))
return false;
return true; return true;
} }
@ -367,6 +368,57 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons)
return out; 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) bool remove_sticks(ExPolygon &poly)
{ {
return remove_sticks(poly.contour) || remove_sticks(poly.holes); return remove_sticks(poly.contour) || remove_sticks(poly.holes);

View File

@ -353,20 +353,24 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
return out; return out;
} }
extern BoundingBox get_extents(const ExPolygon &expolygon); BoundingBox get_extents(const ExPolygon &expolygon);
extern BoundingBox get_extents(const ExPolygons &expolygons); BoundingBox get_extents(const ExPolygons &expolygons);
extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle); BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle); BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons); std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
extern bool remove_sticks(ExPolygon &poly); // Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
extern void keep_largest_contour_only(ExPolygons &polygons); 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 ExPolygon &poly) { return poly.area(); }
inline double area(const ExPolygons &polys) { double s = 0.; for (auto &p : polys) s += p.area(); return s; } 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 // 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 } // namespace Slic3r

View File

@ -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) } }; 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); assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
#endif // NDEBUG #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; out = closest.front().second;
} }
return out; return out;

View File

@ -6,6 +6,7 @@
#include "../GCode.hpp" #include "../GCode.hpp"
#include "../Geometry.hpp" #include "../Geometry.hpp"
#include "../GCode/ThumbnailData.hpp" #include "../GCode/ThumbnailData.hpp"
#include "../Semver.hpp"
#include "../Time.hpp" #include "../Time.hpp"
#include "../I18N.hpp" #include "../I18N.hpp"
@ -21,6 +22,7 @@
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/spirit/include/karma.hpp> #include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi_int.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
@ -34,6 +36,8 @@ namespace pt = boost::property_tree;
#include "TextConfigurationSerialization.hpp" #include "TextConfigurationSerialization.hpp"
#include <fast_float/fast_float.h>
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter, // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
// https://github.com/boostorg/spirit/pull/586 // https://github.com/boostorg/spirit/pull/586
// where the exported string is one digit shorter than it should be to guarantee lossless round trip. // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
@ -133,6 +137,13 @@ static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches"; static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
static constexpr const char* SOURCE_IN_METERS = "source_in_meters"; static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1; const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] = const char* VALID_OBJECT_TYPES[] =
{ {
@ -175,14 +186,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) 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); float value = 0.0f;
return (text != nullptr) ? (float)::atof(text) : 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) 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); int value = 0;
return (text != nullptr) ? ::atoi(text) : 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) bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
@ -301,8 +316,8 @@ namespace Slic3r {
struct Geometry struct Geometry
{ {
std::vector<float> vertices; std::vector<Vec3f> vertices;
std::vector<unsigned int> triangles; std::vector<Vec3i> triangles;
std::vector<std::string> custom_supports; std::vector<std::string> custom_supports;
std::vector<std::string> custom_seam; std::vector<std::string> custom_seam;
std::vector<std::string> mmu_segmentation; std::vector<std::string> mmu_segmentation;
@ -378,6 +393,7 @@ namespace Slic3r {
unsigned int first_triangle_id; unsigned int first_triangle_id;
unsigned int last_triangle_id; unsigned int last_triangle_id;
MetadataList metadata; MetadataList metadata;
RepairedMeshErrors mesh_stats;
VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id) VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id)
: first_triangle_id(first_triangle_id) : first_triangle_id(first_triangle_id)
@ -407,6 +423,8 @@ namespace Slic3r {
unsigned int m_version; unsigned int m_version;
bool m_check_version; bool m_check_version;
// Semantic version of PrusaSlicer, that generated this 3MF.
boost::optional<Semver> m_prusaslicer_generator_version;
unsigned int m_fdm_supports_painting_version = 0; unsigned int m_fdm_supports_painting_version = 0;
unsigned int m_seam_painting_version = 0; unsigned int m_seam_painting_version = 0;
unsigned int m_mm_painting_version = 0; unsigned int m_mm_painting_version = 0;
@ -524,7 +542,9 @@ namespace Slic3r {
bool _handle_end_config_object(); bool _handle_end_config_object();
bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes); bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes);
bool _handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes);
bool _handle_end_config_volume(); bool _handle_end_config_volume();
bool _handle_end_config_volume_mesh();
bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes); bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
bool _handle_end_config_metadata(); bool _handle_end_config_metadata();
@ -716,7 +736,7 @@ namespace Slic3r {
} }
// use the geometry to create the volumes in the new model objects // 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 // for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry // and copy into it the geometry
@ -789,7 +809,7 @@ namespace Slic3r {
// config data not found, this model was not saved using slic3r pe // config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate // 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 // select as volumes
volumes_ptr = &volumes; volumes_ptr = &volumes;
@ -1384,6 +1404,8 @@ namespace Slic3r {
res = _handle_start_config_object(attributes, num_attributes); res = _handle_start_config_object(attributes, num_attributes);
else if (::strcmp(VOLUME_TAG, name) == 0) else if (::strcmp(VOLUME_TAG, name) == 0)
res = _handle_start_config_volume(attributes, num_attributes); res = _handle_start_config_volume(attributes, num_attributes);
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_start_config_volume_mesh(attributes, num_attributes);
else if (::strcmp(METADATA_TAG, name) == 0) else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_start_config_metadata(attributes, num_attributes); res = _handle_start_config_metadata(attributes, num_attributes);
@ -1404,6 +1426,8 @@ namespace Slic3r {
res = _handle_end_config_object(); res = _handle_end_config_object();
else if (::strcmp(VOLUME_TAG, name) == 0) else if (::strcmp(VOLUME_TAG, name) == 0)
res = _handle_end_config_volume(); res = _handle_end_config_volume();
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_end_config_volume_mesh();
else if (::strcmp(METADATA_TAG, name) == 0) else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_end_config_metadata(); res = _handle_end_config_metadata();
@ -1555,9 +1579,10 @@ namespace Slic3r {
{ {
// appends the vertex coordinates // appends the vertex coordinates
// missing values are set equal to ZERO // 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.emplace_back(
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR)); 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, Z_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; return true;
} }
@ -1591,9 +1616,10 @@ namespace Slic3r {
// appends the triangle's vertices indices // appends the triangle's vertices indices
// missing values are set equal to ZERO // 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.emplace_back(
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR)); 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, V3_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_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)); m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
@ -1706,21 +1732,20 @@ namespace Slic3r {
const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str(); const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
throw version_error(msg); throw version_error(msg);
} }
} } else if (m_curr_metadata_name == "Application") {
// Generator application of the 3MF.
if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) { // SLIC3R_APP_KEY - SLIC3R_VERSION
if (boost::starts_with(m_curr_characters, "PrusaSlicer-"))
m_prusaslicer_generator_version = Semver::parse(m_curr_characters.substr(12));
} else if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION, check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
_(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible."))); _(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
} } else if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION, check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
_(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible."))); _(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
} } else if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str()); m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION, check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
_(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible."))); _(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
@ -1837,12 +1862,43 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Importer::_handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes)
{
IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
if (object == m_objects_metadata.end()) {
add_error("Cannot assign volume mesh to a valid object");
return false;
}
if (object->second.volumes.empty()) {
add_error("Cannot assign mesh to a valid olume");
return false;
}
ObjectMetadata::VolumeMetadata& volume = object->second.volumes.back();
int edges_fixed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_EDGES_FIXED );
int degenerate_facets = get_attribute_value_int(attributes, num_attributes, MESH_STAT_DEGENERATED_FACETS);
int facets_removed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_REMOVED );
int facets_reversed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_RESERVED );
int backwards_edges = get_attribute_value_int(attributes, num_attributes, MESH_STAT_BACKWARDS_EDGES );
volume.mesh_stats = { edges_fixed, degenerate_facets, facets_removed, facets_reversed, backwards_edges };
return true;
}
bool _3MF_Importer::_handle_end_config_volume() bool _3MF_Importer::_handle_end_config_volume()
{ {
// do nothing // do nothing
return true; return true;
} }
bool _3MF_Importer::_handle_end_config_volume_mesh()
{
// do nothing
return true;
}
bool _3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes) bool _3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes)
{ {
IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id); IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
@ -1882,7 +1938,7 @@ namespace Slic3r {
return false; 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; unsigned int renamed_volumes_count = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) { for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
@ -1893,77 +1949,78 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity(); Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false; 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 // extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata) { for (const Metadata& metadata : volume_data.metadata) {
if (metadata.key == MATRIX_KEY) { if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value); volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10); 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; break;
} }
} }
// splits volume out of imported geometry // splits volume out of imported geometry
TriangleMesh triangle_mesh; indexed_triangle_set its;
stl_file &stl = triangle_mesh.stl; its.indices.assign(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; const size_t triangles_count = its.indices.size();
stl.stats.type = inmemory; if (triangles_count == 0) {
stl.stats.number_of_facets = (uint32_t)triangles_count; add_error("An empty triangle mesh found");
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");
return false; return false;
} }
facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
{
int min_id = its.indices.front()[0];
int max_id = min_id;
for (const Vec3i& face : its.indices) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
add_error("Found invalid vertex id");
return false;
}
min_id = std::min(min_id, tri_id);
max_id = std::max(max_id, tri_id);
} }
} }
its.vertices.assign(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
stl_get_size(&stl); // rebase indices to the current vertices list
triangle_mesh.repair(); for (Vec3i& face : its.indices)
for (int& tri_id : face)
tri_id -= min_id;
}
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT if (m_prusaslicer_generator_version &&
// PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles *m_prusaslicer_generator_version >= *Semver::parse("2.4.0-alpha1") &&
// This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative *m_prusaslicer_generator_version < *Semver::parse("2.4.0-alpha3"))
if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets) // PrusaSlicer 2.4.0-alpha2 contained a bug, where all vertices of a single object were saved for each volume the object contained.
stl.stats.facets_reversed = 0; // Remove the vertices, that are not referenced by any face.
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT its_compactify_vertices(its, true);
TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
if (m_version == 0) { if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance, // 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 // bake the transformation into the geometry to allow the reload from disk command
// to work properly // to work properly
if (object.instances.size() == 1) { 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()); 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)); ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
// stores the volume matrix taken from the metadata, if present // stores the volume matrix taken from the metadata, if present
if (has_transform) if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); 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 // recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume->supported_facets.reserve(triangles_count); volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count); volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count); volume->mmu_segmentation_facets.reserve(triangles_count);
for (unsigned i=0; i<triangles_count; ++i) { for (size_t i=0; i<triangles_count; ++i) {
size_t index = src_start_id/3 + i; size_t index = volume_data.first_triangle_id + i;
assert(index < geometry.custom_supports.size()); assert(index < geometry.custom_supports.size());
assert(index < geometry.custom_seam.size()); assert(index < geometry.custom_seam.size());
assert(index < geometry.mmu_segmentation.size()); assert(index < geometry.mmu_segmentation.size());
@ -2541,11 +2598,6 @@ namespace Slic3r {
if (volume == nullptr) if (volume == nullptr)
continue; 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) }); volumes_offsets.insert({ volume, Offsets(vertices_count) });
const indexed_triangle_set &its = volume->mesh().its; const indexed_triangle_set &its = volume->mesh().its;
@ -2586,10 +2638,7 @@ namespace Slic3r {
if (volume == nullptr) if (volume == nullptr)
continue; continue;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed(); bool is_left_handed = volume->is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume); VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end()); assert(volume_it != volumes_offsets.end());
@ -2604,7 +2653,6 @@ namespace Slic3r {
{ {
const Vec3i &idx = its.indices[i]; const Vec3i &idx = its.indices[i];
char *ptr = buf; char *ptr = buf;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG << boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ << " v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ << "\" v2=\"" << boost::spirit::int_ <<
@ -2612,15 +2660,6 @@ namespace Slic3r {
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id, idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + 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); 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'; *ptr = '\0';
output_buffer += buf; output_buffer += buf;
} }
@ -2998,8 +3037,9 @@ namespace Slic3r {
} }
// stores volume's config data // stores volume's config data
for (const std::string& key : volume->config.keys()) for (const std::string& key : volume->config.keys()) {
add_metadata(stream, 3, MetadataType::volume, key, volume->config.opt_serialize(key)); stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
stream << " </" << VOLUME_TAG << ">\n"; stream << " </" << VOLUME_TAG << ">\n";
} }

View File

@ -244,11 +244,11 @@ struct AMFParserContext
// Map from obect name to object idx & instances. // Map from obect name to object idx & instances.
std::map<std::string, Object> m_object_instances_map; std::map<std::string, Object> m_object_instances_map;
// Vertices parsed for the current m_object. // 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. // Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume *m_volume { nullptr }; ModelVolume *m_volume { nullptr };
// Faces collected for the current m_volume. // 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. // Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform; Transform3d m_volume_transform;
// Current material allocated for an amf/metadata subtree. // Current material allocated for an amf/metadata subtree.
@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX: case NODE_TYPE_VERTEX:
assert(m_object); assert(m_object);
// Parse the vertex data // 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[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[2].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_value[0].clear(); m_value[0].clear();
m_value[1].clear(); m_value[1].clear();
m_value[2].clear(); m_value[2].clear();
@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Faces of the current volume: // Faces of the current volume:
case NODE_TYPE_TRIANGLE: case NODE_TYPE_TRIANGLE:
assert(m_object && m_volume); assert(m_object && m_volume);
m_volume_facets.emplace_back(atoi(m_value[0].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_volume_facets.emplace_back(atoi(m_value[1].c_str()));
m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
m_value[0].clear(); m_value[0].clear();
m_value[1].clear(); m_value[1].clear();
m_value[2].clear(); m_value[2].clear();
@ -621,39 +617,46 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME: case NODE_TYPE_VOLUME:
{ {
assert(m_object && m_volume); assert(m_object && m_volume);
TriangleMesh mesh; if (m_volume_facets.empty()) {
stl_file &stl = mesh.stl; this->stop("An empty triangle mesh found");
stl.stats.type = inmemory; return;
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; // Verify validity of face indices, find the vertex span.
if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) { int min_id = m_volume_facets.front()[0];
int max_id = min_id;
for (const Vec3i& face : m_volume_facets) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(m_object_vertices.size())) {
this->stop("Malformed triangle mesh"); this->stop("Malformed triangle mesh");
return; return;
} }
facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]); min_id = std::min(min_id, tri_id);
max_id = std::max(max_id, tri_id);
} }
} }
stl_get_size(&stl);
mesh.repair(); // rebase indices to the current vertices list
m_volume->set_mesh(std::move(mesh)); for (Vec3i &face : m_volume_facets)
face -= Vec3i(min_id, min_id, min_id);
indexed_triangle_set its { std::move(m_volume_facets), { m_object_vertices.begin() + min_id, m_object_vertices.begin() + max_id + 1 } };
its_compactify_vertices(its);
if (its_volume(its) < 0)
its_flip_triangles(its);
m_volume->set_mesh(std::move(its));
}
// stores the volume matrix taken from the metadata, if present // 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); 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.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1; m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation(); m_volume->center_geometry_after_creation();
} } else
else
// pass false if the mesh offset has been already taken from the data // 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->center_geometry_after_creation(m_volume->source.input_file.empty());
@ -1187,10 +1190,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
int num_vertices = 0; int num_vertices = 0;
for (ModelVolume *volume : object->volumes) { for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices); 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 indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix(); const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) { for (size_t i = 0; i < its.vertices.size(); ++i) {

View File

@ -19,7 +19,8 @@ namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr) bool load_obj(const char *path, TriangleMesh *meshptr)
{ {
if(meshptr == nullptr) return false; if (meshptr == nullptr)
return false;
// Parse the OBJ file. // Parse the OBJ file.
ObjParser::ObjData data; 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. // Count the faces and verify, that all faces are triangular.
size_t num_faces = 0; size_t num_faces = 0;
size_t num_quads = 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; size_t j = i;
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ; for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
if (i == j) if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
continue; if (num_face_vertices > 4) {
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. // 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; return false;
} }
if (face_vertices == 4) if (num_face_vertices == 4)
++ num_quads; ++ num_quads;
++ num_faces; ++ num_faces;
i = j + 1; i = j;
}
} }
// Convert ObjData into STL. // Convert ObjData into indexed triangle set.
TriangleMesh &mesh = *meshptr; indexed_triangle_set its;
stl_file &stl = mesh.stl; size_t num_vertices = data.coordinates.size() / 4;
stl.stats.type = inmemory; its.vertices.reserve(num_vertices);
stl.stats.number_of_facets = uint32_t(num_faces + num_quads); its.indices.reserve(num_faces + num_quads);
stl.stats.original_num_facets = int(num_faces + num_quads); for (size_t i = 0; i < num_vertices; ++ i) {
// stl_allocate clears all the allocated data to zero, all normals are set to zeros as well. size_t j = i << 2;
stl_allocate(&stl); its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
size_t i_face = 0; }
for (size_t i = 0; i < data.vertices.size(); ++ i) { int indices[4];
for (size_t i = 0; i < data.vertices.size();)
if (data.vertices[i].coordIdx == -1) if (data.vertices[i].coordIdx == -1)
continue; ++ i;
stl_facet &facet = stl.facet_start[i_face ++]; else {
size_t num_normals = 0; int cnt = 0;
stl_normal normal(stl_normal::Zero()); while (i < data.vertices.size())
for (unsigned int v = 0; v < 3; ++ v) { if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
const ObjParser::ObjVertex &vertex = data.vertices[i++]; break;
memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float)); } else {
if (vertex.normalIdx != -1) { assert(cnt < 4);
normal(0) += data.normals[vertex.normalIdx*3]; if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) {
normal(1) += data.normals[vertex.normalIdx*3+1]; BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
normal(2) += data.normals[vertex.normalIdx*3+2]; return false;
++ num_normals; }
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]);
} }
} }
// 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) { *meshptr = TriangleMesh(std::move(its));
// This is a quad. Produce the other triangle. if (meshptr->empty()) {
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;
}
}
} 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) {
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path; BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false; return false;
} }
if (meshptr->volume() < 0)
meshptr->flip_triangles();
return true; return true;
} }

View File

@ -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

View File

@ -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

View File

@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
!opt_mirror_x || !opt_mirror_y || !opt_orient) !opt_mirror_x || !opt_mirror_y || !opt_orient)
throw Slic3r::FileIOError("Invalid SL1 / SL1S file"); throw MissingProfileError("Invalid SL1 / SL1S file");
RasterParams rstp; RasterParams rstp;
@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height"); auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
if (!opt_layerh || !opt_init_layerh) 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()}; 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); 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( ConfigSubstitutions import_sla_archive(
const std::string & zipfname, const std::string & zipfname,
Vec2i windowsize, Vec2i windowsize,
indexed_triangle_set & out, indexed_triangle_set & out,
DynamicPrintConfig & profile, DynamicPrintConfig & profile_out,
std::function<bool(int)> progr) std::function<bool(int)> progr)
{ {
// Ensure minimum window size for marching squares // Ensure minimum window size for marching squares
windowsize.x() = std::max(2, windowsize.x()); windowsize.x() = std::max(2, windowsize.x());
windowsize.y() = std::max(2, windowsize.y()); windowsize.y() = std::max(2, windowsize.y());
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail"); std::string exclude_entries{"thumbnail"};
ConfigSubstitutions config_substitutions = profile.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable); 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()}; rstp.win = {windowsize.y(), windowsize.x()};
SliceParams slicp = get_slice_params(profile); SliceParams slicp = get_slice_params(profile_use);
std::vector<ExPolygons> slices = std::vector<ExPolygons> slices =
extract_slices_from_sla_archive(arch, rstp, progr); extract_slices_from_sla_archive(arch, rstp, progr);
@ -413,7 +423,7 @@ void fill_slicerconf(ConfMap &m, const SLAPrint &print)
} // namespace } // namespace
uqptr<sla::RasterBase> SL1Archive::create_raster() const std::unique_ptr<sla::RasterBase> SL1Archive::create_raster() const
{ {
sla::RasterBase::Resolution res; sla::RasterBase::Resolution res;
sla::RasterBase::PixelDim pxdim; sla::RasterBase::PixelDim pxdim;

View File

@ -8,11 +8,11 @@
namespace Slic3r { namespace Slic3r {
class SL1Archive: public SLAPrinter { class SL1Archive: public SLAArchive {
SLAPrinterConfig m_cfg; SLAPrinterConfig m_cfg;
protected: protected:
uqptr<sla::RasterBase> create_raster() const override; std::unique_ptr<sla::RasterBase> create_raster() const override;
sla::RasterEncoder get_encoder() const override; sla::RasterEncoder get_encoder() const override;
public: public:
@ -57,6 +57,8 @@ inline ConfigSubstitutions import_sla_archive(
return import_sla_archive(zipfname, windowsize, out, profile, progr); return import_sla_archive(zipfname, windowsize, out, profile, progr);
} }
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
} // namespace Slic3r::sla } // namespace Slic3r::sla
#endif // ARCHIVETRAITS_HPP #endif // ARCHIVETRAITS_HPP

View File

@ -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; // die "Failed to open $file\n" if !-e $path;
return false; return false;
} }
mesh.repair(); if (mesh.empty()) {
if (mesh.facets_count() == 0) {
// die "This STL file couldn't be read because it's empty.\n" // die "This STL file couldn't be read because it's empty.\n"
return false; return false;
} }

View File

@ -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(); 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_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
if (result != nullptr) { if (result != nullptr) {
@ -1842,11 +1843,7 @@ namespace ProcessLayer
assert(m600_extruder_before_layer >= 0); assert(m600_extruder_before_layer >= 0);
// Color Change or Tool Change as Color Change. // Color Change or Tool Change as Color Change.
// add tag for processor // 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"; 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 if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer
// && !MMU1 // && !MMU1

View File

@ -2,6 +2,7 @@
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/format.hpp"
#include "GCodeProcessor.hpp" #include "GCodeProcessor.hpp"
#include <boost/log/trivial.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 INCHES_TO_MM = 25.4f;
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 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 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 float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
static const size_t MIN_EXTRUDERS_COUNT = 5; static const size_t MIN_EXTRUDERS_COUNT = 5;
@ -184,10 +183,8 @@ void GCodeProcessor::TimeMachine::reset()
enabled = false; enabled = false;
acceleration = 0.0f; acceleration = 0.0f;
max_acceleration = 0.0f; max_acceleration = 0.0f;
#if ENABLE_RETRACT_ACCELERATION
retract_acceleration = 0.0f; retract_acceleration = 0.0f;
max_retract_acceleration = 0.0f; max_retract_acceleration = 0.0f;
#endif // ENABLE_RETRACT_ACCELERATION
travel_acceleration = 0.0f; travel_acceleration = 0.0f;
max_travel_acceleration = 0.0f; max_travel_acceleration = 0.0f;
extrude_factor_override_percentage = 1.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; 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) 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") }; FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
@ -740,9 +725,7 @@ void GCodeProcessor::Result::reset() {
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); 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>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
time = 0; time = 0;
} }
#else #else
@ -756,18 +739,19 @@ void GCodeProcessor::Result::reset() {
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); 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>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
} }
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = { const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = {
{ EProducer::PrusaSlicer, "PrusaSlicer" }, { EProducer::PrusaSlicer, "generated by PrusaSlicer" },
{ EProducer::Slic3rPE, "Slic3r Prusa Edition" }, { EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
{ EProducer::Slic3r, "Slic3r" }, { 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::Cura, "Cura_SteamEngine" },
{ EProducer::Simplify3D, "Simplify3D" }, { EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
{ EProducer::CraftWare, "CraftWare" }, { EProducer::CraftWare, "CraftWare" },
{ EProducer::ideaMaker, "ideaMaker" }, { EProducer::ideaMaker, "ideaMaker" },
{ EProducer::KissSlicer, "KISSlicer" } { EProducer::KissSlicer, "KISSlicer" }
@ -849,26 +833,16 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.extruders_count = extruders_count; m_result.extruders_count = extruders_count;
m_extruder_offsets.resize(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); m_extruder_colors.resize(extruders_count);
for (size_t i = 0; i < extruders_count; ++i) { m_result.filament_diameters.resize(extruders_count);
m_extruder_colors[i] = static_cast<unsigned char>(i); m_result.filament_densities.resize(extruders_count);
}
m_extruder_temps.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 < extruders_count; ++ i) {
for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) { m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f);
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]); 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));
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]);
} }
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) { 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); 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].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_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); 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].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_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); 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].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_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); 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].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_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); 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].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_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); 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].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_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_result.id = ++s_result_id;
m_use_volumetric_e = false; m_use_volumetric_e = false;
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_last_default_color_id = 0; m_last_default_color_id = 0;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.reset(); m_options_z_corrector.reset();
@ -1215,6 +1183,18 @@ void GCodeProcessor::reset()
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #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) void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
{ {
CNumericLocalesSetter locales_setter; CNumericLocalesSetter locales_setter;
@ -1226,11 +1206,13 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
// pre-processing // pre-processing
// parse the gcode file to detect its producer // parse the gcode file to detect its producer
{ {
m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { m_parser.parse_file_raw(filename, [this](GCodeReader& reader, const char *begin, const char *end) {
const std::string_view cmd = line.cmd(); begin = skip_whitespaces(begin, end);
if (cmd.empty()) { if (begin != end && *begin == ';') {
const std::string_view comment = line.comment(); // Comment.
if (comment.length() > 1 && detect_producer(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.quit_parsing();
} }
}); });
@ -1249,6 +1231,10 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
} }
else if (m_producer == EProducer::Simplify3D) else if (m_producer == EProducer::Simplify3D)
apply_config_simplify3d(filename); 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 // process gcode
@ -1267,7 +1253,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
this->process_gcode_line(line, true); 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) 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 // update width/height of wipe moves
for (MoveVertex& move : m_result.moves) { for (MoveVertex& move : m_result.moves) {
@ -1323,6 +1310,7 @@ void GCodeProcessor::finalize()
m_width_compare.output(); m_width_compare.output();
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
if (post_process)
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends); m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
#if ENABLE_GCODE_VIEWER_STATISTICS #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(); m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
@ -1380,6 +1368,41 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
return ret; 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 std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
@ -1398,9 +1421,11 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
}; };
BedSize bed_size; BedSize bed_size;
bool producer_detected = false;
m_parser.parse_file(filename, [this, &bed_size](GCodeReader& reader, const GCodeReader::GCodeLine& line) { m_parser.parse_file_raw(filename, [this, &bed_size, &producer_detected](GCodeReader& reader, const char* begin, const char* end) {
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); size_t pos = cmt.find(key);
if (pos != cmt.npos) { if (pos != cmt.npos) {
pos = cmt.find(',', pos); pos = cmt.find(',', pos);
@ -1412,12 +1437,12 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
return false; 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); size_t pos = cmt.find(key);
if (pos != cmt.npos) { if (pos != cmt.npos) {
pos = cmt.find(',', pos); pos = cmt.find(',', pos);
if (pos != cmt.npos) { 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; std::vector<std::string> values_str;
boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on); boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
for (const std::string& s : values_str) { for (const std::string& s : values_str) {
@ -1429,8 +1454,15 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
return false; return false;
}; };
const std::string& comment = line.raw(); begin = skip_whitespaces(begin, end);
if (comment.length() > 2 && comment.front() == ';') { 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) if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
extract_double(comment, "strokeXoverride", bed_size.x); extract_double(comment, "strokeXoverride", bed_size.x);
else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos) else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
@ -1438,16 +1470,21 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
else if (comment.find("filamentDiameters") != comment.npos) { else if (comment.find("filamentDiameters") != comment.npos) {
m_result.filament_diameters.clear(); m_result.filament_diameters.clear();
extract_floats(comment, "filamentDiameters", m_result.filament_diameters); extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
} } else if (comment.find("filamentDensities") != comment.npos) {
else if (comment.find("filamentDensities") != comment.npos) {
m_result.filament_densities.clear(); m_result.filament_densities.clear();
extract_floats(comment, "filamentDensities", m_result.filament_densities); extract_floats(comment, "filamentDensities", m_result.filament_densities);
} } else if (comment.find("extruderDiameter") != comment.npos) {
else if (comment.find("extruderDiameter") != comment.npos) {
std::vector<float> extruder_diameters; std::vector<float> extruder_diameters;
extract_floats(comment, "extruderDiameter", extruder_diameters); extract_floats(comment, "extruderDiameter", extruder_diameters);
m_result.extruders_count = extruder_diameters.size(); 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();
}
} }
}); });
@ -1741,7 +1778,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// color change tag // color change tag
if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) { if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
unsigned char extruder_id = 0; unsigned char extruder_id = 0;
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
static std::vector<std::string> Default_Colors = { static std::vector<std::string> Default_Colors = {
"#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish "#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish
"#1C8891", // { 0.110f, 0.533f, 0.569f }, "#1C8891", // { 0.110f, 0.533f, 0.569f },
@ -1790,16 +1826,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
if (m_last_default_color_id == Default_Colors.size()) if (m_last_default_color_id == Default_Colors.size())
m_last_default_color_id = 0; 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()) 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 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 +1836,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
if (m_extruder_id == extruder_id) { if (m_extruder_id == extruder_id) {
m_cp_color.current = m_extruder_colors[extruder_id]; m_cp_color.current = m_extruder_colors[extruder_id];
store_move_vertex(EMoveType::Color_change); 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, "" }; 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); m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_PREVIEW_OPTIONS_Z
@ -1818,27 +1843,19 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
process_custom_gcode_time(CustomGCode::ColorChange); process_custom_gcode_time(CustomGCode::ColorChange);
process_filaments(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; return;
} }
// pause print tag // pause print tag
if (comment == reserved_tag(ETags::Pause_Print)) { if (comment == reserved_tag(ETags::Pause_Print)) {
store_move_vertex(EMoveType::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, "", "" }; 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); m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.set(); m_options_z_corrector.set();
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
process_custom_gcode_time(CustomGCode::PausePrint); process_custom_gcode_time(CustomGCode::PausePrint);
return; return;
} }
@ -1846,13 +1863,11 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// custom code tag // custom code tag
if (comment == reserved_tag(ETags::Custom_Code)) { if (comment == reserved_tag(ETags::Custom_Code)) {
store_move_vertex(EMoveType::Custom_GCode); 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, "", "" }; 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); m_result.custom_gcode_per_print_z.emplace_back(item);
#if ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_PREVIEW_OPTIONS_Z
m_options_z_corrector.set(); m_options_z_corrector.set();
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
return; return;
} }
@ -1878,6 +1893,9 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
{ {
case EProducer::Slic3rPE: case EProducer::Slic3rPE:
case EProducer::Slic3r: 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::PrusaSlicer: { return process_prusaslicer_tags(comment); }
case EProducer::Cura: { return process_cura_tags(comment); } case EProducer::Cura: { return process_cura_tags(comment); }
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); } case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
@ -2707,15 +2725,15 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line)
std::string_view cmd = line.cmd(); std::string_view cmd = line.cmd();
std::string new_line_raw = { cmd.data(), cmd.size() }; std::string new_line_raw = { cmd.data(), cmd.size() };
bool found = false; bool found = false;
if (line.has_x()) { if (line.has('X')) {
new_line_raw += " X0"; new_line_raw += " X0";
found = true; found = true;
} }
if (line.has_y()) { if (line.has('Y')) {
new_line_raw += " Y0"; new_line_raw += " Y0";
found = true; found = true;
} }
if (line.has_z()) { if (line.has('Z')) {
new_line_raw += " Z0"; new_line_raw += " Z0";
found = true; found = true;
} }
@ -2843,6 +2861,8 @@ void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
else else
m_extruder_temps[m_extruder_id] = new_temp; m_extruder_temps[m_extruder_id] = new_temp;
} }
else if (line.has_value('S', new_temp))
m_extruder_temps[m_extruder_id] = new_temp;
} }
void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
@ -2851,16 +2871,16 @@ void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md // 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 // 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; m_origin[X] = 0.0f;
if (line.has_y()) if (line.has('Y'))
m_origin[Y] = 0.0f; m_origin[Y] = 0.0f;
if (line.has_z()) if (line.has('Z'))
m_origin[Z] = 0.0f; m_origin[Z] = 0.0f;
if (line.has_e()) if (line.has('E'))
m_origin[E] = 0.0f; m_origin[E] = 0.0f;
} }
@ -2943,22 +2963,14 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('T', value)) if (line.has_value('T', value))
#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); 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 { else {
// New acceleration format, compatible with the upstream Marlin. // New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value)) if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('R', value)) if (line.has_value('R', value))
#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); 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)) if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format. // Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value); set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
@ -3029,7 +3041,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
// https://github.com/repetier/Repetier-Firmware/blob/master/src/ArduinoAVR/Repetier/Printer.cpp // 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) // 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; float p = FLT_MAX;
for (unsigned char a = X; a <= Z; ++a) { for (unsigned char a = X; a <= Z; ++a) {
@ -3216,20 +3228,12 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
} }
} }
#if ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{ {
size_t id = static_cast<size_t>(mode); 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; 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) void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{ {
size_t id = static_cast<size_t>(mode); size_t id = static_cast<size_t>(mode);
@ -3239,7 +3243,6 @@ void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMod
std::min(value, m_time_processor.machines[id].max_retract_acceleration); std::min(value, m_time_processor.machines[id].max_retract_acceleration);
} }
} }
#endif // ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{ {

View File

@ -242,11 +242,9 @@ namespace Slic3r {
float acceleration; // mm/s^2 float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp. // hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2 float max_acceleration; // mm/s^2
#if ENABLE_RETRACT_ACCELERATION
float retract_acceleration; // mm/s^2 float retract_acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp. // hard limit for the acceleration, to which the firmware will clamp.
float max_retract_acceleration; // mm/s^2 float max_retract_acceleration; // mm/s^2
#endif // ENABLE_RETRACT_ACCELERATION
float travel_acceleration; // mm/s^2 float travel_acceleration; // mm/s^2
// hard limit for the travel acceleration, to which the firmware will clamp. // hard limit for the travel acceleration, to which the firmware will clamp.
float max_travel_acceleration; // mm/s^2 float max_travel_acceleration; // mm/s^2
@ -359,9 +357,7 @@ namespace Slic3r {
std::vector<float> filament_diameters; std::vector<float> filament_diameters;
std::vector<float> filament_densities; std::vector<float> filament_densities;
PrintEstimatedStatistics print_statistics; PrintEstimatedStatistics print_statistics;
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> custom_gcode_per_print_z; std::vector<CustomGCode::Item> custom_gcode_per_print_z;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 }; int64_t time{ 0 };
@ -536,9 +532,7 @@ namespace Slic3r {
#if ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_PREVIEW_OPTIONS_Z
OptionsZCorrector m_options_z_corrector; OptionsZCorrector m_options_z_corrector;
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
size_t m_last_default_color_id; size_t m_last_default_color_id;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time; std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -549,6 +543,9 @@ namespace Slic3r {
PrusaSlicer, PrusaSlicer,
Slic3rPE, Slic3rPE,
Slic3r, Slic3r,
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
SuperSlicer,
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
Cura, Cura,
Simplify3D, Simplify3D,
CraftWare, CraftWare,
@ -585,14 +582,14 @@ namespace Slic3r {
const Result& get_result() const { return m_result; } const Result& get_result() const { return m_result; }
Result&& extract_result() { return std::move(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). // 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); 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. // Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
void initialize(const std::string& filename); void initialize(const std::string& filename);
void process_buffer(const std::string& buffer); void process_buffer(const std::string& buffer);
void finalize(); void finalize(bool post_process);
float get_time(PrintEstimatedStatistics::ETimeMode mode) const; float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
@ -605,6 +602,9 @@ namespace Slic3r {
private: private:
void apply_config(const DynamicPrintConfig& config); void apply_config(const DynamicPrintConfig& config);
void apply_config_simplify3d(const std::string& filename); 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); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
// Process tags embedded into comments // 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_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(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; float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
#if ENABLE_RETRACT_ACCELERATION
void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
#endif // ENABLE_RETRACT_ACCELERATION
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;

View File

@ -2,9 +2,11 @@
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/nowide/fstream.hpp> #include <boost/nowide/fstream.hpp>
#include <boost/nowide/cstdio.hpp>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include "Utils.hpp"
#include "LocalesUtils.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); FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
f.sync_with_stdio(false);
// Read the input stream 64kB at a time, extract lines and process them.
std::vector<char> buffer(65536 * 10, 0); std::vector<char> buffer(65536 * 10, 0);
std::string line; // Line buffer.
std::string gcode_line;
size_t file_pos = 0;
m_parsing = true; m_parsing = true;
GCodeLine gline; for (;;) {
while (m_parsing && ! f.eof()) { size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
f.read(buffer.data(), buffer.size()); if (::ferror(in.f))
if (! f.eof() && ! f.good())
// Reading the input file failed.
return false; return false;
bool eof = cnt_read == 0;
auto it = buffer.begin(); auto it = buffer.begin();
auto it_bufend = buffer.begin() + f.gcount(); auto it_bufend = buffer.begin() + cnt_read;
while (it != it_bufend) { while (it != it_bufend || (eof && ! gcode_line.empty())) {
// Find end of line.
bool eol = false; bool eol = false;
auto it_end = it; auto it_end = it;
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ; for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end)
eol |= f.eof() && it_end == it_bufend; 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) { if (eol) {
gline.reset(); if (gcode_line.empty())
if (line.empty()) parse_line_callback(&(*it), &(*it_end));
this->parse_line(&(*it), &(*it_end), gline, callback);
else { else {
line.insert(line.end(), it, it_end); gcode_line.insert(gcode_line.end(), it, it_end);
this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); parse_line_callback(gcode_line.c_str(), gcode_line.c_str() + gcode_line.size());
line.clear(); gcode_line.clear();
} }
if (! m_parsing)
// The callback wishes to exit.
return true;
} else } else
line.insert(line.end(), it, it_end); gcode_line.insert(gcode_line.end(), it, it_end);
// Skip all the empty lines. // Skip EOL.
for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ; 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; 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 bool GCodeReader::GCodeLine::has(char axis) const
{ {
const char *c = m_raw.c_str(); const char *c = m_raw.c_str();

View File

@ -76,6 +76,7 @@ public:
}; };
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t; 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(); } GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
void reset() { memset(m_position, 0, sizeof(m_position)); } void reset() { memset(m_position, 0, sizeof(m_position)); }
@ -114,6 +115,13 @@ public:
// Returns false if reading the file failed. // Returns false if reading the file failed.
bool parse_file(const std::string &file, callback_t callback); 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; } void quit_parsing() { m_parsing = false; }
float& x() { return m_position[X]; } float& x() { return m_position[X]; }
@ -132,6 +140,11 @@ public:
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; } // void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
private: 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); 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); void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
@ -154,6 +167,7 @@ private:
char m_extrusion_axis; char m_extrusion_axis;
float m_position[NUM_AXES]; float m_position[NUM_AXES];
bool m_verbose; bool m_verbose;
// To be set by the callback to stop parsing.
bool m_parsing{ false }; bool m_parsing{ false };
}; };

View File

@ -1,7 +1,6 @@
#include "GCodeWriter.hpp" #include "GCodeWriter.hpp"
#include "CustomGCode.hpp" #include "CustomGCode.hpp"
#include <algorithm> #include <algorithm>
#include <charconv>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <map> #include <map>
@ -11,15 +10,8 @@
#include <boost/spirit/include/karma.hpp> #include <boost/spirit/include/karma.hpp>
#endif #endif
#define XYZF_EXPORT_DIGITS 3
#define E_EXPORT_DIGITS 5
#define FLAVOR_IS(val) this->config.gcode_flavor == val #define FLAVOR_IS(val) this->config.gcode_flavor == val
#define FLAVOR_IS_NOT(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 { namespace Slic3r {
@ -261,115 +253,12 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
return gcode.str(); 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 std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
{ {
assert(F > 0.); assert(F > 0.);
assert(F < 100000.); assert(F < 100000.);
G1Writer w; GCodeG1Formatter w;
w.emit_f(F); w.emit_f(F);
w.emit_comment(this->config.gcode_comments, comment); w.emit_comment(this->config.gcode_comments, comment);
w.emit_string(cooling_marker); 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(0) = point(0);
m_pos(1) = point(1); m_pos(1) = point(1);
G1Writer w; GCodeG1Formatter w;
w.emit_xy(point); w.emit_xy(point);
w.emit_f(this->config.travel_speed.value * 60.0); w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment); 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_lifted = 0;
m_pos = point; m_pos = point;
G1Writer w; GCodeG1Formatter w;
w.emit_xyz(point); w.emit_xyz(point);
w.emit_f(this->config.travel_speed.value * 60.0); w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment); 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.) if (speed == 0.)
speed = this->config.travel_speed.value; speed = this->config.travel_speed.value;
G1Writer w; GCodeG1Formatter w;
w.emit_z(z); w.emit_z(z);
w.emit_f(speed * 60.0); w.emit_f(speed * 60.0);
w.emit_comment(this->config.gcode_comments, comment); 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_pos(1) = point(1);
m_extruder->extrude(dE); m_extruder->extrude(dE);
G1Writer w; GCodeG1Formatter w;
w.emit_xy(point); w.emit_xy(point);
w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment); 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_lifted = 0;
m_extruder->extrude(dE); m_extruder->extrude(dE);
G1Writer w; GCodeG1Formatter w;
w.emit_xyz(point); w.emit_xyz(point);
w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment); 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::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 /* If firmware retraction is enabled, we use a fake value of 1
since we ignore the actual configured retract_length which since we ignore the actual configured retract_length which
might be 0, in which case the retraction logic gets skipped. */ 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 we use volumetric E values we turn lengths into volumes */
if (this->config.use_volumetric_e) { 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; 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 (this->config.use_firmware_retraction) {
if (FLAVOR_IS(gcfMachinekit)) gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
gcode << "G22 ; retract\n";
else
gcode << "G10 ; retract\n";
} else if (! m_extrusion_axis.empty()) { } else if (! m_extrusion_axis.empty()) {
gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) GCodeG1Formatter w;
<< " F" << XYZF_NUM(m_extruder->retract_speed() * 60.); w.emit_e(m_extrusion_axis, m_extruder->E());
COMMENT(comment); w.emit_f(m_extruder->retract_speed() * 60.);
gcode << "\n"; w.emit_comment(this->config.gcode_comments, comment);
gcode = w.string();
} }
} }
if (FLAVOR_IS(gcfMakerWare)) if (FLAVOR_IS(gcfMakerWare))
gcode << "M103 ; extruder off\n"; gcode += "M103 ; extruder off\n";
return gcode.str(); return gcode;
} }
std::string GCodeWriter::unretract() std::string GCodeWriter::unretract()
{ {
std::ostringstream gcode; std::string gcode;
if (FLAVOR_IS(gcfMakerWare)) if (FLAVOR_IS(gcfMakerWare))
gcode << "M101 ; extruder on\n"; gcode = "M101 ; extruder on\n";
double dE = m_extruder->unretract(); if (double dE = m_extruder->unretract(); dE != 0) {
if (dE != 0) {
if (this->config.use_firmware_retraction) { if (this->config.use_firmware_retraction) {
if (FLAVOR_IS(gcfMachinekit)) gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
gcode << "G23 ; unretract\n"; gcode += this->reset_e();
else
gcode << "G11 ; unretract\n";
gcode << this->reset_e();
} else if (! m_extrusion_axis.empty()) { } else if (! m_extrusion_axis.empty()) {
// use G1 instead of G0 because G0 will blend the restart with the previous travel move // 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()) GCodeG1Formatter w;
<< " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.); w.emit_e(m_extrusion_axis, m_extruder->E());
if (this->config.gcode_comments) gcode << " ; unretract"; w.emit_f(m_extruder->deretract_speed() * 60.);
gcode << "\n"; 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(), /* 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); 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

View File

@ -3,6 +3,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include <string> #include <string>
#include <charconv>
#include "Extruder.hpp" #include "Extruder.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
@ -93,6 +94,84 @@ private:
std::string _retract(double length, double restart_extra, const std::string &comment); 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 */ } /* namespace Slic3r */
#endif /* slic3r_GCodeWriter_hpp_ */ #endif /* slic3r_GCodeWriter_hpp_ */

View File

@ -20,6 +20,12 @@
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW
#endif
#include <boost/multiprecision/integer.hpp>
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
#include "SVG.hpp" #include "SVG.hpp"
#endif #endif
@ -1543,4 +1549,205 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
return (axis.z() < 0) ? -angle : angle; return (axis.z() < 0) ? -angle : angle;
} }
} } namespace rotcalip {
using int256_t = boost::multiprecision::int256_t;
using int128_t = boost::multiprecision::int128_t;
template<class Scalar = int64_t>
inline Scalar magnsq(const Point &p)
{
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
}
template<class Scalar = int64_t>
inline Scalar dot(const Point &a, const Point &b)
{
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
}
template<class Scalar = int64_t>
inline Scalar dotperp(const Point &a, const Point &b)
{
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
}
using boost::multiprecision::abs;
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
// if they are equal and 1 if alpha is greater than beta. Note that dir is
// reversed for beta, because it represents the opposite side of a caliper.
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
int128_t dotA = dot(dir, dirA);
int128_t dotB = dot(-dir, dirB);
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
int256_t diff = dcosa - dcosb;
return diff > 0? -1 : (diff < 0 ? 1 : 0);
}
// A helper class to navigate on a polygon. Given a vertex index, one can
// get the edge belonging to that vertex, the coordinates of the vertex, the
// next and previous edges. Stuff that is needed in the rotating calipers algo.
class Idx
{
size_t m_idx;
const Polygon *m_poly;
public:
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
size_t idx() const { return m_idx; }
void set_idx(size_t i) { m_idx = i; }
size_t next() const { return (m_idx + 1) % m_poly->size(); }
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
Point prev_dir() const {
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
}
const Point &pt() const { return (*m_poly)[m_idx]; }
const Point dir() const { return (*m_poly)[next()] - pt(); }
const Point next_dir() const
{
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
}
const Polygon &poly() const { return *m_poly; }
};
enum class AntipodalVisitMode { Full, EdgesOnly };
// Visit all antipodal pairs starting from the initial ia, ib pair which
// has to be a valid antipodal pair (not checked). fn is called for every
// antipodal pair encountered including the initial one.
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
// where i,j are the vertex indices of the antipodal pair and dir is the
// direction of the calipers touching the i vertex.
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
{
// Set current caliper direction to be the lower edge angle from X axis
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
Idx *initial = current;
bool visitor_continue = true;
size_t start = initial->idx();
bool finished = false;
while (visitor_continue && !finished) {
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
// Parallel edges encountered. An additional pair of antipodals
// can be yielded.
if constexpr (mode == AntipodalVisitMode::Full)
if (cmp == 0 && visitor_continue) {
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
current == &ib ? ib.idx() : ib.next(),
current_dir_a);
}
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
current->inc();
if (cmp > 0) {
std::swap(current, other);
}
if (initial->idx() == start) finished = true;
}
}
} // namespace rotcalip
bool intersects(const Polygon &A, const Polygon &B)
{
using namespace rotcalip;
// Establish starting antipodals as extremes in XY plane. Use the
// easily obtainable bounding boxes to check if A and B is disjoint
// and return false if the are.
struct BB
{
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
const Polygon &P;
static bool cmpy(const Point &l, const Point &u)
{
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
}
BB(const Polygon &poly): P{poly}
{
for (size_t i = 0; i < P.size(); ++i) {
if (P[i] < P[xmin]) xmin = i;
if (P[xmax] < P[i]) xmax = i;
if (cmpy(P[i], P[ymin])) ymin = i;
if (cmpy(P[ymax], P[i])) ymax = i;
}
}
};
BB bA{A}, bB{B};
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
// if (!bbA.overlap(bbB))
// return false;
// Establish starting antipodals as extreme vertex pairs in X or Y direction
// which reside on different polygons. If no such pair is found, the two
// polygons are certainly not disjoint.
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
if (&imin.poly() == &imax.poly()) {
imin = Idx{bA.ymin, A};
imax = Idx{bB.ymax, B};
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
}
if (&imin.poly() == &imax.poly())
return true;
bool found_divisor = false;
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
imin, imax,
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
// std::cout << "A" << ia << " B" << ib << " dir " <<
// dir.x() << " " << dir.y() << std::endl;
const Polygon &A = imin.poly(), &B = imax.poly();
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
// If both reference points are on the left (or right) of their
// respective support lines and the opposite support line is to
// the right (or left), the divisor line is found. We only test
// the reference point, as by definition, if that is on one side,
// all the other points must be on the same side of a support
// line. If the support lines are collinear, the polygons must be
// on the same side of their respective support lines.
auto d = dotperp(dir, B[ib] - A[ia]);
if (d == 0) {
// The caliper lines are collinear, not just parallel
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
} else if (d > 0) { // B is to the left of (A, A+1)
found_divisor = !is_left_a && !is_left_b;
} else { // B is to the right of (A, A+1)
found_divisor = is_left_a && is_left_b;
}
return !found_divisor;
});
// Intersects if the divisor was not found
return !found_divisor;
}
}} // namespace Slic3r::Geometry

View File

@ -532,6 +532,10 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
} }
// Returns true if the intersection of the two convex polygons A and B
// is not an empty set.
bool intersects(const Polygon &A, const Polygon &B);
} } // namespace Slicer::Geometry } } // namespace Slicer::Geometry
#endif #endif

View File

@ -1,7 +1,13 @@
#include "LocalesUtils.hpp" #include "LocalesUtils.hpp"
#ifdef _WIN32
#include <charconv>
#endif
#include <stdexcept> #include <stdexcept>
#include <fast_float/fast_float.h>
namespace Slic3r { namespace Slic3r {
@ -11,15 +17,15 @@ CNumericLocalesSetter::CNumericLocalesSetter()
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE); _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr); m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
std::setlocale(LC_NUMERIC, "C"); std::setlocale(LC_NUMERIC, "C");
#elif __linux__ #elif __APPLE__
m_original_locale = uselocale((locale_t)0);
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
uselocale(m_new_locale);
#else // linux / BSD
m_original_locale = uselocale((locale_t)0); m_original_locale = uselocale((locale_t)0);
m_new_locale = duplocale(m_original_locale); 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_new_locale);
uselocale(m_new_locale); uselocale(m_new_locale);
#else // APPLE
m_original_locale = uselocale((locale_t)0);
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
uselocale(m_new_locale);
#endif #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; double out;
std::istringstream stream(str); size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data();
if (! (stream >> out)) if (pos)
throw std::invalid_argument("string_to_double_decimal_point conversion failed."); *pos = p;
if (pos) {
if (stream.eof())
*pos = str.size();
else
*pos = stream.tellg();
}
return out; return out;
} }
std::string float_to_string_decimal_point(double value, int precision/* = -1*/) 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; std::stringstream buf;
if (precision >= 0) if (precision >= 0)
buf << std::fixed << std::setprecision(precision); buf << std::fixed << std::setprecision(precision);
buf << value; buf << value;
return buf.str(); return buf.str();
#endif
} }

View File

@ -5,6 +5,7 @@
#include <clocale> #include <clocale>
#include <iomanip> #include <iomanip>
#include <cassert> #include <cassert>
#include <string_view>
#ifdef __APPLE__ #ifdef __APPLE__
#include <xlocale.h> #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.) // (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(double value, int precision = -1);
//std::string float_to_string_decimal_point(float 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 } // namespace Slic3r

View File

@ -29,18 +29,17 @@ TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
{ {
auto &VC = emesh.first; auto &FC = emesh.second; auto &VC = emesh.first; auto &FC = emesh.second;
Pointf3s points(size_t(VC.rows())); indexed_triangle_set its;
std::vector<Vec3i> facets(size_t(FC.rows())); its.vertices.reserve(size_t(VC.rows()));
its.indices.reserve(size_t(FC.rows()));
for (Eigen::Index i = 0; i < VC.rows(); ++i) 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) 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}; return TriangleMesh { std::move(its) };
out.require_shared_vertices();
return out;
} }
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh) 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))); 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; CGAL::Cartesian_converter<EpecKernel, EpicKernel> cvt;
auto iv = cvt(v); 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) template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{ {
Pointf3s points; indexed_triangle_set its;
std::vector<Vec3i> facets; its.vertices.reserve(cgalmesh.num_vertices());
points.reserve(cgalmesh.num_vertices()); its.indices.reserve(cgalmesh.num_faces());
facets.reserve(cgalmesh.num_faces());
for (auto &vi : cgalmesh.vertices()) { for (auto &vi : cgalmesh.vertices()) {
auto &v = cgalmesh.point(vi); // Don't ask... 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()) { for (auto &face : cgalmesh.faces()) {
@ -166,12 +164,10 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
} }
if (i == 3) if (i == 3)
facets.emplace_back(facet); its.indices.emplace_back(facet);
} }
TriangleMesh out{points, facets}; return TriangleMesh(std::move(its));
out.repair();
return out;
} }
std::unique_ptr<CGALMesh, CGALMeshDeleter> std::unique_ptr<CGALMesh, CGALMeshDeleter>

View File

@ -28,65 +28,84 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
} }
}; };
// Visit all unvisited neighboring facets that are reachable from the first unvisited facet, // Discover connected patches of facets one by one.
// and return them.
template<class NeighborIndex> template<class NeighborIndex>
std::vector<size_t> its_find_unvisited_neighbors( struct NeighborVisitor {
const indexed_triangle_set &its, NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
const NeighborIndex & neighbor_index, its(its), neighbor_index(neighbor_index) {
std::vector<char> & visited) 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());
}
template<typename Visitor>
void visit(Visitor visitor)
{ {
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 // find the next unvisited facet and push the index
auto facet = std::find(visited.begin(), visited.end(), false); auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
std::vector<size_t> ret; m_seed = facet - m_visited.begin();
if (facet != visited.end()) { if (facet != m_visited.end()) {
ret.reserve(its.indices.size()); // Skip this element in the next round.
auto idx = size_t(facet - visited.begin()); auto idx = m_seed ++;
push(idx); if (! visitor(idx))
ret.emplace_back(idx); return;
visited[idx] = true; 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));
}
} }
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));
} }
} }
} }
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 } // 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 template<class IndexT> struct ItsNeighborsWrapper
{ {
using Index = IndexT; using Index = IndexT;
const indexed_triangle_set *its; const indexed_triangle_set &its;
IndexT index; const IndexT &index_ref;
const IndexT index;
ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx) // Keeping a reference to index, the caller is responsible for keeping the index alive.
: its{&m}, index{std::move(idx)} 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_its() const noexcept { return its; }
const auto& get_index() const noexcept { return index; } const auto& get_index() const noexcept { return index_ref; }
}; };
// Splits a mesh into multiple meshes when possible. // 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); const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
std::vector<char> visited(its.indices.size(), false);
struct VertexConv { struct VertexConv {
size_t part_id = std::numeric_limits<size_t>::max(); size_t part_id = std::numeric_limits<size_t>::max();
size_t vertex_image; size_t vertex_image;
}; };
std::vector<VertexConv> vidx_conv(its.vertices.size()); 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) { for (size_t part_id = 0;; ++part_id) {
std::vector<size_t> facets = // Collect all faces of the next patch.
its_find_unvisited_neighbors(its, neighbor_index, visited); facets.clear();
visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
if (facets.empty()) if (facets.empty())
break; break;
@ -150,17 +168,34 @@ std::vector<indexed_triangle_set> its_split(const Its &its)
return ret; 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; meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m); bool has_some = false;
const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m); 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;
}
std::vector<char> visited(its.indices.size(), false); template<class Its>
its_find_unvisited_neighbors(its, neighbor_index, visited); size_t its_number_of_patches(const Its &m)
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));
return !faces.empty(); 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> template<class ExPolicy>

View File

@ -9,7 +9,6 @@
#include "Format/AMF.hpp" #include "Format/AMF.hpp"
#include "Format/OBJ.hpp" #include "Format/OBJ.hpp"
#include "Format/PRUS.hpp"
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "Format/3mf.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")) else if (boost::algorithm::iends_with(input_file, ".3mf"))
//FIXME options & LoadAttribute::CheckVersion ? //FIXME options & LoadAttribute::CheckVersion ?
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false); 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 else
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); 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) 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) for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) { 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) { for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_meters); assert(! v->source.is_converted_from_meters);
v->source.is_converted_from_inches = true; 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; static constexpr const double m_to_mm = 1000;
for (ModelObject* obj : this->objects) for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) { 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) { for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_inches); assert(! v->source.is_converted_from_inches);
v->source.is_converted_from_meters = true; 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! // 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) { for (ModelVolume *v : this->volumes) {
v->scale_geometry_after_creation(versor); v->scale_geometry_after_creation(scale);
v->set_offset(versor.cwiseProduct(v->get_offset())); v->set_offset(Vec3d(scale, scale, scale).cwiseProduct(v->get_offset()));
} }
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
@ -1080,9 +1077,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
ModelObject* new_object = new_clone(*this); ModelObject* new_object = new_clone(*this);
double koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4 : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787 : 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 : conv_type == ConversionType::CONV_TO_METER ? 0.001 : 1; conv_type == ConversionType::CONV_FROM_METER ? 1000.f : conv_type == ConversionType::CONV_TO_METER ? 0.001f : 1.f;
const Vec3d versor = Vec3d(koef, koef, koef);
new_object->set_model(nullptr); new_object->set_model(nullptr);
new_object->sla_support_points.clear(); new_object->sla_support_points.clear();
@ -1095,7 +1091,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
for (ModelVolume* volume : volumes) { for (ModelVolume* volume : volumes) {
if (!volume->mesh().empty()) { if (!volume->mesh().empty()) {
TriangleMesh mesh(volume->mesh()); TriangleMesh mesh(volume->mesh());
mesh.require_shared_vertices();
ModelVolume* vol = new_object->add_volume(mesh); ModelVolume* vol = new_object->add_volume(mesh);
vol->name = volume->name; 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 && if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() || (volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) { std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
vol->scale_geometry_after_creation(versor); vol->scale_geometry_after_creation(koef);
vol->set_offset(versor.cwiseProduct(volume->get_offset())); vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH) 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; 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) 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; size_t num = 0;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
num += v->mesh().stl.stats.number_of_facets; num += v->mesh().facets_count();
return num; return num;
} }
@ -1167,14 +1162,6 @@ size_t ModelObject::parts_count() const
return num; 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) ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
{ {
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) 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; TriangleMesh upper_mesh, lower_mesh;
{ {
indexed_triangle_set upper_its, lower_its; indexed_triangle_set upper_its, lower_its;
mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its); 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 = TriangleMesh(upper_its);
upper_mesh.repair(); if (attributes.has(ModelObjectCutAttribute::KeepLower))
upper_mesh.reset_repair_stats();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
lower_mesh = TriangleMesh(lower_its); 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); ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name; vol->name = volume->name;
// Don't copy the config's ID. // 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()); assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material()); 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); ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name; vol->name = volume->name;
// Don't copy the config's ID. // Don't copy the config's ID.
@ -1349,24 +1329,22 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (volume->type() != ModelVolumeType::MODEL_PART) if (volume->type() != ModelVolumeType::MODEL_PART)
continue; continue;
TriangleMeshPtrs meshptrs = volume->mesh().split(); std::vector<TriangleMesh> meshes = volume->mesh().split();
size_t counter = 1; size_t counter = 1;
for (TriangleMesh* mesh : meshptrs) { for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied // FIXME: crashes if not satisfied
if (mesh->facets_count() < 3) continue; if (mesh.facets_count() < 3)
continue;
mesh->repair();
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? // 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(); ModelObject* new_object = m_model->add_object();
if (meshptrs.size() == 1) { if (meshes.size() == 1) {
new_object->name = volume->name; new_object->name = volume->name;
// Don't copy the config's ID. // Don't copy the config's ID.
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config); new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
} }
else { 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. // Don't copy the config's ID.
new_object->config.assign_config(this->config); new_object->config.assign_config(this->config);
} }
@ -1375,7 +1353,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
new_object->instances.reserve(this->instances.size()); new_object->instances.reserve(this->instances.size());
for (const ModelInstance* model_instance : this->instances) for (const ModelInstance* model_instance : this->instances)
new_object->add_instance(*model_instance); 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) 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 // reset the source to disable reload from disk
new_vol->source = ModelVolume::Source(); new_vol->source = ModelVolume::Source();
new_objects->emplace_back(new_object); new_objects->emplace_back(new_object);
delete mesh;
} }
} }
} }
@ -1405,7 +1382,6 @@ void ModelObject::merge()
for (ModelVolume* volume : volumes) for (ModelVolume* volume : volumes)
if (!volume->mesh().empty()) if (!volume->mesh().empty())
mesh.merge(volume->mesh()); mesh.merge(volume->mesh());
mesh.repair();
this->clear_volumes(); this->clear_volumes();
ModelVolume* vol = this->add_volume(mesh); 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 Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull(); 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) 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); 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 Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull(); 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) 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); 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; boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh(); TriangleMesh mesh = this->raw_mesh();
mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box(); BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size(); Vec3d size = bb.size();
cout << "size_x = " << size(0) << endl; cout << "size_x = " << size(0) << endl;
@ -1584,26 +1559,26 @@ void ModelObject::print_info() const
cout << "max_x = " << bb.max(0) << endl; cout << "max_x = " << bb.max(0) << endl;
cout << "max_y = " << bb.max(1) << endl; cout << "max_y = " << bb.max(1) << endl;
cout << "max_z = " << bb.max(2) << endl; cout << "max_z = " << bb.max(2) << endl;
cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl; cout << "number_of_facets = " << mesh.facets_count() << endl;
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
mesh.repair(); // this calculates number_of_parts cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
if (mesh.needed_repair()) { if (! mesh.stats().manifold())
mesh.repair(); cout << "open_edges = " << mesh.stats().open_edges << endl;
if (mesh.stl.stats.degenerate_facets > 0)
cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl; if (mesh.stats().repaired()) {
if (mesh.stl.stats.edges_fixed > 0) const RepairedMeshErrors& stats = mesh.stats().repaired_errors;
cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl; if (stats.degenerate_facets > 0)
if (mesh.stl.stats.facets_removed > 0) cout << "degenerate_facets = " << stats.degenerate_facets << endl;
cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl; if (stats.edges_fixed > 0)
if (mesh.stl.stats.facets_added > 0) cout << "edges_fixed = " << stats.edges_fixed << endl;
cout << "facets_added = " << mesh.stl.stats.facets_added << endl; if (stats.facets_removed > 0)
if (mesh.stl.stats.facets_reversed > 0) cout << "facets_removed = " << stats.facets_removed << endl;
cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl; if (stats.facets_reversed > 0)
if (mesh.stl.stats.backwards_edges > 0) cout << "facets_reversed = " << stats.facets_reversed << endl;
cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl; if (stats.backwards_edges > 0)
cout << "backwards_edges = " << stats.backwards_edges << endl;
} }
cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl; cout << "number_of_parts = " << mesh.stats().number_of_parts << endl;
cout << "volume = " << mesh.volume() << endl; cout << "volume = " << mesh.volume() << endl;
} }
@ -1627,26 +1602,22 @@ std::string ModelObject::get_export_filename() const
return ret; return ret;
} }
stl_stats ModelObject::get_object_stl_stats() const TriangleMeshStats ModelObject::get_object_stl_stats() const
{ {
if (this->volumes.size() == 1) 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; full_stats.volume = 0.f;
// fill full_stats from all objet's meshes // fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes) 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) // initialize full_stats (for repaired errors)
full_stats.degenerate_facets += stats.degenerate_facets; full_stats.open_edges += stats.open_edges;
full_stats.edges_fixed += stats.edges_fixed; full_stats.repaired_errors.merge(stats.repaired_errors);
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;
// another used satistics value // another used satistics value
if (volume->is_model_part()) { if (volume->is_model_part()) {
@ -1658,15 +1629,15 @@ stl_stats ModelObject::get_object_stl_stats() const
return full_stats; return full_stats;
} }
int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const int ModelObject::get_repaired_errors_count(const int vol_idx /*= -1*/) const
{ {
if (vol_idx >= 0) if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count(); return this->volumes[vol_idx]->get_repaired_errors_count();
const stl_stats& stats = get_object_stl_stats(); const RepairedMeshErrors& stats = get_object_stl_stats().repaired_errors;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + 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) void ModelVolume::set_material_id(t_model_material_id material_id)
@ -1730,14 +1701,15 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
void ModelVolume::calculate_convex_hull() void ModelVolume::calculate_convex_hull()
{ {
m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d()); m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
assert(m_convex_hull.get());
} }
int ModelVolume::get_mesh_errors_count() const int ModelVolume::get_repaired_errors_count() const
{ {
const stl_stats& stats = this->mesh().stl.stats; const RepairedMeshErrors &stats = this->mesh().stats().repaired_errors;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + 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 const TriangleMesh& ModelVolume::get_convex_hull() const
@ -1785,11 +1757,9 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object. // This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders) size_t ModelVolume::split(unsigned int max_extruders)
{ {
TriangleMeshPtrs meshptrs = this->mesh().split(); std::vector<TriangleMesh> meshes = this->mesh().split();
if (meshptrs.size() <= 1) { if (meshes.size() <= 1)
delete meshptrs.front();
return 1; return 1;
}
size_t idx = 0; size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin(); size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
@ -1798,15 +1768,14 @@ size_t ModelVolume::split(unsigned int max_extruders)
unsigned int extruder_counter = 0; unsigned int extruder_counter = 0;
Vec3d offset = this->get_offset(); Vec3d offset = this->get_offset();
for (TriangleMesh *mesh : meshptrs) { for (TriangleMesh &mesh : meshes) {
mesh->repair(); if (mesh.empty())
if (mesh->empty())
// Repair may have removed unconnected triangles, thus emptying the mesh. // Repair may have removed unconnected triangles, thus emptying the mesh.
continue; continue;
if (idx == 0) if (idx == 0)
{ {
this->set_mesh(std::move(*mesh)); this->set_mesh(std::move(mesh));
this->calculate_convex_hull(); this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated. // Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id(); this->set_new_unique_id();
@ -1814,7 +1783,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->source = ModelVolume::Source(); this->source = ModelVolume::Source();
} }
else 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]->set_offset(Vec3d::Zero());
this->object->volumes[ivolume]->center_geometry_after_creation(); this->object->volumes[ivolume]->center_geometry_after_creation();
@ -1822,7 +1791,6 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); 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]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
this->object->volumes[ivolume]->m_is_splittable = 0; this->object->volumes[ivolume]->m_is_splittable = 0;
delete mesh;
++ idx; ++ idx;
} }
@ -1891,7 +1859,7 @@ void ModelVolume::mirror(Axis axis)
} }
// This method could only be called before the meshes of this ModelVolumes are not shared! // 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_mesh.get())->scale(versor);
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor); const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
@ -1924,8 +1892,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
void ModelVolume::convert_from_imperial_units() void ModelVolume::convert_from_imperial_units()
{ {
assert(! this->source.is_converted_from_meters); assert(! this->source.is_converted_from_meters);
double in_to_mm = 25.4; this->scale_geometry_after_creation(25.4f);
this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
this->set_offset(Vec3d(0, 0, 0)); this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_inches = true; this->source.is_converted_from_inches = true;
} }
@ -1933,8 +1900,7 @@ void ModelVolume::convert_from_imperial_units()
void ModelVolume::convert_from_meters() void ModelVolume::convert_from_meters()
{ {
assert(! this->source.is_converted_from_inches); assert(! this->source.is_converted_from_inches);
double m_to_mm = 1000; this->scale_geometry_after_creation(1000.f);
this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
this->set_offset(Vec3d(0, 0, 0)); this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_meters = true; this->source.is_converted_from_meters = true;
} }

View File

@ -339,13 +339,12 @@ public:
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared! // 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); void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector<int> volume_idxs);
size_t materials_count() const; size_t materials_count() const;
size_t facets_count() const; size_t facets_count() const;
size_t parts_count() const; size_t parts_count() const;
bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects); void split(ModelObjectPtrs* new_objects);
void merge(); void merge();
@ -369,9 +368,9 @@ public:
std::string get_export_filename() const; std::string get_export_filename() const;
// Get full stl statistics for all object's meshes // 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) // 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; int get_repaired_errors_count(const int vol_idx = -1) const;
private: private:
friend class Model; friend class Model;
@ -613,6 +612,8 @@ public:
const TriangleMesh& mesh() const { return *m_mesh.get(); } 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(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(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::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(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>(); } void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
@ -667,7 +668,8 @@ public:
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared! // 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. // 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! // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
@ -677,7 +679,7 @@ public:
const TriangleMesh& get_convex_hull() const; const TriangleMesh& get_convex_hull() const;
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; } std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
// Get count of errors in the mesh // Get count of errors in the mesh
int get_mesh_errors_count() const; int get_repaired_errors_count() const;
// Helpers for loading / storing into AMF / 3MF files. // Helpers for loading / storing into AMF / 3MF files.
static ModelVolumeType type_from_string(const std::string &s); static ModelVolumeType type_from_string(const std::string &s);
@ -771,7 +773,7 @@ private:
assert(this->id() != this->supported_facets.id()); assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id()); assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_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(); calculate_convex_hull();
} }
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) : 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()); assert(this->config.id() == other.config.id());
this->set_material_id(other.material_id()); this->set_material_id(other.material_id());
this->config.set_new_unique_id(); this->config.set_new_unique_id();
if (mesh.stl.stats.number_of_facets > 1) if (mesh.facets_count() > 1)
calculate_convex_hull(); calculate_convex_hull();
assert(this->config.id().valid()); assert(this->config.id().valid());
assert(this->config.id() != other.config.id()); assert(this->config.id() != other.config.id());

View File

@ -33,7 +33,9 @@ public:
void rotate(double angle, const Point &center); void rotate(double angle, const Point &center);
void reverse() { std::reverse(this->points.begin(), this->points.end()); } 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 const Point& last_point() const = 0;
virtual Lines lines() const = 0; virtual Lines lines() const = 0;
size_t size() const { return points.size(); } size_t size() const { return points.size(); }

View File

@ -105,4 +105,42 @@ PlatformFlavor platform_flavor()
return s_platform_flavor; return s_platform_flavor;
} }
std::string platform_to_string(Platform platform)
{
switch (platform) {
case Platform::Uninitialized: return "Unitialized";
case Platform::Unknown : return "Unknown";
case Platform::Windows : return "Windows";
case Platform::OSX : return "OSX";
case Platform::Linux : return "Linux";
case Platform::BSDUnix : return "BSDUnix";
}
assert(false);
return "";
}
std::string platform_flavor_to_string(PlatformFlavor pf)
{
switch (pf) {
case PlatformFlavor::Uninitialized : return "Unitialized";
case PlatformFlavor::Unknown : return "Unknown";
case PlatformFlavor::Generic : return "Generic";
case PlatformFlavor::GenericLinux : return "GenericLinux";
case PlatformFlavor::LinuxOnChromium : return "LinuxOnChromium";
case PlatformFlavor::WSL : return "WSL";
case PlatformFlavor::WSL2 : return "WSL2";
case PlatformFlavor::OpenBSD : return "OpenBSD";
case PlatformFlavor::GenericOSX : return "GenericOSX";
case PlatformFlavor::OSXOnX86 : return "OSXOnX86";
case PlatformFlavor::OSXOnArm : return "OSXOnArm";
}
assert(false);
return "";
}
} // namespace Slic3r } // namespace Slic3r

View File

@ -1,6 +1,8 @@
#ifndef SLIC3R_Platform_HPP #ifndef SLIC3R_Platform_HPP
#define SLIC3R_Platform_HPP #define SLIC3R_Platform_HPP
#include <string>
namespace Slic3r { namespace Slic3r {
enum class Platform enum class Platform
@ -17,23 +19,15 @@ enum class PlatformFlavor
{ {
Uninitialized, Uninitialized,
Unknown, Unknown,
// For Windows and OSX, until we need to be more specific. Generic, // For Windows and OSX, until we need to be more specific.
Generic, GenericLinux, // For Platform::Linux
// For Platform::Linux LinuxOnChromium, // For Platform::Linux
GenericLinux, WSL, // Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel)
LinuxOnChromium, WSL2, // Microsoft's Windows on Linux, version 2 (virtual machine)
// Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel) OpenBSD, // For Platform::BSDUnix
WSL, GenericOSX, // For Platform::OSX
// Microsoft's Windows on Linux, version 2 (virtual machine) OSXOnX86, // For Apple's on Intel X86 CPU
WSL2, OSXOnArm, // For Apple's on Arm CPU
// For Platform::BSDUnix
OpenBSD,
// For Platform::OSX
GenericOSX,
// For Apple's on Intel X86 CPU
OSXOnX86,
// For Apple's on Arm CPU
OSXOnArm,
}; };
// To be called on program start-up. // To be called on program start-up.
@ -42,6 +36,9 @@ void detect_platform();
Platform platform(); Platform platform();
PlatformFlavor platform_flavor(); PlatformFlavor platform_flavor();
std::string platform_to_string(Platform platform);
std::string platform_flavor_to_string(PlatformFlavor pf);
} // namespace Slic3r } // namespace Slic3r
#endif // SLIC3R_Platform_HPP #endif // SLIC3R_Platform_HPP

View File

@ -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; 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) BoundingBox get_extents(const Points &pts)
{ {
return BoundingBox(pts); return BoundingBox(pts);

View File

@ -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>(); return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
} }
extern BoundingBox get_extents(const Points &pts); BoundingBox get_extents(const Points &pts);
extern BoundingBox get_extents(const std::vector<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 { namespace int128 {
// Exact orientation predicate, // Exact orientation predicate,
@ -418,7 +444,7 @@ template<class Tout = double,
class = FloatingOnly<Tout>> class = FloatingOnly<Tout>>
inline constexpr Tout unscaled(const Tin &v) noexcept 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 // 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...> inline constexpr Eigen::Matrix<Tout, N, EigenArgs...>
unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept 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, // Align a coordinate to a grid. The coordinate may be negative,

View File

@ -334,6 +334,27 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
return out; 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) static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
{ {
Point v1 = p2 - p1; Point v1 = p2 - p1;

View File

@ -18,11 +18,6 @@ using ConstPolygonPtrs = std::vector<const Polygon*>;
class Polygon : public MultiPoint class Polygon : public MultiPoint
{ {
public: 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; Polygon() = default;
virtual ~Polygon() = default; virtual ~Polygon() = default;
explicit Polygon(const Points &points) : MultiPoint(points) {} explicit Polygon(const Points &points) : MultiPoint(points) {}
@ -39,6 +34,9 @@ public:
Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
Polygon& operator=(Polygon &&other) { points = std::move(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 // last point == first point for polygons
const Point& last_point() const override { return this->points.front(); } 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; }
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); BoundingBox get_extents(const Polygon &poly);
extern BoundingBox get_extents(const Polygons &polygons); BoundingBox get_extents(const Polygons &polygons);
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle); BoundingBox get_extents_rotated(const Polygon &poly, double angle);
extern BoundingBox get_extents_rotated(const Polygons &polygons, double angle); BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons); 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) { inline double total_length(const Polygons &polylines) {
double total = 0; double total = 0;
@ -104,14 +107,14 @@ inline double area(const Polygons &polys)
} }
// Remove sticks (tentacles with zero area) from the polygon. // Remove sticks (tentacles with zero area) from the polygon.
extern bool remove_sticks(Polygon &poly); bool remove_sticks(Polygon &poly);
extern bool remove_sticks(Polygons &polys); bool remove_sticks(Polygons &polys);
// Remove polygons with less than 3 edges. // Remove polygons with less than 3 edges.
extern bool remove_degenerate(Polygons &polys); bool remove_degenerate(Polygons &polys);
extern bool remove_small(Polygons &polys, double min_area); bool remove_small(Polygons &polys, double min_area);
extern void remove_collinear(Polygon &poly); void remove_collinear(Polygon &poly);
extern void remove_collinear(Polygons &polys); void remove_collinear(Polygons &polys);
// Append a vector of polygons at the end of another vector of polygons. // 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()); }

View File

@ -10,20 +10,6 @@
namespace Slic3r { 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& Polyline::leftmost_point() const
{ {
const Point *p = &this->points.front(); const Point *p = &this->points.front();

View File

@ -60,12 +60,10 @@ public:
} }
} }
explicit operator Polylines() const;
explicit operator Line() const;
const Point& last_point() const override { return this->points.back(); } const Point& last_point() const override { return this->points.back(); }
const Point& leftmost_point() const; const Point& leftmost_point() const;
Lines lines() const override; Lines lines() const override;
void clip_end(double distance); void clip_end(double distance);
void clip_start(double distance); void clip_start(double distance);
void extend_end(double distance); void extend_end(double distance);

View File

@ -1097,14 +1097,6 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
return m_idx_selected; 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 // Update a dirty flag of the current preset
// Return true if the dirty flag changed. // Return true if the dirty flag changed.
bool PresetCollection::update_dirty() bool PresetCollection::update_dirty()

View File

@ -500,13 +500,15 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
// Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer. // Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer.
auto printer_technology = printers.get_selected_preset().printer_technology(); auto printer_technology = printers.get_selected_preset().printer_technology();
if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) { if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) {
if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible) { std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament);
filaments.select_preset_by_name_strict(preferred_selection.filament); if (auto it = filaments.find_preset_internal(preferred_preset_name); it != filaments.end() && it->is_visible) {
filaments.select_preset_by_name_strict(preferred_preset_name);
this->filament_presets.front() = filaments.get_selected_preset_name(); this->filament_presets.front() = filaments.get_selected_preset_name();
} }
} else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) { } else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) {
if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible) std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material);
sla_materials.select_preset_by_name_strict(preferred_selection.sla_material); if (auto it = sla_materials.find_preset_internal(preferred_preset_name); it != sla_materials.end() && it->is_visible)
sla_materials.select_preset_by_name_strict(preferred_preset_name);
} }
} }

View File

@ -143,7 +143,7 @@ public:
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; 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 // 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); void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options);

View File

@ -397,7 +397,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
convex_hull.translate(instance.shift - print_object->center_offset()); convex_hull.translate(instance.shift - print_object->center_offset());
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls // if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
for (size_t i = 0; i < convex_hulls_other.size(); ++i) { 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) if (polygons == nullptr)
return false; return false;
else { 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); 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. // Precondition: Print::validate() requires the Print::apply() to be called its invocation.
std::string Print::validate(std::string* warning) const std::string Print::validate(std::string* warning) const
{ {
@ -526,42 +528,11 @@ std::string Print::validate(std::string* warning) const
} }
if (has_custom_layering) { 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) { for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
if (idx_object == tallest_object_idx) if (idx_object == tallest_object_idx)
continue; continue;
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object]; if (layer_height_profiles[idx_object] != layer_height_profiles[tallest_object_idx])
// 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"); return L("The Wipe tower is only supported if all objects have the same variable layer height");
i += 2;
}
} }
} }
} }
@ -812,7 +783,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
// Slicing process, running at a background thread. // Slicing process, running at a background thread.
void Print::process() 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(); BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects) for (PrintObject *obj : m_objects)

View File

@ -1300,8 +1300,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders, num_extruders,
painting_extruders, painting_extruders,
*print_object_regions, *print_object_regions,
[&print_object, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) { [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) {
update_apply_status(print_object.invalidate_state_by_config_options(old_config, new_config, 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. // Regions are valid, just keep them.
} else { } else {

View File

@ -60,6 +60,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
DynamicConfig cfg; DynamicConfig cfg;
if (config_override != nullptr) if (config_override != nullptr)
cfg = *config_override; cfg = *config_override;
cfg.set_key_value("version", new ConfigOptionString(std::string(SLIC3R_VERSION)));
PlaceholderParser::update_timestamp(cfg); PlaceholderParser::update_timestamp(cfg);
this->update_object_placeholders(cfg, default_ext); this->update_object_placeholders(cfg, default_ext);
if (! filename_base.empty()) { if (! filename_base.empty()) {

View File

@ -397,22 +397,6 @@ void PrintObject::generate_support_material()
if (layer->empty()) if (layer->empty())
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
#endif #endif
// Do we have custom support data that would not be used?
// Notify the user in that case.
if (! this->has_support()) {
for (const ModelVolume* mv : this->model_object()->volumes) {
bool has_enforcers = mv->is_support_enforcer() ||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
if (has_enforcers) {
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("An object has custom support enforcers which will not be used "
"because supports are off. Consider turning them on.") + "\n" +
(L("Object name")) + ": " + this->model_object()->name);
break;
}
}
}
} }
this->set_done(posSupportMaterial); this->set_done(posSupportMaterial);
} }
@ -1447,7 +1431,7 @@ void PrintObject::bridge_over_infill()
Polygons to_bridge_pp = internal_solid; Polygons to_bridge_pp = internal_solid;
// iterate through lower layers spanned by bridge_flow // 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) { for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i]; const Layer* lower_layer = m_layers[i];

View File

@ -39,23 +39,6 @@ LayerPtrs new_layers(
return out; 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. // Slice single triangle mesh.
static std::vector<ExPolygons> slice_volume( static std::vector<ExPolygons> slice_volume(
const ModelVolume &volume, const ModelVolume &volume,
@ -65,7 +48,7 @@ static std::vector<ExPolygons> slice_volume(
{ {
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
if (! zs.empty()) { 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) { if (its.indices.size() > 0) {
MeshSlicingParamsEx params2 { params }; MeshSlicingParamsEx params2 { params };
params2.trafo = params2.trafo * volume.get_matrix(); params2.trafo = params2.trafo * volume.get_matrix();

View File

@ -286,8 +286,6 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
if (mesh.empty()) return; if (mesh.empty()) return;
mesh.require_shared_vertices();
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr); std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size()) 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); remove_inside_triangles(mesh, interior);
mesh.merge(TriangleMesh{interior.mesh}); mesh.merge(TriangleMesh{interior.mesh});
mesh.require_shared_vertices();
} }
// Get the distance of p to the interior's zero iso_surface. Interior should // 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 = {}; new_faces = {};
mesh = TriangleMesh{mesh.its}; mesh = TriangleMesh{mesh.its};
mesh.repaired = true; //FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles?
mesh.require_shared_vertices();
} }
}} // namespace Slic3r::sla }} // namespace Slic3r::sla

View File

@ -370,7 +370,7 @@ bool add_cavity(indexed_triangle_set &pad,
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; } 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; } if (pdiff.size() != 1) { logerr(); return false; }

View File

@ -13,10 +13,6 @@
namespace Slic3r { namespace Slic3r {
template<class T> using uqptr = std::unique_ptr<T>;
template<class T> using shptr = std::shared_ptr<T>;
template<class T> using wkptr = std::weak_ptr<T>;
namespace sla { namespace sla {
// Raw byte buffer paired with its size. Suitable for compressed image data. // Raw byte buffer paired with its size. Suitable for compressed image data.
@ -112,7 +108,7 @@ struct PPMRasterEncoder {
std::ostream& operator<<(std::ostream &stream, const EncodedRaster &bytes); std::ostream& operator<<(std::ostream &stream, const EncodedRaster &bytes);
// If gamma is zero, thresholding will be performed which disables AA. // If gamma is zero, thresholding will be performed which disables AA.
uqptr<RasterBase> create_raster_grayscale_aa( std::unique_ptr<RasterBase> create_raster_grayscale_aa(
const RasterBase::Resolution &res, const RasterBase::Resolution &res,
const RasterBase::PixelDim & pxdim, const RasterBase::PixelDim & pxdim,
double gamma = 1.0, double gamma = 1.0,

View File

@ -33,7 +33,6 @@ inline void reproject_points_and_holes(ModelObject *object)
if (!object || (!has_holes && !has_sppoints)) return; if (!object || (!has_holes && !has_sppoints)) return;
TriangleMesh rmsh = object->raw_mesh(); TriangleMesh rmsh = object->raw_mesh();
rmsh.require_shared_vertices();
IndexedMesh emesh{rmsh}; IndexedMesh emesh{rmsh};
if (has_sppoints) if (has_sppoints)

View File

@ -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) std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max_count)
{ {
TriangleMesh chull = mesh.convex_hull_3d(); TriangleMesh chull = mesh.convex_hull_3d();
chull.require_shared_vertices();
double chull2d_area = chull.convex_hull().area(); double chull2d_area = chull.convex_hull().area();
double area_threshold = chull2d_area / (scaled<double>(1e3) * scaled(1.)); 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) static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
{ {
TriangleMesh mesh = mo.raw_mesh(); TriangleMesh mesh = mo.raw_mesh();
mesh.require_shared_vertices();
ModelInstance *mi = mo.instances[0]; ModelInstance *mi = mo.instances[0];
auto rotation = Vec3d::Zero(); auto rotation = Vec3d::Zero();
@ -437,7 +435,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo,
RotfinderBoilerplate<1000> bp{mo, params}; RotfinderBoilerplate<1000> bp{mo, params};
TriangleMesh chull = bp.mesh.convex_hull_3d(); TriangleMesh chull = bp.mesh.convex_hull_3d();
chull.require_shared_vertices();
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size()); auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) { auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y]; double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];

View File

@ -670,7 +670,7 @@ std::string SLAPrint::validate(std::string*) const
return ""; return "";
} }
void SLAPrint::set_printer(SLAPrinter *arch) void SLAPrint::set_printer(SLAArchive *arch)
{ {
invalidate_step(slapsRasterize); invalidate_step(slapsRasterize);
m_printer = arch; m_printer = arch;
@ -693,7 +693,7 @@ void SLAPrint::process()
if (m_objects.empty()) if (m_objects.empty())
return; 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 // 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 // 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(); obj = m_model_object->raw_mesh();
if (!obj.empty()) { if (!obj.empty()) {
obj.transform(m_trafo); obj.transform(m_trafo);
obj.require_shared_vertices();
} }
}) })
{} {}
@ -1048,15 +1047,15 @@ Vec3d SLAPrint::relative_correction() const
Vec3d corr(1., 1., 1.); Vec3d corr(1., 1., 1.);
if(printer_config().relative_correction.values.size() >= 2) { if(printer_config().relative_correction.values.size() >= 2) {
corr(X) = printer_config().relative_correction.values[0]; corr.x() = printer_config().relative_correction.values[0];
corr(Y) = printer_config().relative_correction.values[0]; corr.y() = corr.x();
corr(Z) = printer_config().relative_correction.values.back(); corr.z() = printer_config().relative_correction.values[1];
} }
if(material_config().material_correction.values.size() >= 2) { if(material_config().material_correction.values.size() >= 2) {
corr(X) *= material_config().material_correction.values[0]; corr.x() *= material_config().material_correction.values[0];
corr(Y) *= material_config().material_correction.values[0]; corr.y() = corr.x();
corr(Z) *= material_config().material_correction.values.back(); corr.z() *= material_config().material_correction.values[1];
} }
return corr; return corr;

View File

@ -323,7 +323,6 @@ private:
{ {
support_tree_ptr = sla::SupportTree::create(*this, ctl); support_tree_ptr = sla::SupportTree::create(*this, ctl);
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)}; tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
tree_mesh.require_shared_vertices();
return support_tree_ptr; return support_tree_ptr;
} }
@ -388,15 +387,15 @@ struct SLAPrintStatistics
} }
}; };
class SLAPrinter { class SLAArchive {
protected: protected:
std::vector<sla::EncodedRaster> m_layers; std::vector<sla::EncodedRaster> m_layers;
virtual uqptr<sla::RasterBase> create_raster() const = 0; virtual std::unique_ptr<sla::RasterBase> create_raster() const = 0;
virtual sla::RasterEncoder get_encoder() const = 0; virtual sla::RasterEncoder get_encoder() const = 0;
public: public:
virtual ~SLAPrinter() = default; virtual ~SLAArchive() = default;
virtual void apply(const SLAPrinterConfig &cfg) = 0; virtual void apply(const SLAPrinterConfig &cfg) = 0;
@ -527,7 +526,7 @@ public:
// TODO: use this structure for the preview in the future. // TODO: use this structure for the preview in the future.
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; } const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
void set_printer(SLAPrinter *archiver); void set_printer(SLAArchive *archiver);
private: private:
@ -549,7 +548,7 @@ private:
std::vector<PrintLayer> m_printer_input; std::vector<PrintLayer> m_printer_input;
// The archive object which collects the raster images after slicing // The archive object which collects the raster images after slicing
SLAPrinter *m_printer = nullptr; SLAArchive *m_printer = nullptr;
// Estimated print time, material consumed. // Estimated print time, material consumed.
SLAPrintStatistics m_print_statistics; SLAPrintStatistics m_print_statistics;

View File

@ -526,7 +526,6 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
} }
auto thr = [this]() { m_print->throw_if_canceled(); }; auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels; 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); po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
sla::Interior *interior = po.m_hollowing_data ? sla::Interior *interior = po.m_hollowing_data ?

View File

@ -14,10 +14,8 @@ void simplify_mesh(indexed_triangle_set &);
template<class...Args> void simplify_mesh(TriangleMesh &m, Args &&...a) template<class...Args> void simplify_mesh(TriangleMesh &m, Args &&...a)
{ {
m.require_shared_vertices();
simplify_mesh(m.its, std::forward<Args>(a)...); simplify_mesh(m.its, std::forward<Args>(a)...);
m = TriangleMesh{m.its}; m = TriangleMesh{ std::move(m.its) };
m.require_shared_vertices();
} }
} // namespace Slic3r } // namespace Slic3r

View File

@ -35,13 +35,6 @@ legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as Pru
namespace Slic3r 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: // By Florens Waserfall aka @platch:
// This constant essentially describes the volumetric error at the surface which is induced // This constant essentially describes the volumetric error at the surface which is induced
// by stacking "elliptic" extrusion threads. It is empirically determined by // 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()); mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
// 1) Collect faces from mesh. // 1) Collect faces from mesh.
m_faces.reserve(mesh.stl.stats.number_of_facets); m_faces.reserve(mesh.facets_count());
for (const stl_facet &face : mesh.stl.facet_start) { for (stl_triangle_vertex_indices face : mesh.its.indices) {
Vec3f n = face.normal.normalized(); stl_vertex vertex[3] = { mesh.its.vertices[face[0]], mesh.its.vertices[face[1]], mesh.its.vertices[face[2]] };
m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) })); 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. // 2) Sort faces lexicographically by their Z span.

View File

@ -502,7 +502,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled. // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
// There is also a 1st intermediate layer containing bases of support columns. // There is also a 1st intermediate layer containing bases of support columns.
// Inflate the bases of the support columns and create the raft base under the object. // Inflate the bases of the support columns and create the raft base under the object.
MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, intermediate_layers, base_interface_layers, layer_storage); MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
for (const MyLayer *l : interface_layers) for (const MyLayer *l : interface_layers)
@ -721,9 +721,9 @@ public:
#ifdef SUPPORT_USE_AGG_RASTERIZER #ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox; m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls. // 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))); int extrusion_width_scaled = scale_(params.extrusion_width);
m_pixel_size = scale_(m_support_spacing / oversampling); int oversampling = std::clamp(int(scale_(m_support_spacing) / (extrusion_width_scaled + 100)), 1, 8);
assert(scale_(params.extrusion_width) + 20 < m_pixel_size); m_pixel_size = std::max<double>(extrusion_width_scaled + 21, scale_(m_support_spacing / oversampling));
// Add one empty column / row boundaries. // Add one empty column / row boundaries.
m_bbox.offset(m_pixel_size); m_bbox.offset(m_pixel_size);
// Grid size fitting the support polygons plus one pixel boundary around the polygons. // Grid size fitting the support polygons plus one pixel boundary around the polygons.
@ -1600,6 +1600,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
const PrintConfig &print_config, const PrintConfig &print_config,
const PrintObjectConfig &object_config, const PrintObjectConfig &object_config,
const SlicingParameters &slicing_params, const SlicingParameters &slicing_params,
const coordf_t support_layer_height_min,
const Layer &layer, const Layer &layer,
std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage, std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage,
tbb::spin_mutex &layer_storage_mutex) 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. // 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 //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. // 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. // 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); return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr);
} else if (print_z < slicing_params.first_print_layer_height + EPSILON) { } 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 += region->region().bridging_height_avg(print_config);
bridging_height /= coordf_t(layer.regions().size()); bridging_height /= coordf_t(layer.regions().size());
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object; 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. // Not below the first layer height means this layer is printable.
if (print_z < slicing_params.first_print_layer_height + EPSILON) { if (print_z < slicing_params.first_print_layer_height + EPSILON) {
// Align the layer with the 1st layer height. // 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) { if (bridging_print_z == slicing_params.first_print_layer_height) {
bridging_layer->bottom_z = 0; bridging_layer->bottom_z = 0;
bridging_layer->height = slicing_params.first_print_layer_height; bridging_layer->height = slicing_params.first_print_layer_height;
} } else {
else {
// Don't know the height yet. // Don't know the height yet.
bridging_layer->bottom_z = bridging_print_z; bridging_layer->bottom_z = bridging_print_z;
bridging_layer->height = 0; 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. // Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) { 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) { if (new_layer) {
fill_contact_layer(*new_layer, layer_id, m_slicing_params, fill_contact_layer(*new_layer, layer_id, m_slicing_params,
*m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons, *m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons,
@ -2058,17 +2059,17 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
const Layer &layer_above = *object.layers()[layer_id_above]; const Layer &layer_above = *object.layers()[layer_id_above];
if (layer_above.print_z > layer_new.print_z - EPSILON) if (layer_above.print_z > layer_new.print_z - EPSILON)
break; break;
if (! layer_support_areas[layer_id_above].empty()) { if (Polygons &above = layer_support_areas[layer_id_above]; ! above.empty()) {
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
{ { { union_ex(touching) }, { "touching", "blue", 0.5f } }, { { { union_ex(touching) }, { "touching", "blue", 0.5f } },
{ { union_safety_offset_ex(layer_support_areas[layer_id_above]) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } }); { { union_safety_offset_ex(above) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching); above = diff(above, touching);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons( Slic3r::SVG::export_expolygons(
debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
union_ex(layer_support_areas[layer_id_above])); union_ex(above));
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
} }
} }
@ -2600,8 +2601,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
// No top contacts -> no intermediate layers will be produced. // No top contacts -> no intermediate layers will be produced.
return; return;
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, intermediate_layers.size()), tbb::blocked_range<size_t>(0, intermediate_layers.size()),
@ -2623,10 +2622,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
// New polygons for layer_intermediate. // New polygons for layer_intermediate.
Polygons polygons_new; Polygons polygons_new;
// Use the precomputed layer_support_areas. // Use the precomputed layer_support_areas. "idx_object_layer_above": above means above since the last iteration, not above after this call.
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers().begin(), object.layers().end(), idx_object_layer_above, idx_object_layer_above = idx_lower_or_equal(object.layers().begin(), object.layers().end(), idx_object_layer_above,
[&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; })); [&layer_intermediate](const Layer* layer) { return layer->print_z <= layer_intermediate.print_z + EPSILON; });
polygons_new = layer_support_areas[idx_object_layer_above];
// Polygons to trim polygons_new. // Polygons to trim polygons_new.
Polygons polygons_trimming; Polygons polygons_trimming;
@ -2652,6 +2650,22 @@ void PrintObjectSupportMaterial::generate_base_layers(
polygons_append(polygons_trimming, layer_top_overlapping.polygons); polygons_append(polygons_trimming, layer_top_overlapping.polygons);
} }
if (idx_object_layer_above < 0) {
// layer_support_areas are synchronized with object layers and they contain projections of the contact layers above them.
// This intermediate layer is not above any object layer, thus there is no information in layer_support_areas about
// towers supporting contact layers intersecting the first object layer. Project these contact layers now.
polygons_new = layer_support_areas.front();
double first_layer_z = object.layers().front()->print_z;
for (int i = idx_top_contact_above + 1; i < int(top_contacts.size()); ++ i) {
MyLayer &contacts = *top_contacts[i];
if (contacts.print_z > first_layer_z + EPSILON)
break;
assert(contacts.bottom_z > layer_intermediate.print_z - EPSILON);
polygons_append(polygons_new, contacts.polygons);
}
} else
polygons_new = layer_support_areas[idx_object_layer_above];
// Trimming the base layer with any overlapping bottom layer. // Trimming the base layer with any overlapping bottom layer.
// Following cases are recognized: // Following cases are recognized:
// 1) bottom.bottom_z >= base.top_z -> No overlap, no trimming needed. // 1) bottom.bottom_z >= base.top_z -> No overlap, no trimming needed.
@ -2696,6 +2710,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
layer_intermediate.layer_type = sltBase; layer_intermediate.layer_type = sltBase;
#if 0 #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. // Fillet the base polygons and trim them again with the top, interface and contact layers.
$base->{$i} = diff( $base->{$i} = diff(
offset2( offset2(
@ -3338,7 +3353,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]; Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
const Point *seg_current_pt = nullptr; const Point *seg_current_pt = nullptr;
coordf_t seg_current_t = 0.; 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. // The contour is below the overhang at least to some extent.
//FIXME ideally one would place the circles below the overhang only. //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. // Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
@ -3784,7 +3799,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers. // Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath; 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; std::vector<float> angles;
angles.push_back(base_angle); angles.push_back(base_angle);

View File

@ -41,16 +41,8 @@
//==================== //====================
#define ENABLE_2_4_0_ALPHA1 1 #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 // Enable implementation of retract acceleration in gcode processor
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1) #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 // Enable rendering seams (and other options) in preview using models
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1) #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 // Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
@ -65,7 +57,20 @@
// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3 // 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) #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 // 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)
#endif // _prusaslicer_technologies_h_ #endif // _prusaslicer_technologies_h_

View File

@ -188,7 +188,8 @@ std::optional<std::string> get_current_thread_name()
#endif // _WIN32 #endif // _WIN32
// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID. // 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; static bool initialized = false;
if (initialized) if (initialized)
@ -233,6 +234,21 @@ void name_tbb_thread_pool_threads()
std::ostringstream name; std::ostringstream name;
name << "slic3r_tbb_" << range.begin(); name << "slic3r_tbb_" << range.begin();
set_current_thread_name(name.str().c_str()); 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
} }
}); });
} }

View File

@ -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 // To be called somewhere before the TBB threads are spinned for the first time, to
// give them names recognizible in the debugger. // 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> template<class Fn>
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)

File diff suppressed because it is too large Load Diff

View File

@ -15,25 +15,91 @@ namespace Slic3r {
class TriangleMesh; class TriangleMesh;
class TriangleMeshSlicer; class TriangleMeshSlicer;
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
struct RepairedMeshErrors {
// 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 = RepairedMeshErrors(); }
void merge(const RepairedMeshErrors& rhs) {
this->edges_fixed += rhs.edges_fixed;
this->degenerate_facets += rhs.degenerate_facets;
this->facets_removed += rhs.facets_removed;
this->facets_reversed += rhs.facets_reversed;
this->backwards_edges += rhs.backwards_edges;
}
bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; }
};
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.
RepairedMeshErrors repaired_errors;
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.repaired_errors.merge(rhs.repaired_errors);
return out;
}
}
bool manifold() const { return open_edges == 0; }
bool repaired() const { return repaired_errors.repaired(); }
};
class TriangleMesh class TriangleMesh
{ {
public: public:
TriangleMesh() : repaired(false) {} TriangleMesh() = default;
TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets); 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); explicit TriangleMesh(const indexed_triangle_set &M);
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; } explicit TriangleMesh(indexed_triangle_set &&M, const RepairedMeshErrors& repaired_errors = RepairedMeshErrors());
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } void clear() { this->its.clear(); this->m_stats.clear(); }
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } bool ReadSTLFile(const char* input_file, bool repair = true);
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); } bool write_ascii(const char* output_file);
void repair(bool update_shared_vertices = true); bool write_binary(const char* output_file);
float volume(); 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 WriteOBJFile(const char* output_file) const;
void scale(float factor); void scale(float factor);
void scale(const Vec3d &versor); void scale(const Vec3f &versor);
void translate(float x, float y, float z); void translate(float x, float y, float z);
void translate(const Vec3f &displacement); void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis); void rotate(float angle, const Axis &axis);
@ -41,51 +107,52 @@ public:
void rotate_x(float angle) { this->rotate(angle, X); } void rotate_x(float angle) { this->rotate(angle, X); }
void rotate_y(float angle) { this->rotate(angle, Y); } void rotate_y(float angle) { this->rotate(angle, Y); }
void rotate_z(float angle) { this->rotate(angle, Z); } 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_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); } void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); } void mirror_z() { this->mirror(Z); }
void transform(const Transform3d& t, bool fix_left_handed = false); void transform(const Transform3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& 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 align_to_origin();
void rotate(double angle, Point* center); void rotate(double angle, Point* center);
TriangleMeshPtrs split() const; std::vector<TriangleMesh> split() const;
void merge(const TriangleMesh &mesh); void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const; 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. // 2D convex hull of a 3D mesh projected into the Z=0 plane.
Polygon convex_hull(); Polygon convex_hull();
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation // Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const; BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
// Return the size of the mesh in coordinates. // 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. /// Return the center of the related bounding box.
Vec3d center() const { return this->bounding_box().center(); } Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh // Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const; TriangleMesh convex_hull_3d() const;
// Slice this mesh at the provided Z levels and return the vector // Slice this mesh at the provided Z levels and return the vector
std::vector<ExPolygons> slice(const std::vector<double>& z) const; std::vector<ExPolygons> slice(const std::vector<double>& z) const;
void reset_repair_stats(); size_t facets_count() const { assert(m_stats.number_of_facets == this->its.indices.size()); return m_stats.number_of_facets; }
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; }
bool empty() const { return this->facets_count() == 0; } bool empty() const { return this->facets_count() == 0; }
bool repaired() const;
bool is_splittable() const; bool is_splittable() const;
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation. // Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
size_t memsize() const; size_t memsize() const;
// 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();
// Restore optional data possibly released by release_optional().
void restore_optional();
stl_file stl; // 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() { return 0; }
// Restore optional data possibly released by release_optional().
void restore_optional() {}
const TriangleMeshStats& stats() const { return m_stats; }
indexed_triangle_set its; indexed_triangle_set its;
bool repaired;
private: 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. // Index of face indices incident with a vertex index.
@ -145,8 +212,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); 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<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);
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. // 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); void its_shrink_to_fit(indexed_triangle_set &its);
@ -205,16 +282,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 std::vector<Vec3f> &triangles);
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles); void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
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_cube(double x, double y, double z);
TriangleMesh make_cube(double x, double y, double z); indexed_triangle_set its_make_prism(float width, float length, float height);
// Generate a TriangleMesh of a cylinder
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); 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_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);
indexed_triangle_set its_make_sphere(double rho, double fa=(2*PI/360)); inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)); inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); 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 TriangleMesh &m) { return m.bounding_box(); }
inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its) inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
@ -239,18 +332,12 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
namespace cereal { namespace cereal {
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {}; template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) { template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
stl_file &stl = mesh.stl; archive.loadBinary(reinterpret_cast<char*>(const_cast<Slic3r::TriangleMeshStats*>(&mesh.stats())), sizeof(Slic3r::TriangleMeshStats));
stl.stats.type = inmemory; archive(mesh.its.indices, mesh.its.vertices);
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();
} }
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) { template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
const stl_file& stl = mesh.stl; archive.saveBinary(reinterpret_cast<const char*>(&mesh.stats()), sizeof(Slic3r::TriangleMeshStats));
archive(stl.stats.number_of_facets, stl.stats.original_num_facets); archive(mesh.its.indices, mesh.its.vertices);
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
} }
} }

View File

@ -1967,7 +1967,8 @@ static void triangulate_slice(
int num_original_vertices, int num_original_vertices,
// Z height of the slice. // Z height of the slice.
float z, float z,
bool triangulate) bool triangulate,
bool normals_down)
{ {
sort_remove_duplicates(slice_vertices); 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); std::vector<int> map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
int i = 0; int i = 0;
int k = 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]; map_vertex_to_index[k ++] = map_vertex_to_index[i];
const Vec2f &ipos = map_vertex_to_index[i].first; const Vec2f &ipos = map_vertex_to_index[i].first;
const int iidx = map_vertex_to_index[i].second; const int iidx = map_vertex_to_index[i].second;
@ -2003,6 +2004,7 @@ static void triangulate_slice(
// map to the first vertex // map to the first vertex
map_duplicate_vertex[jidx - num_original_vertices] = iidx; 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()); map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
for (stl_triangle_vertex_indices &f : its.indices) for (stl_triangle_vertex_indices &f : its.indices)
@ -2013,7 +2015,7 @@ static void triangulate_slice(
if (triangulate) { if (triangulate) {
size_t idx_vertex_new_first = its.vertices.size(); 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(); ) { for (size_t i = 0; i < triangles.size(); ) {
stl_triangle_vertex_indices facet; stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) { for (size_t j = 0; j < 3; ++ j) {
@ -2049,6 +2051,33 @@ static void triangulate_slice(
// its_remove_degenerate_faces(its); // 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) void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower, bool triangulate_caps)
{ {
assert(upper || lower); 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()); 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. // To triangulate the caps after slicing.
IntersectionLines upper_lines, lower_lines; IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices; std::vector<int> upper_slice_vertices, lower_slice_vertices;
@ -2137,11 +2170,12 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
stl_vertex v0v1, v2v0; 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) { if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
v0v1 = to_3d(unscaled<float>(line.a), z); // Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v2v0 = to_3d(unscaled<float>(line.b), z); v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
} else { } else {
v0v1 = to_3d(unscaled<float>(line.b), z); v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<float>(line.a), z); v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
} }
const stl_vertex &v0 = vertices[iv]; const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[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) if (upper != nullptr) {
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps); triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
#ifndef NDEBUG
if (lower != nullptr) if (triangulate_caps) {
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, 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, 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

View File

@ -98,6 +98,20 @@ void slice_mesh_slabs(
std::vector<Polygons> *out_bottom, std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel); std::function<void()> throw_on_cancel);
// 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( void cut_mesh(
const indexed_triangle_set &mesh, const indexed_triangle_set &mesh,
float z, float z,

View File

@ -9,16 +9,6 @@
namespace Slic3r { 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 #ifndef NDEBUG
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const 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()) if (!m_triangles[facet_idx].valid())
return -1; 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)); assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
return this->select_unsplit_triangle(hit, 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 (! visited[facet]) {
if (select_triangle(facet, new_state, triangle_splitting)) { if (select_triangle(facet, new_state, triangle_splitting)) {
// add neighboring facets to list to be proccessed later // 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))) if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
facets_to_check.push_back(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) if (current_facet < m_orig_size_indices)
// Propagate over the original triangles. // 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); assert(neighbor_idx >= -1);
if (neighbor_idx >= 0 && !visited[neighbor_idx]) { 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. // 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 &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
const Vec3f &n2 = m_mesh->stl.facet_start[m_triangles[current_facet].source_triangle].normal; 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) if (std::clamp(n1.dot(n2), 0.f, 1.f) >= facet_angle_limit)
facet_queue.push(neighbor_idx); 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(m_triangles.size(), Vec3i(-1, -1, -1));
std::vector<Vec3i> neighbors_propagated(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) { 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]; neighbors_propagated[facet_idx] = neighbors[facet_idx];
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx])); assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
if (m_triangles[facet_idx].is_split()) 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()) if (! m_triangles[facet_idx].valid())
return false; 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)); assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting)) 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 bool TriangleSelector::faces_camera(int facet) const
{ {
assert(facet < m_orig_size_indices); assert(facet < m_orig_size_indices);
// The normal is cached in mesh->stl, use it. Vec3f n = m_face_normals[facet];
Vec3f normal = m_mesh->stl.facet_start[facet].normal; if (! m_cursor.uniform_scaling)
n = m_cursor.trafo_normal * n;
if (! m_cursor.uniform_scaling) { return n.dot(m_cursor.dir) < 0.;
// Transform the normal into world coords.
normal = m_cursor.trafo_normal * normal;
}
return (normal.dot(m_cursor.dir) < 0.);
} }
@ -1094,7 +1080,7 @@ void TriangleSelector::garbage_collect()
} }
TriangleSelector::TriangleSelector(const TriangleMesh& mesh) 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(); reset();
} }
@ -1107,16 +1093,17 @@ void TriangleSelector::reset()
m_invalid_triangles = 0; m_invalid_triangles = 0;
m_free_triangles_head = -1; m_free_triangles_head = -1;
m_free_vertices_head = -1; m_free_vertices_head = -1;
m_vertices.reserve(m_mesh->its.vertices.size()); m_vertices.reserve(m_mesh.its.vertices.size());
for (const stl_vertex& vert : m_mesh->its.vertices) for (const stl_vertex& vert : m_mesh.its.vertices)
m_vertices.emplace_back(vert); m_vertices.emplace_back(vert);
m_triangles.reserve(m_mesh->its.indices.size()); m_triangles.reserve(m_mesh.its.indices.size());
for (size_t i = 0; i < m_mesh->its.indices.size(); ++i) { for (size_t i = 0; i < m_mesh.its.indices.size(); ++i) {
const stl_triangle_vertex_indices &ind = m_mesh->its.indices[i]; const stl_triangle_vertex_indices &ind = m_mesh.its.indices[i];
push_triangle(ind[0], ind[1], ind[2], int(i)); push_triangle(ind[0], ind[1], ind[2], int(i));
} }
m_orig_size_vertices = int(m_vertices.size()); m_orig_size_vertices = int(m_vertices.size());
m_orig_size_indices = int(m_triangles.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) 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 (auto &triangle : out.indices)
for (int i = 0; i < 3; ++ i) 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> TriangleSelector::get_seed_fill_contour() const {
std::vector<Vec2i> edges_out; std::vector<Vec2i> edges_out;
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) { 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)); assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out); 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. // 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. // 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. // 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. // 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. // Vector to store all parents that have offsprings.
struct ProcessingInfo { struct ProcessingInfo {
@ -1565,7 +1552,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
if (is_split) { if (is_split) {
// root is split, add it into list of parents and split it. // root is split, add it into list of parents and split it.
// then go to the next. // 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}); parents.push_back({triangle_id, neighbors, 0, num_of_children});
m_triangles[triangle_id].set_division(num_of_split_sides, special_side); m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE); perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);

View File

@ -161,7 +161,9 @@ protected:
// Lists of vertices and triangles, both original and new // Lists of vertices and triangles, both original and new
std::vector<Vertex> m_vertices; std::vector<Vertex> m_vertices;
std::vector<Triangle> m_triangles; 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). // Number of invalid triangles (to trigger garbage collection).
int m_invalid_triangles; int m_invalid_triangles;

View File

@ -255,6 +255,19 @@ template<typename T> struct IsTriviallyCopyable { static constexpr bool value =
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {}; template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
#endif #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 class ScopeGuard
{ {

View File

@ -889,7 +889,7 @@ std::string string_printf(const char *format, ...)
std::string header_slic3r_generated() 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() std::string header_gcodeviewer_generated()
@ -906,6 +906,7 @@ unsigned get_current_pid()
#endif #endif
} }
//FIXME this has potentially O(n^2) time complexity!
std::string xml_escape(std::string text, bool is_marked/* = false*/) std::string xml_escape(std::string text, bool is_marked/* = false*/)
{ {
std::string::size_type pos = 0; std::string::size_type pos = 0;

View File

@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer" VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@" VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer" 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" VALUE "OriginalFilename", "prusa-gcodeviewer.exe"
} }
} }

View File

@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
VALUE "ProductName", "@SLIC3R_APP_NAME@" VALUE "ProductName", "@SLIC3R_APP_NAME@"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@" VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "@SLIC3R_APP_NAME@" 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" VALUE "OriginalFilename", "prusa-slicer.exe"
} }
} }

View File

@ -5,7 +5,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>@SLIC3R_APP_KEY@</string> <string>@SLIC3R_APP_KEY@</string>
<key>CFBundleGetInfoString</key> <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> <key>CFBundleIconFile</key>
<string>PrusaSlicer.icns</string> <string>PrusaSlicer.icns</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@ -143,6 +143,8 @@ set(SLIC3R_GUI_SOURCES
GUI/RammingChart.hpp GUI/RammingChart.hpp
GUI/RemovableDriveManager.cpp GUI/RemovableDriveManager.cpp
GUI/RemovableDriveManager.hpp GUI/RemovableDriveManager.hpp
GUI/SendSystemInfoDialog.cpp
GUI/SendSystemInfoDialog.hpp
GUI/BonjourDialog.cpp GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp GUI/BonjourDialog.hpp
GUI/ButtonsDescription.cpp GUI/ButtonsDescription.cpp
@ -181,6 +183,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/SLAImportJob.hpp GUI/Jobs/SLAImportJob.hpp
GUI/Jobs/SLAImportJob.cpp GUI/Jobs/SLAImportJob.cpp
GUI/Jobs/ProgressIndicator.hpp GUI/Jobs/ProgressIndicator.hpp
GUI/Jobs/NotificationProgressIndicator.hpp
GUI/Jobs/NotificationProgressIndicator.cpp
GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.hpp
GUI/ProgressStatusBar.cpp GUI/ProgressStatusBar.cpp
GUI/Mouse3DController.cpp GUI/Mouse3DController.cpp

View File

@ -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) { 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.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)); dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
for (auto pl : polylines) for (auto pl : polylines)

View File

@ -9,9 +9,7 @@
#include "3DScene.hpp" #include "3DScene.hpp"
#include "GLShader.hpp" #include "GLShader.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "Plater.hpp" #include "Plater.hpp"
#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp"
@ -25,9 +23,7 @@
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#if ENABLE_SINKING_CONTOURS
#include "libslic3r/Tesselate.hpp" #include "libslic3r/Tesselate.hpp"
#endif // ENABLE_SINKING_CONTOURS
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -158,22 +154,27 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh)
} }
else { else {
#endif // ENABLE_SMOOTH_NORMALS #endif // ENABLE_SMOOTH_NORMALS
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); this->load_its_flat_shading(mesh.its);
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;
}
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
} }
#endif // 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) void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
{ {
assert(this->vertices_and_normals_interleaved_VBO_id == 0); assert(this->vertices_and_normals_interleaved_VBO_id == 0);
@ -288,7 +289,6 @@ void GLIndexedVertexArray::render(
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
#if ENABLE_SINKING_CONTOURS
const float GLVolume::SinkingContours::HalfWidth = 0.25f; const float GLVolume::SinkingContours::HalfWidth = 0.25f;
void GLVolume::SinkingContours::render() void GLVolume::SinkingContours::render()
@ -313,7 +313,6 @@ void GLVolume::SinkingContours::update()
m_shift = Vec3d::Zero(); m_shift = Vec3d::Zero();
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh(); const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
assert(mesh.has_shared_vertices());
m_model.reset(); m_model.reset();
GUI::GLModel::InitializationData init_data; GUI::GLModel::InitializationData init_data;
@ -356,7 +355,6 @@ void GLVolume::SinkingContours::update()
else else
m_model.reset(); 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::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 }; const std::array<float, 4> GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
@ -375,12 +373,8 @@ const std::array<std::array<float, 4>, 4> GLVolume::MODEL_COLOR = { {
} }; } };
GLVolume::GLVolume(float r, float g, float b, float a) GLVolume::GLVolume(float r, float g, float b, float a)
: m_transformed_bounding_box_dirty(true) : m_sla_shift_z(0.0)
, m_sla_shift_z(0.0)
, m_transformed_convex_hull_bounding_box_dirty(true)
#if ENABLE_SINKING_CONTOURS
, m_sinking_contours(*this) , m_sinking_contours(*this)
#endif // ENABLE_SINKING_CONTOURS
// geometry_id == 0 -> invalid // geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0)) , geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0) , extruder_id(0)
@ -398,9 +392,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, force_transparent(false) , force_transparent(false)
, force_native_color(false) , force_native_color(false)
, force_neutral_color(false) , force_neutral_color(false)
#if ENABLE_SINKING_CONTOURS
, force_sinking_contours(false) , force_sinking_contours(false)
#endif // ENABLE_SINKING_CONTOURS
, tverts_range(0, size_t(-1)) , tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1)) , qverts_range(0, size_t(-1))
{ {
@ -505,32 +497,27 @@ bool GLVolume::is_left_handed() const
const BoundingBoxf3& GLVolume::transformed_bounding_box() const const BoundingBoxf3& GLVolume::transformed_bounding_box() const
{ {
if (!m_transformed_bounding_box.has_value()) {
const BoundingBoxf3& box = bounding_box(); 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)); 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);
BoundingBoxf3* transformed_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_bounding_box); *trans_box = box.transformed(world_matrix());
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;
} }
return *transformed_bounding_box; return *m_transformed_bounding_box;
} }
const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
{ {
BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_convex_hull_bounding_box); if (!m_transformed_convex_hull_bounding_box.has_value()) {
bool* transformed_convex_hull_bounding_box_dirty = const_cast<bool*>(&m_transformed_convex_hull_bounding_box_dirty); std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_convex_hull_bounding_box);
if (*transformed_convex_hull_bounding_box_dirty) { *trans_box = transformed_convex_hull_bounding_box(world_matrix());
*transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
*transformed_convex_hull_bounding_box_dirty = false;
} }
return *transformed_convex_hull_bounding_box; return *m_transformed_convex_hull_bounding_box;
} }
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const 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) : m_convex_hull->transformed_bounding_box(trafo) :
bounding_box().transformed(trafo); bounding_box().transformed(trafo);
} }
@ -604,12 +591,10 @@ bool GLVolume::is_below_printbed() const
return transformed_convex_hull_bounding_box().max.z() < 0.0; return transformed_convex_hull_bounding_box().max.z() < 0.0;
} }
#if ENABLE_SINKING_CONTOURS
void GLVolume::render_sinking_contours() void GLVolume::render_sinking_contours()
{ {
m_sinking_contours.render(); m_sinking_contours.render();
} }
#endif // ENABLE_SINKING_CONTOURS
std::vector<int> GLVolumeCollection::load_object( std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object, const ModelObject *model_object,
@ -732,21 +717,20 @@ int GLVolumeCollection::load_wipe_tower_preview(
float min_width = 30.f; float min_width = 30.f;
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model // 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: // 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 }, 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 } }; { 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 }, 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 }, { 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 } }; { 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) for (int i = 0; i < 16; ++i)
points.emplace_back(out_points_idx[i][0] / (100.f / min_width), its.vertices.emplace_back(out_points_idx[i][0] / (100.f / min_width),
out_points_idx[i][1] + depth, out_points_idx[i][2]); out_points_idx[i][1] + depth, out_points_idx[i][2]);
for (int i = 0; i < 28; ++i) its.indices.reserve(28);
facets.emplace_back(out_facets_idx[i][0], for (const int *face : out_facets_idx)
out_facets_idx[i][1], its.indices.emplace_back(face);
out_facets_idx[i][2]); TriangleMesh tooth_mesh(std::move(its));
TriangleMesh tooth_mesh(points, facets);
// We have the mesh ready. It has one tooth and width of min_width. We will now // 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 // append several of these together until we are close to the required width
@ -757,7 +741,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
tooth_mesh.translate(min_width, 0.f, 0.f); 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 else
mesh = make_cube(width, depth, height); mesh = make_cube(width, depth, height);
@ -833,11 +817,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 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); GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
if (to_render.empty()) if (to_render.empty())
return; return;
#endif // ENABLE_SINKING_CONTOURS
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
if (shader == nullptr) if (shader == nullptr)
@ -852,7 +834,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
if (disable_cullface) if (disable_cullface)
glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glDisable(GL_CULL_FACE));
#if ENABLE_SINKING_CONTOURS
for (GLVolumeWithIdAndZ& volume : to_render) { for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color(); volume.first->set_render_color();
@ -902,7 +883,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
} }
if (m_show_sinking_contours) if (m_show_sinking_contours) {
for (GLVolumeWithIdAndZ& volume : to_render) { for (GLVolumeWithIdAndZ& volume : to_render) {
// render sinking contours of hovered/displaced volumes // render sinking contours of hovered/displaced volumes
if (volume.first->is_sinking() && !volume.first->is_below_printbed() && if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
@ -914,48 +895,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
shader->start_using(); 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) if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glEnable(GL_CULL_FACE));

Some files were not shown because too many files have changed in this diff Show More