Merge branch 'master' into et_tm_sla_volumes_6-SPE-1285

This commit is contained in:
tamasmeszaros 2023-01-13 10:00:57 +01:00
commit fc9b7ed59c
211 changed files with 10741 additions and 3369 deletions

View File

@ -13,8 +13,8 @@ AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false

View File

@ -3,11 +3,10 @@
<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 id="expand">
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="4" x2="12" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="12" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="12"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="4" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="12"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="12" x2="12" y2="8"/></g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 915 B

View File

@ -1,28 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, 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 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="cut">
<g>
<path fill="#ED6B21" d="M118.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S118.95,65.5,118.12,65.5z M98.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S98.95,65.5,98.12,65.5z M78.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S78.95,65.5,78.12,65.5z M58.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S58.95,65.5,58.12,65.5z M38.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S38.95,65.5,38.12,65.5z M18.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S18.95,65.5,18.12,65.5z"/>
</g>
<g>
<g>
<path fill="#808080" d="M108.79,51.6H19.21c-1.93,0-3.5-1.57-3.5-3.5V10.12c0-1.93,1.57-3.5,3.5-3.5h89.57
c1.93,0,3.5,1.57,3.5,3.5V48.1C112.29,50.03,110.71,51.6,108.79,51.6z M19.21,9.62c-0.27,0-0.5,0.23-0.5,0.5V48.1
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V10.12c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
</g>
<g>
<path fill="#808080" d="M108.79,121.38H19.21c-1.93,0-3.5-1.57-3.5-3.5V79.4c0-1.93,1.57-3.5,3.5-3.5h89.57
c1.93,0,3.5,1.57,3.5,3.5v38.49C112.29,119.81,110.71,121.38,108.79,121.38z M19.21,78.9c-0.27,0-0.5,0.23-0.5,0.5v38.49
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V79.4c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
</g>
</g>
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path fill="#ED6B21" d="M3.0597045,10.3634434H0.5884437C0.2628375,10.3634434,0,10.100606,0,9.7750006
c0-0.3256063,0.2628375-0.5884438,0.5884437-0.5884438h2.4712608c0.3256061,0,0.5884435,0.2628374,0.5884435,0.5884438
C3.6481481,10.100606,3.3853078,10.3634434,3.0597045,10.3634434z"/>
<path fill="#ED6B21" d="M12.0967369,10.3634434h-2.471261c-0.3256063,0-0.5884438-0.2628374-0.5884438-0.5884428
c0-0.3256063,0.2628374-0.5884438,0.5884438-0.5884438h2.471261c0.3256063,0,0.5884438,0.2628374,0.5884438,0.5884438
C12.6851807,10.100606,12.4223404,10.3634434,12.0967369,10.3634434z"/>
<path fill="#ED6B21" d="M7.5782208,10.3634434h-2.471261c-0.3256059,0-0.5884438-0.2628374-0.5884438-0.5884428
c0-0.3256063,0.2628379-0.5884438,0.5884438-0.5884438h2.471261c0.3256059,0,0.5884433,0.2628374,0.5884433,0.5884438
C8.1666641,10.100606,7.9038239,10.3634434,7.5782208,10.3634434z"/>
<g>
<path fill="#808080" d="M10.98001,11.9849854c-0.289978,0-0.5199585,0.2299805-0.5199585,0.5199585v0.5922852v0.3327637
c0,0.289978-0.2300415,0.5200195-0.5200195,0.5200195H2.7452075c-0.289978,0-0.5200195-0.2300415-0.5200195-0.5200195V13.097229
v-0.5922852c0-0.289978-0.2299803-0.5199585-0.5199584-0.5199585c-0.2900391,0-0.5200195,0.2299805-0.5200195,0.5199585v0.5922852
v0.3327637C1.1852101,14.2999878,1.8952321,15,2.7552173,15h7.1748047c0.8599854,0,1.5700073-0.7000122,1.5700073-1.5700073
V13.097229v-0.5922852C11.5000296,12.2149658,11.2700491,11.9849854,10.98001,11.9849854z"/>
<path fill="#808080" d="M9.9300222,4.5499878H2.7552173c-0.8599852,0-1.5700072,0.7000122-1.5700072,1.5700073v0.3327637v0.5922852
c0,0.289978,0.2299805,0.5199585,0.5200195,0.5199585c0.289978,0,0.5199584-0.2299805,0.5199584-0.5199585V6.4527588V6.1199951
c0-0.289978,0.2300415-0.5200195,0.5200195-0.5200195H9.940032c0.289978,0,0.5200195,0.2300415,0.5200195,0.5200195v0.3327637
v0.5922852c0,0.289978,0.2299805,0.5199585,0.5199585,0.5199585c0.2900391,0,0.5200195-0.2299805,0.5200195-0.5199585V6.4527588
V6.1199951C11.5000296,5.25,10.7900076,4.5499878,9.9300222,4.5499878z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,26 +1,20 @@
<?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 id="add_x5F_part">
<g>
<path fill="#ED6B21" d="M14.62,4.37c-0.01-0.14,0.06-0.34,0.15-0.44l0.13-0.15c0.09-0.11,0.12-0.3,0.07-0.43l-0.2-0.49
c-0.05-0.13-0.21-0.24-0.35-0.25l-0.2-0.01c-0.14-0.01-0.33-0.1-0.42-0.21c-0.09-0.1-0.37-0.46-0.38-0.6l-0.01-0.2
c-0.01-0.14-0.12-0.3-0.25-0.35l-0.49-0.2C12.52,0.97,12.33,1,12.22,1.1l-0.15,0.13c-0.11,0.09-0.31,0.16-0.44,0.15
c-0.14-0.01-0.59-0.06-0.69-0.15L10.78,1.1c-0.11-0.09-0.3-0.12-0.43-0.07l-0.49,0.2C9.73,1.28,9.61,1.44,9.6,1.58l-0.01,0.2
C9.58,1.92,9.49,2.11,9.38,2.2c-0.1,0.09-0.46,0.37-0.6,0.38L8.58,2.6c-0.14,0.01-0.3,0.12-0.35,0.25l-0.2,0.49
C7.97,3.48,8,3.67,8.1,3.78l0.13,0.15c0.09,0.11,0.16,0.31,0.15,0.44C8.37,4.52,8.32,4.96,8.23,5.07L8.1,5.22
C8,5.33,7.97,5.52,8.03,5.65l0.2,0.49C8.28,6.27,8.44,6.39,8.58,6.4l0.2,0.01c0.14,0.01,0.33,0.1,0.42,0.21
c0.09,0.1,0.37,0.46,0.38,0.6l0.01,0.2c0.01,0.14,0.12,0.3,0.25,0.35l0.49,0.2C10.48,8.03,10.67,8,10.78,7.9l0.15-0.13
c0.11-0.09,0.31-0.16,0.44-0.15c0.14,0.01,0.59,0.06,0.69,0.15l0.15,0.13c0.11,0.09,0.3,0.12,0.43,0.07l0.49-0.2
c0.13-0.05,0.24-0.21,0.25-0.35l0.01-0.2c0.01-0.14,0.1-0.33,0.21-0.42s0.46-0.37,0.6-0.38l0.2-0.01c0.14-0.01,0.3-0.12,0.35-0.25
l0.2-0.49C15.03,5.52,15,5.33,14.9,5.22l-0.13-0.15C14.68,4.96,14.63,4.51,14.62,4.37z M11.5,6.6c-1.16,0-2.1-0.94-2.1-2.1
s0.94-2.1,2.1-2.1s2.1,0.94,2.1,2.1S12.66,6.6,11.5,6.6z"/>
</g>
<path fill="#808080" d="M10.98,9.78c-0.29,0-0.52,0.23-0.52,0.52v2.09v1.04c0,0.29-0.23,0.52-0.52,0.52H2.62
c-0.29,0-0.53-0.24-0.53-0.53L2.04,6.12c0-0.14,0.05-0.27,0.15-0.37c0.1-0.1,0.23-0.15,0.37-0.15l3.19,0v0
c0.29,0,0.52-0.23,0.52-0.52S6.04,4.55,5.75,4.55H3.66c-0.01,0-0.01,0-0.02,0l-1.08,0c-0.42,0-0.81,0.16-1.11,0.46
C1.16,5.31,1,5.71,1,6.13l0.04,7.31C1.05,14.3,1.75,15,2.62,15h7.31c0.86,0,1.57-0.7,1.57-1.57v-1.04V10.3
C11.5,10.01,11.27,9.78,10.98,9.78z"/>
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path fill="#ED6B21" d="M7.2122064,10.8825388H5.473033c-0.6128426,0-1.1075444-0.4947023-1.1075444-1.1075449
S4.8601904,8.667449,5.473033,8.667449h1.7391734c0.6128426,0,1.1075444,0.4947023,1.1075444,1.1075449
S7.8250437,10.8825388,7.2122064,10.8825388z"/>
<g>
<path fill="#808080" d="M10.98001,15c-0.289978,0-0.5199585-0.2299805-0.5199585-0.5199585v-0.5922852v-0.3327637
c0-0.289978-0.2300415-0.5200195-0.5200195-0.5200195H2.7452075c-0.289978,0-0.5200195,0.2300415-0.5200195,0.5200195v0.3327637
v0.5922852C2.225188,14.7700195,1.9952077,15,1.7052296,15c-0.2900391,0-0.5200195-0.2299805-0.5200195-0.5199585v-0.5922852
v-0.3327637c0-0.8699951,0.710022-1.5700073,1.5700072-1.5700073h7.1748047c0.8599854,0,1.5700073,0.7000122,1.5700073,1.5700073
v0.3327637v0.5922852C11.5000296,14.7700195,11.2700491,15,10.98001,15z"/>
<path fill="#808080" d="M9.9300222,7.5650024H2.7552173c-0.8599852,0-1.5700072-0.7000122-1.5700072-1.5700073V5.6622314V5.0699463
c0-0.289978,0.2299805-0.5199585,0.5200195-0.5199585c0.289978,0,0.5199584,0.2299805,0.5199584,0.5199585v0.5922852v0.3327637
c0,0.289978,0.2300415,0.5200195,0.5200195,0.5200195H9.940032c0.289978,0,0.5200195-0.2300415,0.5200195-0.5200195V5.6622314
V5.0699463c0-0.289978,0.2299805-0.5199585,0.5199585-0.5199585c0.2900391,0,0.5200195,0.2299805,0.5200195,0.5199585v0.5922852
v0.3327637C11.5000296,6.8649902,10.7900076,7.5650024,9.9300222,7.5650024z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -3,10 +3,11 @@
<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 id="expand">
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="4" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="12"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="12" x2="12" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="4" x2="12" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="12" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="12"/></g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 919 B

10
resources/icons/mode.svg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.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 id="hex_x5F_green">
<g>
<polygon fill="#ED6B21" points="8,1 2,5 2,7 2,11 8,15 14,11 14,7 14,5 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@ -0,0 +1,63 @@
<?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"
version="1.0"
id="close_window"
x="0px"
y="0px"
viewBox="0 0 100 100"
enable-background="new 0 0 100 100"
xml:space="preserve"
sodipodi:docname="notification_open.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><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:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1369"
id="namedview15"
showgrid="false"
inkscape:zoom="10.08"
inkscape:cx="50"
inkscape:cy="50"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="close_window" />
<g
id="g4">
<path
fill="#ED6B21"
d="M80,92.83H20c-7.08,0-12.83-5.76-12.83-12.84V20c0-7.08,5.76-12.83,12.83-12.83h60 c7.08,0,12.84,5.76,12.84,12.83v60C92.83,87.08,87.08,92.83,80,92.83z M20,12.83c-3.95,0-7.17,3.21-7.17,7.17v60 c0,3.95,3.21,7.17,7.17,7.17h60c3.95,0,7.17-3.21,7.17-7.17V20c0-3.95-3.21-7.17-7.17-7.17H20z"
id="path2" />
</g>
<g
id="open"
transform="matrix(4.05,0,0,4.05,15.4645,15.277381)"
style="fill:#ed6b21;fill-opacity:1"><path
id="path2-7"
d="M 1.22,14 V 3 c 0,0 0,-1 1,-1 1,0 4,0 5,0 1,0 1,2 2,2 1,0 4,0 4,0 0,0 1,0 1,1 v 2 h -1 c 0,0 0,0 0,-1 0,-1 -1,-1 -1,-1 h -3.5 c -1,0 -1,-2 -2,-2 -1,0 -3.5,0 -3.5,0 -1,0 -1,1 -1,1 v 9 1 h 1 v 1 c 0,0 0,0 -1,0 -1,0 -1,-1 -1,-1 z"
fill="#808080"
style="fill:#ed6b21;fill-opacity:1" /><path
id="path4"
d="M 5,6 C 4.45,6 3.86,6.43 3.68,6.95 l -2.37,7.1 C 1.14,14.57 1.45,15 2,15 h 10 c 0.55,0 1.14,-0.43 1.32,-0.95 l 2.37,-7.1 C 15.86,6.43 15.55,6 15,6 Z"
fill="#ed6b21"
style="fill:#ed6b21;fill-opacity:1" /></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,64 @@
<?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"
version="1.0"
id="close_window"
x="0px"
y="0px"
viewBox="0 0 100 100"
enable-background="new 0 0 100 100"
xml:space="preserve"
sodipodi:docname="notification_open_hover.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs17" /><sodipodi:namedview
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1369"
id="namedview15"
showgrid="false"
inkscape:zoom="10.08"
inkscape:cx="50"
inkscape:cy="50"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="close_window" />
<g
transform="matrix(1.15,0,0,1.15,-7.50075,-7.5)"
id="g4">
<path
fill="#ed6b21"
d="M 80,92.83 H 20 C 12.92,92.83 7.17,87.07 7.17,79.99 V 20 C 7.17,12.92 12.93,7.17 20,7.17 h 60 c 7.08,0 12.84,5.76 12.84,12.83 V 80 C 92.83,87.08 87.08,92.83 80,92.83 Z m -60,-80 c -3.95,0 -7.17,3.21 -7.17,7.17 v 60 c 0,3.95 3.21,7.17 7.17,7.17 h 60 c 3.95,0 7.17,-3.21 7.17,-7.17 V 20 c 0,-3.95 -3.21,-7.17 -7.17,-7.17 z"
id="path2" />
</g>
<g
id="open"
transform="matrix(4.6575,0,0,4.6575,10.313937,10.113631)"
style="fill:#ed6b21;fill-opacity:1"><path
id="path2-7"
d="M 1.22,14 V 3 c 0,0 0,-1 1,-1 1,0 4,0 5,0 1,0 1,2 2,2 1,0 4,0 4,0 0,0 1,0 1,1 v 2 h -1 c 0,0 0,0 0,-1 0,-1 -1,-1 -1,-1 h -3.5 c -1,0 -1,-2 -2,-2 -1,0 -3.5,0 -3.5,0 -1,0 -1,1 -1,1 v 9 1 h 1 v 1 c 0,0 0,0 -1,0 -1,0 -1,-1 -1,-1 z"
fill="#808080"
style="fill:#ed6b21;fill-opacity:1" /><path
id="path4"
d="M 5,6 C 4.45,6 3.86,6.43 3.68,6.95 l -2.37,7.1 C 1.14,14.57 1.45,15 2,15 h 10 c 0.55,0 1.14,-0.43 1.32,-0.95 l 2.37,-7.1 C 15.86,6.43 15.55,6 15,6 Z"
fill="#ed6b21"
style="fill:#ed6b21;fill-opacity:1" /></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,75 @@
<?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)"
sodipodi:docname="notification_pause.svg"
xml:space="preserve"
style="enable-background:new 0 0 800 800;"
viewBox="0 0 800 800"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata15"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:cy="459.92063"
inkscape:cx="400"
inkscape:zoom="1.26"
showgrid="false"
id="namedview11"
inkscape:window-height="1369"
inkscape:window-width="2560"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff"
inkscape:document-rotation="0" />
<style
id="style2"
type="text/css">
.st0{fill:#ED6B21;}
</style>
<path
id="path4"
d="m 317.95173,562.36128 h -60.775 v -330.565 h 60.775 v 330.565 m 48.195,27.88 v -386.24 c 0,-11.22 -9.095,-20.315 -20.315,-20.315 h -116.535 c -11.22,0 -20.315,9.095 -20.315,20.315 v 386.24 c 0,11.22 9.095,20.315 20.315,20.315 h 116.45 c 11.22,0 20.4,-9.095 20.4,-20.315 z"
class="st0"
style="stroke-width:0.85" />
<g
transform="matrix(0.9775,0,0,0.9775,53.547,53.54775)"
id="g4">
<path
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z"
class="st0"
id="path2" />
</g>
<path
id="path17"
d="m 150.65676,738.12999 c -12.4717,-1.39663 -26.66772,-5.94192 -37.84321,-12.11671 -17.754551,-9.80992 -33.768844,-26.68981 -42.418124,-44.71089 -5.985061,-12.4701 -8.760227,-23.35456 -9.821918,-38.52249 -0.48061,-6.8663 -0.640464,-87.42616 -0.497289,-250.61508 0.195544,-222.88027 0.294923,-240.94223 1.356742,-246.58759 4.2349,-22.51562 13.68014,-40.62012 29.200931,-55.97194 14.237938,-14.082924 31.958648,-23.427941 52.602238,-27.739791 5.87892,-1.227937 14.00696,-1.268146 256.3492,-1.268146 h 250.27778 l 7.08334,1.561512 c 21.30688,4.697075 36.90336,13.216072 51.96052,28.381502 14.67865,14.784203 23.1932,30.350373 27.76125,50.752683 l 1.56791,7.00271 v 250.95239 c 0,242.72256 -0.0418,251.15149 -1.26428,257.0238 -9.30592,44.69034 -45.18963,77.43352 -89.75566,81.90028 -9.17898,0.92002 -488.33076,0.87927 -496.55943,-0.0425 z M 652.87275,692.49 c 19.93824,-6.17834 34.6922,-21.42493 40.00111,-41.33675 l 1.51306,-5.67494 V 399.58544 153.69259 l -1.52571,-5.73412 c -5.66288,-21.28292 -21.4158,-36.89778 -42.2051,-41.83523 -5.63965,-1.33941 -7.66026,-1.3488 -253.17948,-1.17613 l -247.49447,0.17405 -4.72222,1.5953 c -18.05932,6.10093 -31.7315,19.23923 -37.4918,36.0278 -1.04762,3.05333 -2.22128,7.52472 -2.60813,9.93642 -0.47859,2.9836 -0.705,81.91876 -0.70847,246.99889 -0.005,218.14117 0.10226,243.1829 1.05916,248.25397 4.27172,22.63802 22.24346,40.86392 44.80877,45.4425 3.58848,0.72811 49.16893,0.87009 250.95237,0.78171 l 246.56747,-0.10801 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603" /><path
id="path21"
d="m 210.06357,199.0892 c 1.10499,-4.08272 3.30912,-7.53117 6.63124,-10.37477 5.42019,-4.63948 2.10678,-4.43387 71.33297,-4.42657 l 62.44927,0.007 3.44194,1.60268 c 4.05635,1.88877 7.75734,5.3977 9.8769,9.36436 l 1.52243,2.84915 v 199.00794 199.00793 l -1.52243,2.84915 c -2.11956,3.96665 -5.82055,7.47559 -9.8769,9.36436 l -3.44194,1.60267 -62.44927,0.007 c -69.78764,0.008 -65.98231,0.26152 -71.72146,-4.79028 -1.69319,-1.4904 -3.87229,-4.37627 -4.9283,-6.52672 l -1.89304,-3.85513 -0.12602,-196.30953 c -0.0987,-153.67069 0.0544,-196.97613 0.70461,-199.37859 z m 77.67539,363.64446 H 318.4334 V 397.11858 231.5035 H 287.73896 257.0445 v 165.61508 165.61508 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603" /><path
style="fill:#ed6b21;stroke-width:0.85"
class="st0"
d="m 533.78507,563.71023 h -60.775 v -330.565 h 60.775 v 330.565 m 48.195,27.88 v -386.24 c 0,-11.22 -9.095,-20.315 -20.315,-20.315 h -116.535 c -11.22,0 -20.315,9.095 -20.315,20.315 v 386.24 c 0,11.22 9.095,20.315 20.315,20.315 h 116.45 c 11.22,0 20.4,-9.095 20.4,-20.315 z"
id="path4-3" /><path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603"
d="m 425.89691,200.43815 c 1.10499,-4.08272 3.30912,-7.53117 6.63124,-10.37477 5.42019,-4.63948 2.10678,-4.43387 71.33297,-4.42657 l 62.44927,0.007 3.44194,1.60268 c 4.05635,1.88877 7.75734,5.3977 9.8769,9.36436 l 1.52243,2.84915 v 199.00794 199.00793 l -1.52243,2.84915 c -2.11956,3.96665 -5.82055,7.47559 -9.8769,9.36436 l -3.44194,1.60267 -62.44927,0.007 c -69.78764,0.008 -65.98231,0.26152 -71.72146,-4.79028 -1.69319,-1.4904 -3.87229,-4.37627 -4.9283,-6.52672 l -1.89304,-3.85513 -0.12602,-196.30953 c -0.0987,-153.67069 0.0544,-196.97613 0.70461,-199.37859 z m 77.67539,363.64446 h 30.69444 V 398.46753 232.85245 H 503.5723 472.87784 v 165.61508 165.61508 z"
id="path21-9" /></svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,75 @@
<?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"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 800 800"
style="enable-background:new 0 0 800 800;"
xml:space="preserve"
sodipodi:docname="notification_pause_hover.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
id="metadata15"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><sodipodi:namedview
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1369"
id="namedview11"
showgrid="false"
inkscape:zoom="1.26"
inkscape:cx="400"
inkscape:cy="459.92063"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#ED6B21;}
</style>
<path
style="stroke-width:0.9775"
class="st0"
d="M 322.50986,587.14728 H 252.61861 V 206.99753 h 69.89125 v 380.14975 m 55.42424,32.062 v -444.176 c 0,-12.903 -10.45925,-23.36225 -23.36225,-23.36225 H 220.55661 c -12.903,0 -23.36225,10.45925 -23.36225,23.36225 v 444.176 c 0,12.903 10.45925,23.36225 23.36225,23.36225 H 354.4741 c 12.90301,0 23.46,-10.45925 23.46,-23.36225 z"
id="path4" />
<g
id="g4"
transform="matrix(1.124125,0,0,1.124125,1.6564125,1.6571625)">
<path
id="path2"
class="st0"
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
</g>
<path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793"
d="M 113.38564,788.89154 C 99.043181,787.28541 82.717758,782.05833 69.865944,774.95732 49.44821,763.67591 31.031774,744.26404 21.085102,723.5398 14.202281,709.19918 11.01084,696.68205 9.7898958,679.23893 9.2371943,671.34269 9.0533622,578.69885 9.2180135,391.03159 9.4428891,134.71928 9.5571749,113.94803 10.778267,107.45586 15.648402,81.5629 26.510428,60.742725 44.359337,43.088132 60.732966,26.892769 81.111783,16.146 104.85191,11.187372 111.61267,9.7752445 120.95992,9.7290042 399.65349,9.7290042 h 287.81945 l 8.14584,1.7957388 c 24.50291,5.401636 42.43886,15.198483 59.7546,32.638727 16.88044,17.001834 26.67218,34.902929 31.92543,58.36559 l 1.8031,8.05311 v 288.59525 c 0,279.13094 -0.0481,288.82421 -1.45392,295.57737 -10.70181,51.39389 -51.96808,89.04855 -103.21901,94.18532 -10.55583,1.05803 -561.58037,1.01116 -571.04334,-0.0489 z m 577.54838,-52.48599 c 22.92898,-7.10509 39.89603,-24.63867 46.00128,-47.53726 l 1.74002,-6.52619 V 399.5653 116.78853 l -1.75457,-6.59424 c -6.51231,-24.475359 -24.62817,-42.432448 -48.53586,-48.110516 -6.4856,-1.540321 -8.8093,-1.55112 -291.1564,-1.352549 l -284.61864,0.200157 -5.43056,1.834595 c -20.768215,7.01607 -36.491222,22.125115 -43.115567,41.431973 -1.204763,3.51133 -2.554472,8.65343 -2.99935,11.42688 -0.550378,3.43114 -0.81075,94.20657 -0.81474,284.04872 -0.0058,250.86235 0.117599,279.66034 1.218034,285.49207 4.912478,26.03372 25.579979,46.99351 51.530083,52.25887 4.12675,0.83733 56.54427,1.00061 288.59523,0.89897 l 283.55259,-0.12421 z"
id="path17" /><path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793"
d="m 198.47436,169.38465 c 1.27074,-4.69513 3.80549,-8.66084 7.62593,-11.93098 6.23322,-5.33541 2.4228,-5.09896 82.03292,-5.09056 l 71.81666,0.008 3.95823,1.84308 c 4.6648,2.17209 8.92094,6.20736 11.35843,10.76902 l 1.7508,3.27652 v 228.85913 228.85912 l -1.7508,3.27652 c -2.43749,4.56165 -6.69363,8.59693 -11.35843,10.76902 l -3.95823,1.84307 -71.81666,0.008 c -80.25579,0.009 -75.87966,0.30074 -82.47968,-5.50883 -1.94717,-1.71396 -4.45314,-5.03271 -5.66755,-7.50572 l -2.17699,-4.4334 -0.14493,-225.75596 c -0.1135,-176.7213 0.0626,-226.52255 0.8103,-229.28538 z m 89.3267,418.19113 h 35.29861 V 397.11844 206.6611 h -35.29861 -35.29863 v 190.45734 190.45734 z"
id="path21" /><path
id="path4-3"
d="M 538.3432,588.49623 H 468.45195 V 208.34648 h 69.89125 v 380.14975 m 55.42425,32.062 v -444.176 c 0,-12.903 -10.45925,-23.36225 -23.36225,-23.36225 H 436.38995 c -12.903,0 -23.36225,10.45925 -23.36225,23.36225 v 444.176 c 0,12.903 10.45925,23.36225 23.36225,23.36225 h 133.9175 c 12.903,0 23.46,-10.45925 23.46,-23.36225 z"
class="st0"
style="fill:#ed6b21;stroke-width:0.9775" /><path
id="path21-9"
d="m 414.3077,170.7336 c 1.27074,-4.69513 3.80549,-8.66084 7.62593,-11.93098 6.23322,-5.33541 2.4228,-5.09896 82.03292,-5.09056 l 71.81666,0.008 3.95823,1.84308 c 4.6648,2.17209 8.92094,6.20736 11.35843,10.76902 l 1.7508,3.27652 v 228.85913 228.85912 l -1.7508,3.27652 c -2.43749,4.56165 -6.69363,8.59693 -11.35843,10.76902 l -3.95823,1.84307 -71.81666,0.008 c -80.25579,0.009 -75.87966,0.30074 -82.47968,-5.50883 -1.94717,-1.71396 -4.45314,-5.03271 -5.66755,-7.50572 l -2.17699,-4.4334 -0.14493,-225.75596 c -0.1135,-176.7213 0.0626,-226.52255 0.8103,-229.28538 z m 89.3267,418.19113 h 35.29861 V 398.46739 208.01005 H 503.6344 468.33577 v 190.45734 190.45734 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.775793" /></svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,75 @@
<?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)"
sodipodi:docname="notification_play.svg"
xml:space="preserve"
style="enable-background:new 0 0 800 800;"
viewBox="0 0 800 800"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata15"><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="defs13" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:cy="407.44757"
inkscape:cx="291.98172"
inkscape:zoom="1.7819091"
showgrid="false"
id="namedview11"
inkscape:window-height="1369"
inkscape:window-width="2560"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff"
inkscape:document-rotation="0" />
<style
id="style2"
type="text/css">
.st0{fill:#ED6B21;}
</style>
<path
id="path6"
d="M 501.14231,400.1595 345.84732,517.11951 v -233.75 l 155.29499,116.79 m 66.63999,0 c 0,-6.12 -2.72,-12.155 -8.15999,-16.235 L 330.12233,211.2895 c -13.43,-10.115 -32.555,-0.51 -32.555,16.235 v 345.35501 c 0,16.745 19.125,26.35 32.555,16.235 l 229.49998,-172.805 c 5.44,-3.995 8.16,-10.03 8.15999,-16.15 z"
class="st0"
style="stroke-width:0.85" />
<g
transform="matrix(0.9775,0,0,0.9775,53.547,53.54776)"
id="g4">
<path
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z"
class="st0"
id="path2" />
</g>
<path
id="path17"
d="m 150.65676,738.13001 c -12.4717,-1.39665 -26.66772,-5.9419 -37.84321,-12.1167 C 95.05898,716.20336 79.0447,699.32348 70.39542,681.3024 64.41036,668.8323 61.63518,657.94784 60.5735,642.7799 c -0.48062,-6.86631 -0.64046,-87.42616 -0.4973,-250.61509 0.1956,-222.88026 0.29494,-240.94222 1.35676,-246.58758 4.2349,-22.51562 13.68014,-40.62012 29.20092,-55.971935 14.23795,-14.08294 31.95867,-23.42796 52.60225,-27.7398 5.87892,-1.22794 14.00696,-1.26814 256.3492,-1.26814 h 250.27778 l 7.08334,1.5615 c 21.30688,4.69708 36.90336,13.21608 51.96053,28.3815 14.67865,14.784215 23.1932,30.350375 27.76125,50.752695 l 1.5679,7.0027 v 250.95241 c 0,242.72256 -0.042,251.15147 -1.2643,257.0238 -9.3059,44.69035 -45.18961,77.4335 -89.75565,81.9003 -9.17897,0.92 -488.33076,0.87925 -496.55942,-0.0425 z M 652.87274,692.49 c 19.93824,-6.17835 34.6922,-21.42494 40.00112,-41.33676 l 1.51306,-5.67491 V 399.58544 153.69258 l -1.52572,-5.7341 c -5.66288,-21.28292 -21.4158,-36.89778 -42.2051,-41.83524 -5.63964,-1.3394 -7.66026,-1.3488 -253.17948,-1.17612 l -247.49446,0.174 -4.72222,1.5953 c -18.05932,6.10094 -31.7315,19.23924 -37.4918,36.02779 -1.04762,3.05335 -2.22128,7.52472 -2.60814,9.93642 -0.47858,2.98361 -0.705,81.91877 -0.70846,246.99891 -0.004,218.14116 0.1022,243.18289 1.05916,248.25395 4.27172,22.63803 22.24346,40.86393 44.80876,45.44251 3.58848,0.72811 49.16894,0.8701 250.95238,0.7817 l 246.56746,-0.10799 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746" /><path
id="path19"
d="m 300.17824,218.44362 c 2.06443,-4.13128 4.83026,-6.86894 9.40917,-9.3133 3.43647,-1.83451 12.82007,-1.79344 16.52778,0.0724 3.22403,1.6224 232.7236,174.17375 235.79013,177.281 3.35767,3.4022 4.88323,7.0012 5.23218,12.34324 0.27168,4.159 0.0718,5.32914 -1.5138,8.86232 -1.00471,2.23886 -2.78731,4.97342 -3.96129,6.07682 -4.28583,4.02812 -232.27571,175.25818 -235.54572,176.9048 -4.6586,2.34584 -12.1025,2.3876 -16.52928,0.0928 -3.87486,-2.00878 -7.80538,-5.7435 -9.67029,-9.18862 l -1.46069,-2.69842 -0.1736,-178.1462 -0.1736,-178.14618 z m 45.66908,298.67589 c 1.00351,-0.0612 154.99009,-116.48775 154.99009,-117.18534 0,-0.71184 -154.02593,-116.84804 -154.99009,-116.86318 -0.27827,-0.004 -0.50595,52.66168 -0.50595,117.03568 0,64.37401 0.22768,117.0298 0.50595,117.01284 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746" /><path
style="fill:#ed6b21;stroke-width:0.85"
class="st0"
d="M 501.14231,400.15949 345.84732,517.1195 v -233.75 l 155.29499,116.79 m 66.63999,0 c 0,-6.12 -2.72,-12.155 -8.15999,-16.235 L 330.12233,211.28949 c -13.43,-10.115 -32.555,-0.51 -32.555,16.235 V 572.8795 c 0,16.745 19.125,26.35 32.555,16.235 l 229.49998,-172.805 c 5.44,-3.995 8.16,-10.03 8.15999,-16.15 z"
id="path6-2" /><path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.6746"
d="m 300.17824,218.44361 c 2.06443,-4.13128 4.83026,-6.86894 9.40917,-9.3133 3.43647,-1.83451 12.82007,-1.79344 16.52778,0.0724 3.22403,1.6224 232.7236,174.17375 235.79013,177.281 3.35767,3.4022 4.88323,7.0012 5.23218,12.34324 0.27168,4.159 0.0718,5.32914 -1.5138,8.86232 -1.00471,2.23886 -2.78731,4.97342 -3.96129,6.07682 -4.28583,4.02812 -232.27571,175.25818 -235.54572,176.9048 -4.6586,2.34584 -12.1025,2.3876 -16.52928,0.0928 -3.87486,-2.00878 -7.80538,-5.7435 -9.67029,-9.18862 l -1.46069,-2.69842 -0.1736,-178.1462 -0.1736,-178.14618 z m 45.66908,298.67589 c 1.00351,-0.0612 154.99009,-116.48775 154.99009,-117.18534 0,-0.71184 -154.02593,-116.84804 -154.99009,-116.86318 -0.27827,-0.004 -0.50595,52.66168 -0.50595,117.03568 0,64.37401 0.22768,117.0298 0.50595,117.01284 z"
id="path19-3" /></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,75 @@
<?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"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 800 800"
style="enable-background:new 0 0 800 800;"
xml:space="preserve"
sodipodi:docname="notification_play_hover.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
id="metadata15"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><sodipodi:namedview
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1369"
id="namedview11"
showgrid="false"
inkscape:zoom="0.89095455"
inkscape:cx="422.22291"
inkscape:cy="611.44958"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#ED6B21;}
</style>
<path
style="stroke-width:0.9775"
class="st0"
d="M 511.41243,400.15312 332.8232,534.65714 v -268.8125 l 178.58923,134.3085 m 76.63599,0 c 0,-7.038 -3.128,-13.97825 -9.38399,-18.67025 L 314.73946,182.95262 c -15.4445,-11.63225 -37.43825,-0.5865 -37.43825,18.67025 v 397.15827 c 0,19.25675 21.99375,30.3025 37.43825,18.67025 L 578.66443,418.72564 c 6.256,-4.59425 9.384,-11.5345 9.38399,-18.5725 z"
id="path6" />
<g
id="g4"
transform="matrix(1.124125,0,0,1.124125,1.6564125,1.6571725)">
<path
id="path2"
class="st0"
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
</g>
<path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579"
d="M 113.38564,788.89156 C 99.043182,787.28541 82.717759,782.05837 69.865945,774.95735 49.44819,763.67591 31.031768,744.26405 21.085096,723.53981 14.202277,709.19919 11.01082,696.68206 9.7898876,679.23893 9.2371746,671.34268 9.0533586,578.69885 9.2179926,391.03158 9.4429326,134.71928 9.5571736,113.94803 10.778267,107.45586 15.648402,81.562898 26.510428,60.742723 44.359325,43.088136 60.732967,26.892755 81.111795,16.145982 104.85191,11.187366 111.61267,9.7752352 120.95992,9.7290052 399.65349,9.7290052 h 287.81945 l 8.14584,1.7957248 c 24.50291,5.401642 42.43886,15.198492 59.75461,32.638725 16.88045,17.001847 26.67218,34.902931 31.92544,58.365595 l 1.80308,8.05311 v 288.59527 c 0,279.13094 -0.0483,288.82419 -1.45394,295.57737 -10.70179,51.3939 -51.96805,89.04853 -103.219,94.18535 -10.55582,1.058 -561.58037,1.01113 -571.04333,-0.0489 z m 577.54837,-52.48601 c 22.92898,-7.10511 39.89603,-24.63868 46.00129,-47.53728 l 1.74002,-6.52614 V 399.5653 116.78851 l -1.75458,-6.59421 c -6.51231,-24.475359 -24.62817,-42.432448 -48.53586,-48.110527 -6.48559,-1.54031 -8.8093,-1.55112 -291.1564,-1.352538 l -284.61863,0.2001 -5.43056,1.834595 c -20.768214,7.016081 -36.491221,22.125126 -43.115566,41.43196 -1.204763,3.51135 -2.554472,8.65343 -2.999361,11.42688 -0.550367,3.43115 -0.81075,94.20659 -0.814729,284.04875 -0.0046,250.86233 0.11753,279.66032 1.218034,285.49204 4.912478,26.03373 25.579979,46.99352 51.530072,52.25889 4.12675,0.83732 56.54428,1.00061 288.59524,0.89895 l 283.55258,-0.12419 z"
id="path17" /><path
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579"
d="m 280.30386,191.19243 c 2.37409,-4.75097 5.55479,-7.89928 10.82054,-10.7103 3.95194,-2.10968 14.74308,-2.06245 19.00695,0.0833 3.70763,1.86576 267.63214,200.29982 271.15865,203.87315 3.86132,3.91253 5.61571,8.05138 6.017,14.19473 0.31244,4.78285 0.0826,6.12851 -1.74087,10.19167 -1.15541,2.57469 -3.2054,5.71943 -4.55548,6.98834 -4.9287,4.63234 -267.11707,201.54691 -270.87758,203.44052 -5.35739,2.69772 -13.91787,2.74574 -19.00867,0.10672 -4.45609,-2.3101 -8.97619,-6.60503 -11.12083,-10.56691 l -1.6798,-3.10319 -0.19964,-204.86813 -0.19964,-204.8681 z M 332.8233,534.6697 c 1.15403,-0.0704 178.2386,-133.96091 178.2386,-134.76314 0,-0.81862 -177.12982,-134.37525 -178.2386,-134.39266 -0.32001,-0.005 -0.58185,60.56094 -0.58185,134.59104 0,74.03011 0.26184,134.58427 0.58185,134.56476 z"
id="path19" /><path
id="path6-2"
d="M 511.41243,400.15311 332.8232,534.65713 v -268.8125 l 178.58923,134.3085 m 76.63599,0 c 0,-7.038 -3.128,-13.97825 -9.38399,-18.67025 L 314.73946,182.95261 c -15.4445,-11.63225 -37.43825,-0.5865 -37.43825,18.67025 v 397.15827 c 0,19.25675 21.99375,30.3025 37.43825,18.67025 L 578.66443,418.72563 c 6.256,-4.59425 9.384,-11.5345 9.38399,-18.5725 z"
class="st0"
style="fill:#ed6b21;stroke-width:0.9775" /><path
id="path19-3"
d="m 280.30386,191.19242 c 2.37409,-4.75097 5.55479,-7.89928 10.82054,-10.7103 3.95194,-2.10968 14.74308,-2.06245 19.00695,0.0833 3.70763,1.86576 267.63214,200.29982 271.15865,203.87315 3.86132,3.91253 5.61571,8.05138 6.017,14.19473 0.31244,4.78285 0.0826,6.12851 -1.74087,10.19167 -1.15541,2.57469 -3.2054,5.71943 -4.55548,6.98834 -4.9287,4.63234 -267.11707,201.54691 -270.87758,203.44052 -5.35739,2.69772 -13.91787,2.74574 -19.00867,0.10672 -4.45609,-2.3101 -8.97619,-6.60503 -11.12083,-10.56691 l -1.6798,-3.10319 -0.19964,-204.86813 -0.19964,-204.8681 z m 52.51944,343.47727 c 1.15403,-0.0704 178.2386,-133.96091 178.2386,-134.76314 0,-0.81862 -177.12982,-134.37525 -178.2386,-134.39266 -0.32001,-0.005 -0.58185,60.56094 -0.58185,134.59104 0,74.03011 0.26184,134.58427 0.58185,134.56476 z"
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.77579" /></svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,61 @@
<?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"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="16px"
height="16px"
viewBox="0 0 16 16"
style="enable-background:new 0 0 16 16;"
xml:space="preserve"
sodipodi:docname="open_browser.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
id="metadata13"><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="defs11" /><sodipodi:namedview
inkscape:document-rotation="0"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1369"
id="namedview9"
showgrid="false"
inkscape:zoom="63"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#808080;}
.st1{fill:#ED6B21;}
</style>
<path
style="fill:#808080"
id="path4"
d="m 2.9520337,6.2200003 c 0.29,0 0.5200005,-0.2299995 0.5200005,-0.5199995 v -2.0900002 -1.04 c 0,-0.29 0.2299995,-0.5199995 0.5200005,-0.5199995 h 8.8199993 c 0.29,0 0.53,0.5149999 0.53,0.8049998 l 0.05,7.5833331 c 0,0.139999 -0.05,0.269999 -0.150001,0.369999 -0.1,0.1 -0.229999,0.15 -0.369998,0.15 H 9.9520337 v 0 c -0.290001,0 -0.5200005,0.230001 -0.5200005,0.520001 0,0.289999 0.2299995,0.529999 0.5200005,0.529999 h 2.8200003 c 0.01,0 0.01,0 0.02,0 h 0.08 c 0.42,0 0.809999,-0.16 1.109999,-0.46 0.29,-0.3 0.45,-0.7 0.45,-1.12 l -0.04,-7.5933334 C 14.382033,1.9749999 13.682034,1 12.812034,1 H 4.0020329 C 3.1420332,1 2.4320332,1.6999998 2.4320332,2.5699997 v 1.04 2.0900002 c 0,0.2899999 0.2299995,0.5200004 0.5200005,0.5200004 z"
class="st0" /><path
style="fill:#ed6b21"
id="path6"
d="m 7.4320332,8.4527254 c 5e-7,-0.2545586 -0.2050605,-0.4596195 -0.452548,-0.452548 H 2.9206921 c -0.2545586,0 -0.3111269,0.1414213 -0.1343503,0.3181982 L 3.8045753,9.336609 c 0.1767766,0.176777 0.1767766,0.4596189 0,0.6363959 L 1.1317118,12.645868 c -0.17677665,0.176777 -0.17677685,0.45962 0.00707,0.643468 l 1.0040927,1.004092 c 0.1838475,0.183847 0.4666904,0.183848 0.6434673,0.0071 l 2.6657925,-2.665793 c 0.1767769,-0.176776 0.459619,-0.176776 0.6363959,0 l 1.0111628,1.011163 c 0.1838479,0.169677 0.3252692,0.113108 0.3323402,-0.13438 z"
class="st1" /></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="165mm" height="165mm" version="1.1" viewBox="0 0 165 165" xmlns="http://www.w3.org/2000/svg">
<rect x=".25" y=".25" width="164.5" height="164.5" fill="none" stroke="#fff" stroke-width=".5"/>
</svg>

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

View File

@ -113,6 +113,9 @@ int CLI::run(int argc, char **argv)
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
bool start_downloader = false;
bool delete_after_load = false;
std::string download_url;
bool start_as_gcodeviewer =
#ifdef _WIN32
false;
@ -221,6 +224,11 @@ int CLI::run(int argc, char **argv)
}
if (!start_as_gcodeviewer) {
for (const std::string& file : m_input_files) {
if (boost::starts_with(file, "prusaslicer://")) {
start_downloader = true;
download_url = file;
continue;
}
if (!boost::filesystem::exists(file)) {
boost::nowide::cerr << "No such file: " << file << std::endl;
exit(1);
@ -478,6 +486,9 @@ int CLI::run(int argc, char **argv)
// Models are repaired by default.
//for (auto &model : m_models)
// model.repair();
} else if (opt_key == "delete-after-load") {
delete_after_load = true;
} else {
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1;
@ -663,9 +674,12 @@ int CLI::run(int argc, char **argv)
params.extra_config = std::move(m_extra_config);
params.input_files = std::move(m_input_files);
params.start_as_gcodeviewer = start_as_gcodeviewer;
params.start_downloader = start_downloader;
params.download_url = download_url;
params.delete_after_load = delete_after_load;
#if ENABLE_GL_CORE_PROFILE
params.opengl_version = opengl_version;
#if ENABLE_OPENGL_DEBUG_OPTION
params.opengl_version = opengl_version;
params.opengl_debug = opengl_debug;
#endif // ENABLE_OPENGL_DEBUG_OPTION
#endif // ENABLE_GL_CORE_PROFILE

View File

@ -242,13 +242,14 @@ int wmain(int argc, wchar_t **argv)
#ifdef SLIC3R_GUI
// Here one may push some additional parameters based on the wrapper type.
bool force_mesa = false;
bool force_hw = false;
#endif /* SLIC3R_GUI */
for (int i = 1; i < argc; ++ i) {
#ifdef SLIC3R_GUI
if (wcscmp(argv[i], L"--sw-renderer") == 0)
force_mesa = true;
else if (wcscmp(argv[i], L"--no-sw-renderer") == 0)
force_mesa = false;
force_hw = true;
#endif /* SLIC3R_GUI */
argv_extended.emplace_back(argv[i]);
}
@ -261,7 +262,7 @@ int wmain(int argc, wchar_t **argv)
force_mesa ||
// Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context.
// In that case, use Mesa.
::GetSystemMetrics(SM_REMOTESESSION) ||
(::GetSystemMetrics(SM_REMOTESESSION) && !force_hw) ||
// Try to load the default OpenGL driver and test its context version.
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
#endif /* SLIC3R_GUI */

View File

@ -210,8 +210,9 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file)
bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file)
{
Slic3r::CNumericLocalesSetter locales_setter;
FILE* fp = fopen(file, "w");
FILE* fp = boost::nowide::fopen(file, "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
return false;
}

View File

@ -17,6 +17,12 @@ imstb_truetype.h modification:
Hot fix for open symbolic fonts on windows
62bdfe6f8d04b88e8bd511cd613be80c0baa7f55
Add case STBTT_MS_EID_SYMBOL to swith in file imstb_truetype.h on line 1440.
Hot fix for open curved fonts mainly on MAC
2148e49f75d82cb19dc6ec409fb7825296ed005c
viz. https://github.com/nothings/stb/issues/1296
In file imstb_truetype.h line 1667 change malloc size from:
vertices = (stbtt_vertex *) STBTT_malloc((m + 1) * sizeof(vertices[0]), info->userdata);
to:
vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);

View File

@ -158,6 +158,12 @@ namespace ImGui
const wchar_t SliderFloatEditBtnIcon = 0x2604;
const wchar_t SliderFloatEditBtnPressedIcon = 0x2605;
const wchar_t ClipboardBtnIcon = 0x2606;
const wchar_t PlayButton = 0x2618;
const wchar_t PlayHoverButton = 0x2619;
const wchar_t PauseButton = 0x261A;
const wchar_t PauseHoverButton = 0x261B;
const wchar_t OpenButton = 0x261C;
const wchar_t OpenHoverButton = 0x261D;
const wchar_t LegendTravel = 0x2701;
const wchar_t LegendWipe = 0x2702;
@ -173,8 +179,8 @@ namespace ImGui
const wchar_t LegendToolMarker = 0x2712;
const wchar_t WarningMarkerSmall = 0x2713;
const wchar_t ExpandBtn = 0x2714;
const wchar_t CollapseBtn = 0x2715;
const wchar_t InfoMarkerSmall = 0x2716;
const wchar_t CollapseBtn = 0x2715;
// void MyFunction(const char* name, const MyMatrix44& v);
}

View File

@ -1,85 +1,197 @@
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
#include "Point.hpp"
#include "Utils.hpp"
#include "libslic3r.h"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/Line.hpp"
#include <algorithm>
#include <cmath>
#include <type_traits>
#include <vector>
namespace Slic3r {
namespace AABBTreeLines {
namespace Slic3r { namespace AABBTreeLines {
namespace detail {
template<typename ALineType, typename ATreeType, typename AVectorType>
struct IndexedLinesDistancer {
using LineType = ALineType;
using TreeType = ATreeType;
template<typename ALineType, typename ATreeType, typename AVectorType> struct IndexedLinesDistancer
{
using LineType = ALineType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<LineType> &lines;
const TreeType &tree;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType &squared_distance) const {
inline VectorType closest_point_to_origin(size_t primitive_index, ScalarType &squared_distance) const
{
Vec<LineType::Dim, typename LineType::Scalar> nearest_point;
const LineType &line = lines[primitive_index];
const LineType &line = lines[primitive_index];
squared_distance = line_alg::distance_to_squared(line, origin.template cast<typename LineType::Scalar>(), &nearest_point);
return nearest_point.template cast<ScalarType>();
}
};
// returns number of intersections of ray starting in ray_origin and following the specified coordinate line with lines in tree
// first number is hits in positive direction of ray, second number hits in negative direction. returns neagtive numbers when ray_origin is
// on some line exactly.
template<typename LineType, typename TreeType, typename VectorType, int coordinate>
inline std::tuple<int, int> coordinate_aligned_ray_hit_count(size_t node_idx,
const TreeType &tree,
const std::vector<LineType> &lines,
const VectorType &ray_origin)
{
static constexpr int other_coordinate = (coordinate + 1) % 2;
using Scalar = typename LineType::Scalar;
using Floating = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, double>::type;
const auto &node = tree.node(node_idx);
assert(node.is_valid());
if (node.is_leaf()) {
const LineType &line = lines[node.idx];
if (ray_origin[other_coordinate] < std::min(line.a[other_coordinate], line.b[other_coordinate]) ||
ray_origin[other_coordinate] >= std::max(line.a[other_coordinate], line.b[other_coordinate])) {
// the second inequality is nonsharp for a reason
// without it, we may count contour border twice when the lines meet exactly at the spot of intersection. this prevents is
return {0, 0};
}
Scalar line_max = std::max(line.a[coordinate], line.b[coordinate]);
Scalar line_min = std::min(line.a[coordinate], line.b[coordinate]);
if (ray_origin[coordinate] > line_max) {
return {1, 0};
} else if (ray_origin[coordinate] < line_min) {
return {0, 1};
} else {
// find intersection of ray with line
// that is when ( line.a + t * (line.b - line.a) )[other_coordinate] == ray_origin[other_coordinate]
// t = ray_origin[oc] - line.a[oc] / (line.b[oc] - line.a[oc]);
// then we want to get value of intersection[ coordinate]
// val_c = line.a[c] + t * (line.b[c] - line.a[c]);
// Note that ray and line may overlap, when (line.b[oc] - line.a[oc]) is zero
// In that case, we return negative number
Floating distance_oc = line.b[other_coordinate] - line.a[other_coordinate];
Floating t = (ray_origin[other_coordinate] - line.a[other_coordinate]) / distance_oc;
Floating val_c = line.a[coordinate] + t * (line.b[coordinate] - line.a[coordinate]);
if (ray_origin[coordinate] > val_c) {
return {1, 0};
} else if (ray_origin[coordinate] < val_c) {
return {0, 1};
} else { // ray origin is on boundary
return {-1, -1};
}
}
} else {
int intersections_above = 0;
int intersections_below = 0;
size_t left_node_idx = node_idx * 2 + 1;
size_t right_node_idx = left_node_idx + 1;
const auto &node_left = tree.node(left_node_idx);
const auto &node_right = tree.node(right_node_idx);
assert(node_left.is_valid());
assert(node_right.is_valid());
if (node_left.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] &&
node_left.bbox.max()[other_coordinate] >=
ray_origin[other_coordinate]) {
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(left_node_idx, tree, lines,
ray_origin);
if (above < 0 || below < 0) return {-1, -1};
intersections_above += above;
intersections_below += below;
}
if (node_right.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] &&
node_right.bbox.max()[other_coordinate] >= ray_origin[other_coordinate]) {
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(right_node_idx, tree, lines,
ray_origin);
if (above < 0 || below < 0) return {-1, -1};
intersections_above += above;
intersections_below += below;
}
return {intersections_above, intersections_below};
}
}
template<typename LineType, typename TreeType, typename VectorType>
inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(size_t node_idx,
const TreeType &tree,
const std::vector<LineType> &lines,
const LineType &line,
const typename TreeType::BoundingBox &line_bb)
{
const auto &node = tree.node(node_idx);
assert(node.is_valid());
if (node.is_leaf()) {
VectorType intersection_pt;
if (line_alg::intersection(line, lines[node.idx], &intersection_pt)) {
return {std::pair<VectorType, size_t>(intersection_pt, node.idx)};
} else {
return {};
}
} else {
size_t left_node_idx = node_idx * 2 + 1;
size_t right_node_idx = left_node_idx + 1;
const auto &node_left = tree.node(left_node_idx);
const auto &node_right = tree.node(right_node_idx);
assert(node_left.is_valid());
assert(node_right.is_valid());
std::vector<std::pair<VectorType, size_t>> result;
if (node_left.bbox.intersects(line_bb)) {
std::vector<std::pair<VectorType, size_t>> intersections =
get_intersections_with_line<LineType, TreeType, VectorType>(left_node_idx, tree, lines, line, line_bb);
result.insert(result.end(), intersections.begin(), intersections.end());
}
if (node_right.bbox.intersects(line_bb)) {
std::vector<std::pair<VectorType, size_t>> intersections =
get_intersections_with_line<LineType, TreeType, VectorType>(right_node_idx, tree, lines, line, line_bb);
result.insert(result.end(), intersections.begin(), intersections.end());
}
return result;
}
}
} // namespace detail
// Build a balanced AABB Tree over a vector of lines, balancing the tree
// on centroids of the lines.
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
const std::vector<LineType> &lines,
//FIXME do we want to apply an epsilon?
const double eps = 0)
{
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;
struct InputType {
size_t idx() const {
return m_idx;
}
const BoundingBox& bbox() const {
return m_bbox;
}
const VectorType& centroid() const {
return m_centroid;
}
struct InputType
{
size_t idx() const { return m_idx; }
const BoundingBox &bbox() const { return m_bbox; }
const VectorType &centroid() const { return m_centroid; }
size_t m_idx;
size_t m_idx;
BoundingBox m_bbox;
VectorType m_centroid;
VectorType m_centroid;
};
std::vector<InputType> input;
input.reserve(lines.size());
const VectorType veps(eps, eps);
for (size_t i = 0; i < lines.size(); ++i) {
const LineType &line = lines[i];
InputType n;
n.m_idx = i;
InputType n;
n.m_idx = i;
n.m_centroid = (line.a + line.b) * 0.5;
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox.extend(line.b);
n.m_bbox.min() -= veps;
n.m_bbox.max() += veps;
input.emplace_back(n);
}
@ -103,8 +215,8 @@ inline typename VectorType::Scalar squared_distance_to_indexed_lines(
using Scalar = typename VectorType::Scalar;
if (tree.empty()) return Scalar(-1);
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(
distancer, size_t(0), Scalar(0), max_sqr_dist, hit_idx_out, hit_point_out);
return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_sqr_dist,
hit_idx_out, hit_point_out);
}
// Returns all lines within the given radius limit
@ -112,7 +224,7 @@ template<typename LineType, typename TreeType, typename VectorType>
inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType> &lines,
const TreeType &tree,
const VectorType &point,
typename VectorType::Scalar max_distance_squared)
typename VectorType::Scalar max_distance_squared)
{
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
@ -123,12 +235,71 @@ inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType> &line
return found_lines;
}
// return 1 if true, -1 if false, 0 for point on contour (or if cannot be determined)
template<typename LineType, typename TreeType, typename VectorType>
inline int point_outside_closed_contours(const std::vector<LineType> &lines, const TreeType &tree, const VectorType &point)
{
if (tree.empty()) { return 1; }
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 0>(0, tree, lines, point);
if (hits_above < 0 || hits_below < 0) {
return 0;
} else if (hits_above % 2 == 1 && hits_below % 2 == 1) {
return -1;
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
return 1;
} else { // this should not happen with closed contours. lets check it in Y direction
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 1>(0, tree, lines, point);
if (hits_above < 0 || hits_below < 0) {
return 0;
} else if (hits_above % 2 == 1 && hits_below % 2 == 1) {
return -1;
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
return 1;
} else { // both results were unclear
return 0;
}
}
}
template<bool sorted, typename VectorType, typename LineType, typename TreeType>
inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(const std::vector<LineType> &lines,
const TreeType &tree,
const LineType &line)
{
if (tree.empty()) {
return {};
}
auto line_bb = typename TreeType::BoundingBox(line.a, line.a);
line_bb.extend(line.b);
auto intersections = detail::get_intersections_with_line<LineType, TreeType, VectorType>(0, tree, lines, line, line_bb);
if (sorted) {
using Floating =
typename std::conditional<std::is_floating_point<typename LineType::Scalar>::value, typename LineType::Scalar, double>::type;
std::vector<std::pair<Floating, std::pair<VectorType, size_t>>> points_with_sq_distance{};
for (const auto &p : intersections) {
points_with_sq_distance.emplace_back((p.first - line.a).template cast<Floating>().squaredNorm(), p);
}
std::sort(points_with_sq_distance.begin(), points_with_sq_distance.end(),
[](const std::pair<Floating, std::pair<VectorType, size_t>> &left,
std::pair<Floating, std::pair<VectorType, size_t>> &right) { return left.first < right.first; });
for (size_t i = 0; i < points_with_sq_distance.size(); i++) {
intersections[i] = points_with_sq_distance[i].second;
}
}
return intersections;
}
template<typename LineType> class LinesDistancer
{
private:
std::vector<LineType> lines;
public:
using Scalar = typename LineType::Scalar;
using Floating = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, double>::type;
private:
std::vector<LineType> lines;
AABBTreeIndirect::Tree<2, Scalar> tree;
public:
@ -137,41 +308,40 @@ public:
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines);
}
explicit LinesDistancer(std::vector<LineType> &&lines) : lines(lines)
{
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines);
}
LinesDistancer() = default;
// 1 true, -1 false, 0 cannot determine
int outside(const Vec<2, Scalar> &point) const { return point_outside_closed_contours(lines, tree, point); }
// negative sign means inside
std::tuple<Floating, size_t, Vec<2, Floating>> signed_distance_from_lines_extra(const Vec<2, Scalar> &point) const
template<bool SIGNED_DISTANCE>
std::tuple<Floating, size_t, Vec<2, Floating>> distance_from_lines_extra(const Vec<2, Scalar> &point) const
{
size_t nearest_line_index_out = size_t(-1);
Vec<2, Floating> nearest_point_out = Vec<2, Floating>::Zero();
Vec<2, Floating> p = point.template cast<Floating>();
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, nearest_point_out);
if (distance < 0) { return {std::numeric_limits<Floating>::infinity(), nearest_line_index_out, nearest_point_out}; }
distance = sqrt(distance);
const LineType &line = lines[nearest_line_index_out];
Vec<2, Floating> v1 = (line.b - line.a).template cast<Floating>();
Vec<2, Floating> v2 = (point - line.a).template cast<Floating>();
auto d1 = (v1.x() * v2.y()) - (v1.y() * v2.x());
LineType second_line = line;
if ((line.a.template cast<Floating>() - nearest_point_out).squaredNorm() < SCALED_EPSILON) {
second_line = lines[prev_idx_modulo(nearest_line_index_out, lines.size())];
} else {
second_line = lines[next_idx_modulo(nearest_line_index_out, lines.size())];
if (distance < 0) {
return {std::numeric_limits<Floating>::infinity(), nearest_line_index_out, nearest_point_out};
}
v1 = (second_line.b - second_line.a).template cast<Floating>();
v2 = (point - second_line.a).template cast<Floating>();
auto d2 = (v1.x() * v2.y()) - (v1.y() * v2.x());
auto d = abs(d1) > abs(d2) ? d1 : d2;
if (d > 0.0) { distance *= -1.0; }
distance = sqrt(distance);
if (SIGNED_DISTANCE) {
distance *= outside(point);
}
return {distance, nearest_line_index_out, nearest_point_out};
}
Floating signed_distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const
{
auto [dist, idx, np] = signed_distance_from_lines_extra(point);
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
return dist;
}
@ -180,6 +350,11 @@ public:
return all_lines_in_radius(this->lines, this->tree, point, radius * radius);
}
template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const
{
return get_intersections_with_line<sorted, Vec<2, Scalar>>(lines, tree, line);
}
const LineType &get_line(size_t line_idx) const { return lines[line_idx]; }
const std::vector<LineType> &get_lines() const { return lines; }

View File

@ -573,6 +573,59 @@ inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalT
}
}
bool detect_voronoi_edge_intersecting_input_segment(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments)
{
for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
if (!cell.incident_edge())
continue; // Degenerated cell, there is no spoon
if (!cell.contains_segment())
continue; // Skip cells that don't contain segments.
const VoronoiUtils::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
const Vec2d source_segment_from = source_segment.from().cast<double>();
const Vec2d source_segment_vec = source_segment.to().cast<double>() - source_segment_from;
Point start_source_point, end_source_point;
VoronoiUtils::vd_t::edge_type *begin_voronoi_edge = nullptr, *end_voronoi_edge = nullptr;
SkeletalTrapezoidation::computeSegmentCellRange(cell, start_source_point, end_source_point, begin_voronoi_edge, end_voronoi_edge, segments);
// All Voronoi vertices must be on left side of the source segment, otherwise Voronoi diagram is invalid.
// FIXME Lukas H.: Be aware that begin_voronoi_edge and end_voronoi_edge could be nullptr in some specific cases.
// It mostly happens when there is some missing Voronoi, for example, in GH issue #8846 (IssuesWithMysteriousPerimeters.3mf).
if (begin_voronoi_edge != nullptr && end_voronoi_edge != nullptr)
for (VoronoiUtils::vd_t::edge_type *edge = begin_voronoi_edge; edge != end_voronoi_edge; edge = edge->next())
if (const Vec2d edge_v1(edge->vertex1()->x(), edge->vertex1()->y()); Slic3r::cross2(source_segment_vec, edge_v1 - source_segment_from) < 0)
return true;
}
return false;
}
enum class VoronoiDiagramStatus {
NO_ISSUE_DETECTED,
MISSING_VORONOI_VERTEX,
NON_PLANAR_VORONOI_DIAGRAM,
VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT,
OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION
};
// Try to detect cases when some Voronoi vertex is missing, when the Voronoi diagram
// is not planar or some Voronoi edge is intersecting input segment.
VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::VoronoiDiagram &voronoi_diagram,
const std::vector<SkeletalTrapezoidation::Segment> &segments)
{
if (const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments); has_missing_voronoi_vertex) {
return VoronoiDiagramStatus::MISSING_VORONOI_VERTEX;
} else if (const bool has_voronoi_edge_intersecting_input_segment = detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments); has_voronoi_edge_intersecting_input_segment) {
// Detection if Voronoi edge is intersecting input segment detects at least one model in GH issue #8446.
return VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT;
} else if (const bool is_voronoi_diagram_planar = Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments); !is_voronoi_diagram_planar) {
// Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
return VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM;
}
return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
}
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
{
#ifdef ARACHNE_DEBUG
@ -614,36 +667,35 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
}
#endif
// Try to detect cases when some Voronoi vertex is missing and when
// the Voronoi diagram is not planar.
// When any Voronoi vertex is missing, or the Voronoi diagram is not
// planar, rotate the input polygon and try again.
const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments);
// Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
const bool is_voronoi_diagram_planar = Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram);
const double fix_angle = PI / 6;
// When any Voronoi vertex is missing, the Voronoi diagram is not planar, or some voronoi edge is
// intersecting input segment, rotate the input polygon and try again.
VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments);
const double fix_angle = PI / 6;
std::unordered_map<Point, Point, PointHash> vertex_mapping;
// polys_copy is referenced through items stored in the std::vector segments.
Polygons polys_copy = polys;
if (has_missing_voronoi_vertex || !is_voronoi_diagram_planar) {
if (has_missing_voronoi_vertex)
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
if (status == VoronoiDiagramStatus::MISSING_VORONOI_VERTEX)
BOOST_LOG_TRIVIAL(warning) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth.";
else if (!is_voronoi_diagram_planar)
else if (status == VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM)
BOOST_LOG_TRIVIAL(warning) << "Detected non-planar Voronoi diagram, input polygons will be rotated back and forth.";
else if (status == VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT)
BOOST_LOG_TRIVIAL(warning) << "Detected Voronoi edge intersecting input segment, input polygons will be rotated back and forth.";
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle);
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram));
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments));
assert(!detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments));
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram))
else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments))
BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input.";
else if (detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments))
BOOST_LOG_TRIVIAL(error) << "Detected Voronoi edge intersecting input segment even after the rotation of input.";
}
bool degenerated_voronoi_diagram = has_missing_voronoi_vertex || !is_voronoi_diagram_planar;
process_voronoi_diagram:
assert(this->graph.edges.empty() && this->graph.nodes.empty() && this->vd_edge_to_he_edge.empty() && this->vd_node_to_he_node.empty());
for (vd_t::cell_type cell : voronoi_diagram.cells()) {
@ -652,35 +704,35 @@ process_voronoi_diagram:
Point start_source_point;
Point end_source_point;
vd_t::edge_type* starting_vonoroi_edge = nullptr;
vd_t::edge_type* ending_vonoroi_edge = nullptr;
vd_t::edge_type* starting_voronoi_edge = nullptr;
vd_t::edge_type* ending_voronoi_edge = nullptr;
// Compute and store result in above variables
if (cell.contains_point()) {
const bool keep_going = computePointCellRange(cell, start_source_point, end_source_point, starting_vonoroi_edge, ending_vonoroi_edge, segments);
const bool keep_going = computePointCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
if (!keep_going)
continue;
} else {
assert(cell.contains_segment());
computeSegmentCellRange(cell, start_source_point, end_source_point, starting_vonoroi_edge, ending_vonoroi_edge, segments);
computeSegmentCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
}
if (!starting_vonoroi_edge || !ending_vonoroi_edge) {
if (!starting_voronoi_edge || !ending_voronoi_edge) {
assert(false && "Each cell should start / end in a polygon vertex");
continue;
}
// Copy start to end edge to graph
edge_t* prev_edge = nullptr;
assert(VoronoiUtils::p(starting_vonoroi_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_vonoroi_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
assert(VoronoiUtils::p(starting_vonoroi_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_vonoroi_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
transferEdge(start_source_point, VoronoiUtils::p(starting_vonoroi_edge->vertex1()).cast<coord_t>(), *starting_vonoroi_edge, prev_edge, start_source_point, end_source_point, segments);
node_t* starting_node = vd_node_to_he_node[starting_vonoroi_edge->vertex0()];
assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
transferEdge(start_source_point, VoronoiUtils::p(starting_voronoi_edge->vertex1()).cast<coord_t>(), *starting_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
node_t* starting_node = vd_node_to_he_node[starting_voronoi_edge->vertex0()];
starting_node->data.distance_to_boundary = 0;
constexpr bool is_next_to_start_or_end = true;
graph.makeRib(prev_edge, start_source_point, end_source_point, is_next_to_start_or_end);
for (vd_t::edge_type* vd_edge = starting_vonoroi_edge->next(); vd_edge != ending_vonoroi_edge; vd_edge = vd_edge->next()) {
for (vd_t::edge_type* vd_edge = starting_voronoi_edge->next(); vd_edge != ending_voronoi_edge; vd_edge = vd_edge->next()) {
assert(vd_edge->is_finite());
assert(VoronoiUtils::p(vd_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
@ -692,12 +744,12 @@ process_voronoi_diagram:
Point v2 = VoronoiUtils::p(vd_edge->vertex1()).cast<coord_t>();
transferEdge(v1, v2, *vd_edge, prev_edge, start_source_point, end_source_point, segments);
graph.makeRib(prev_edge, start_source_point, end_source_point, vd_edge->next() == ending_vonoroi_edge);
graph.makeRib(prev_edge, start_source_point, end_source_point, vd_edge->next() == ending_voronoi_edge);
}
assert(VoronoiUtils::p(starting_vonoroi_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_vonoroi_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
assert(VoronoiUtils::p(starting_vonoroi_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_vonoroi_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
transferEdge(VoronoiUtils::p(ending_vonoroi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_vonoroi_edge, prev_edge, start_source_point, end_source_point, segments);
assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
transferEdge(VoronoiUtils::p(ending_voronoi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
prev_edge->to->data.distance_to_boundary = 0;
}
@ -705,9 +757,9 @@ process_voronoi_diagram:
// When this degenerated Voronoi diagram is processed, the resulting half-edge structure contains some edges that don't have
// a twin edge. Based on this, we created a fast mechanism that detects those causes and tries to recompute the Voronoi
// diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram.
if (!degenerated_voronoi_diagram && has_missing_twin_edge(this->graph)) {
if (status == VoronoiDiagramStatus::NO_ISSUE_DETECTED && has_missing_twin_edge(this->graph)) {
BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth.";
degenerated_voronoi_diagram = true;
status = VoronoiDiagramStatus::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION;
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle);
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
@ -724,14 +776,14 @@ process_voronoi_diagram:
goto process_voronoi_diagram;
}
if (degenerated_voronoi_diagram) {
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
assert(!has_missing_twin_edge(this->graph));
if (has_missing_twin_edge(this->graph))
BOOST_LOG_TRIVIAL(error) << "Detected degenerated Voronoi diagram even after the rotation of input.";
}
if (degenerated_voronoi_diagram)
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED)
rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping);
#ifdef ARACHNE_DEBUG
@ -742,7 +794,7 @@ process_voronoi_diagram:
graph.collapseSmallEdges();
// Set [incident_edge] the the first possible edge that way we can iterate over all reachable edges from node.incident_edge,
// Set [incident_edge] the first possible edge that way we can iterate over all reachable edges from node.incident_edge,
// without needing to iterate backward
for (edge_t& edge : graph.edges)
if (!edge.prev)

View File

@ -9,6 +9,7 @@
#include <memory> // smart pointers
#include <unordered_map>
#include <utility> // pair
#include <Arachne/utils/VoronoiUtils.hpp>
#include "utils/HalfEdgeGraph.hpp"
#include "utils/PolygonsSegmentIndex.hpp"
@ -229,7 +230,7 @@ protected:
* /return Whether the cell is inside of the polygon. If it's outside of the
* polygon we should skip processing it altogether.
*/
bool computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
static bool computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
/*!
* Compute the range of line segments that surround a cell of the skeletal
@ -255,7 +256,7 @@ protected:
* /return Whether the cell is inside of the polygon. If it's outside of the
* polygon we should skip processing it altogether.
*/
void computeSegmentCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
static void computeSegmentCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
/*!
* For VD cells associated with an input polygon vertex, we need to separate the node at the end and start of the cell into two
@ -597,6 +598,8 @@ protected:
* Genrate small segments for local maxima where the beading would only result in a single bead
*/
void generateLocalMaximaSingleBeads();
friend bool detect_voronoi_edge_intersecting_input_segment(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments);
};
} // namespace Slic3r::Arachne

View File

@ -14,7 +14,7 @@
#include "../../../clipper/clipper_z.hpp"
namespace Slic3r {
class ThickPolyline;
struct ThickPolyline;
}
namespace Slic3r::Arachne

View File

@ -425,24 +425,11 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
{
auto bincenter = m_bin.center();
return [this, bincenter](const Item &item) {
auto result = objfunc(item, bincenter);
double score = std::get<0>(result);
auto isBig = [this](const Item& itm) {
return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ;
};
if(isBig(item)) {
auto mp = m_merged_pile;
mp.push_back(item.transformedShape());
auto chull = sl::convexHull(mp);
double miss = Placer::overfit(chull, m_bin);
if(miss < 0) miss = 0;
score += miss*miss;
}
return score;
};
}

View File

@ -174,7 +174,6 @@ public:
BoundingBox rotated(double angle, const Point &center) const;
void rotate(double angle) { (*this) = this->rotated(angle); }
void rotate(double angle, const Point &center) { (*this) = this->rotated(angle, center); }
bool intersects(const BoundingBox &other) const { return this->min(0) <= other.max(0) && this->max(0) >= other.min(0) && this->min(1) <= other.max(1) && this->max(1) >= other.min(1); }
// Align the min corner to a grid of cell_size x cell_size cells,
// to encompass the original bounding box.
void align_to_grid(const coord_t cell_size);

View File

@ -193,7 +193,6 @@ set(SLIC3R_SOURCES
Model.hpp
ModelArrange.hpp
ModelArrange.cpp
#ModelVolumeType.hpp
MultiMaterialSegmentation.cpp
MultiMaterialSegmentation.hpp
MeshNormals.hpp

View File

@ -1677,6 +1677,7 @@ public:
case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
case coFloatsOrPercents:{ auto opt = new ConfigOptionFloatsOrPercents();archive(*opt); return opt; }
case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
@ -1708,6 +1709,7 @@ public:
case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
case coFloatsOrPercents:archive(*static_cast<const ConfigOptionFloatsOrPercents*>(opt));break;
case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;

View File

@ -78,6 +78,8 @@ using EI = CGAL::SM_Edge_index;
using FI = CGAL::SM_Face_index;
using P3 = CGAL::Epick::Point_3;
inline Vec3d to_vec3d(const P3 &p) { return Vec3d(p.x(),p.y(),p.z()); }
/// <summary>
/// Convert triangle mesh model to CGAL Surface_mesh
/// Filtrate out opposite triangles
@ -1573,8 +1575,52 @@ void priv::create_reduce_map(ReductionMap &reduction_map, const CutMesh &mesh)
assert(!is_reducible_vertex(left));
VI &vi = reduction_map[erase];
// check if it is first add
if (!vi.is_valid())
reduction_map[erase] = left;
if (vi.is_valid())
return;
// check that all triangles after reduction has 'erase' and 'left' vertex
// on same side of opposite line of vertex in triangle
Vec3d v_erase = to_vec3d(mesh.point(erase));
Vec3d v_left = to_vec3d(mesh.point(left));
for (FI fi : mesh.faces_around_target(hi)) {
if (!fi.is_valid())
continue;
// get vertices of rest
VI vi_a, vi_b;
for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) {
if (!vi.is_valid())
continue;
if (vi == erase)
continue;
if (!vi_a.is_valid())
vi_a = vi;
else {
assert(!vi_b.is_valid());
vi_b = vi;
}
}
assert(vi_b.is_valid());
// do not check triangle, which will be removed
if (vi_a == left || vi_b == left)
continue;
Vec3d v_a = to_vec3d(mesh.point(vi_a));
Vec3d v_b = to_vec3d(mesh.point(vi_b));
// Vectors of triangle edges
Vec3d v_ab = v_b - v_a;
Vec3d v_ae = v_erase - v_a;
Vec3d v_al = v_left - v_a;
Vec3d n1 = v_ab.cross(v_ae);
Vec3d n2 = v_ab.cross(v_al);
// check that normal has same direction
if ((n1.x() > 0 != n2.x() > 0) ||
(n1.y() > 0 != n2.y() > 0) ||
(n1.z() > 0 != n2.z() > 0))
return; // this reduction will create CCW triangle
}
reduction_map[erase] = left;
// I have no better rule than take the first
// for decide which reduction will be better
// But it could be use only one of them
@ -2521,7 +2567,7 @@ bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
BoundingBoxf3 priv::bounding_box(const CutAOI &cut, const CutMesh &mesh) {
const P3& p_from_cut = mesh.point(mesh.target(mesh.halfedge(cut.first.front())));
Vec3d min(p_from_cut.x(), p_from_cut.y(), p_from_cut.z());
Vec3d min = to_vec3d(p_from_cut);
Vec3d max = min;
for (FI fi : cut.first) {
for(VI vi: mesh.vertices_around_face(mesh.halfedge(fi))){
@ -2537,9 +2583,8 @@ BoundingBoxf3 priv::bounding_box(const CutAOI &cut, const CutMesh &mesh) {
BoundingBoxf3 priv::bounding_box(const CutMesh &mesh)
{
const P3 &p_from_cut = *mesh.points().begin();
Vec3d min(p_from_cut.x(), p_from_cut.y(), p_from_cut.z());
Vec3d max = min;
Vec3d min = to_vec3d(*mesh.points().begin());
Vec3d max = min;
for (VI vi : mesh.vertices()) {
const P3 &p = mesh.point(vi);
for (size_t i = 0; i < 3; ++i) {
@ -2806,7 +2851,7 @@ bool priv::is_patch_inside_of_model(const SurfacePatch &patch,
{
// TODO: Solve model with hole in projection direction !!!
const P3 &a = patch.mesh.point(VI(0));
Vec3d a_(a.x(), a.y(), a.z());
Vec3d a_ = to_vec3d(a);
Vec3d b_ = projection.project(a_);
P3 b(b_.x(), b_.y(), b_.z());
@ -3396,7 +3441,7 @@ Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &project
pts.reserve(l.size());
for (VI vi : l) {
const P3 &p3 = patch.mesh.point(vi);
Vec3d p(p3.x(), p3.y(), p3.z());
Vec3d p = to_vec3d(p3);
double depth;
std::optional<Vec2d> p2_opt = projection.unproject(p, &depth);
if (depth_range[0] > depth) depth_range[0] = depth; // min
@ -3426,9 +3471,21 @@ ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &projection,
// should not be used when no opposit triangle are counted so should not create overlaps
ClipperLib::PolyFillType fill_type = ClipperLib::PolyFillType::pftEvenOdd;
ExPolygons expolys = Slic3r::union_ex(polys, fill_type);
assert(expolys.size() == 1);
if (expolys.size() == 1)
return expolys.front();
// It should be one expolygon
assert(false);
if (expolys.empty()) return {};
return expolys.front();
// find biggest
const ExPolygon *biggest = &expolys.front();
for (size_t index = 1; index < expolys.size(); ++index) {
const ExPolygon *current = &expolys[index];
if (biggest->contour.size() < current->contour.size())
biggest = current;
}
return *biggest;
}
SurfaceCut priv::patch2cut(SurfacePatch &patch)
@ -3597,7 +3654,7 @@ void priv::store(const CutMesh &mesh, const FaceTypeMap &face_type_map, const st
default: color = CGAL::Color{0, 0, 255}; // blue
}
}
CGAL::IO::write_OFF(off_file, mesh);
CGAL::IO::write_OFF(off_file, mesh, CGAL::parameters::face_color_map(face_colors));
mesh_.remove_property_map(face_colors);
}
@ -3624,7 +3681,7 @@ void priv::store(const CutMesh &mesh, const ReductionMap &reduction_map, const s
vertex_colors[reduction_to] = CGAL::Color{0, 0, 255};
}
CGAL::IO::write_OFF(off_file, mesh);
CGAL::IO::write_OFF(off_file, mesh, CGAL::parameters::vertex_color_map(vertex_colors));
mesh_.remove_property_map(vertex_colors);
}

View File

@ -283,7 +283,7 @@ ExPolygons Emboss::heal_shape(const Polygons &shape) {
// Do not remove all duplicits but do it better way
// Overlap all duplicit points by rectangle 3x3
Points duplicits = collect_duplications(to_points(polygons));
Points duplicits = collect_duplicates(to_points(polygons));
if (!duplicits.empty()) {
polygons.reserve(polygons.size() + duplicits.size());
for (const Point &p : duplicits) {
@ -310,7 +310,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
priv::remove_same_neighbor(shape);
Pointfs intersections = intersection_points(shape);
Points duplicits = collect_duplications(to_points(shape));
Points duplicits = collect_duplicates(to_points(shape));
//Points close = priv::collect_close_points(shape, 1.);
if (intersections.empty() && duplicits.empty() /* && close.empty() */) break;
@ -353,7 +353,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
svg.draw(shape, "green");
svg.draw(duplicits, "lightgray", 13 / Emboss::SHAPE_SCALE);
Points duplicits3 = collect_duplications(to_points(shape));
Points duplicits3 = collect_duplicates(to_points(shape));
svg.draw(duplicits3, "black", 7 / Emboss::SHAPE_SCALE);
Pointfs pts2 = intersection_points(shape);
@ -387,7 +387,7 @@ bool Emboss::heal_shape(ExPolygons &shape, unsigned max_iteration)
}
assert(intersection_points(shape).empty());
assert(collect_duplications(to_points(shape)).empty());
assert(collect_duplicates(to_points(shape)).empty());
return true;
}
@ -1186,7 +1186,7 @@ indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
const IProjection &projection)
{
Points points = to_points(shape2d);
Points duplicits = collect_duplications(points);
Points duplicits = collect_duplicates(points);
return (duplicits.empty()) ?
priv::polygons2model_unique(shape2d, projection, points) :
priv::polygons2model_duplicit(shape2d, projection, points, duplicits);

View File

@ -320,7 +320,9 @@ void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polyl
{
ThickPolylines tp;
this->medial_axis(min_width, max_width, &tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
polylines->reserve(polylines->size() + tp.size());
for (auto &pl : tp)
polylines->emplace_back(pl.points);
}
Lines ExPolygon::lines() const

View File

@ -148,7 +148,7 @@ static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
// Store / load of TextConfiguration
static constexpr const char *TEXT_TAG = "emboss";
static constexpr const char *TEXT_TAG = "slic3rpe:text";
static constexpr const char *TEXT_DATA_ATTR = "text";
// TextConfiguration::EmbossStyle
static constexpr const char *STYLE_NAME_ATTR = "style_name";
@ -3628,8 +3628,8 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
float distance = get_attribute_value_float(attributes, num_attributes, DISTANCE_ATTR);
if (std::fabs(distance) > std::numeric_limits<float>::epsilon())
fp.distance = distance;
std::string use_surface = get_attribute_value_string(attributes, num_attributes, USE_SURFACE_ATTR);
if (!use_surface.empty()) fp.use_surface = true;
int use_surface = get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
if (use_surface == 1) fp.use_surface = true;
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
fp.angle = angle;

View File

@ -1,4 +1,5 @@
#include "libslic3r.h"
#include "GCode/ExtrusionProcessor.hpp"
#include "I18N.hpp"
#include "GCode.hpp"
#include "Exception.hpp"
@ -2160,14 +2161,18 @@ LayerResult GCode::process_layer(
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
if (this->config().avoid_curled_filament_during_travels) {
m_avoid_curled_filaments.clear();
if (this->config().avoid_crossing_curled_overhangs) {
m_avoid_crossing_curled_overhangs.clear();
for (const ObjectLayerToPrint &layer_to_print : layers) {
m_avoid_curled_filaments.add_obstacles(layer_to_print.object_layer, Point(scaled(this->origin())));
m_avoid_curled_filaments.add_obstacles(layer_to_print.support_layer, Point(scaled(this->origin())));
m_avoid_crossing_curled_overhangs.add_obstacles(layer_to_print.object_layer, Point(scaled(this->origin())));
m_avoid_crossing_curled_overhangs.add_obstacles(layer_to_print.support_layer, Point(scaled(this->origin())));
}
}
for (const ObjectLayerToPrint &layer_to_print : layers) {
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.object_layer);
}
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
for (unsigned int extruder_id : layer_tools.extruders)
{
@ -2295,6 +2300,8 @@ void GCode::process_layer_single_object(
const PrintObject &print_object = print_instance.print_object;
const Print &print = *print_object.print();
m_extrusion_quality_estimator.set_current_object(&print_object);
if (! print_wipe_extrusions && layer_to_print.support_layer != nullptr)
if (const SupportLayer &support_layer = *layer_to_print.support_layer; ! support_layer.support_fills.entities.empty()) {
ExtrusionRole role = support_layer.support_fills.role();
@ -2327,7 +2334,7 @@ void GCode::process_layer_single_object(
interface_extruder = dontcare_extruder;
}
bool extrude_support = has_support && support_extruder == extruder_id;
bool extrude_interface = interface_extruder && interface_extruder == extruder_id;
bool extrude_interface = has_interface && interface_extruder == extruder_id;
if (extrude_support || extrude_interface) {
init_layer_delayed();
m_layer = layer_to_print.support_layer;
@ -2845,12 +2852,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
acceleration = m_config.first_layer_acceleration.value;
} else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
acceleration = m_config.first_layer_acceleration_over_raft.value;
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
} else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) {
acceleration = m_config.bridge_acceleration.value;
} else if (m_config.infill_acceleration.value > 0 && is_infill(path.role())) {
acceleration = m_config.infill_acceleration.value;
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
} else {
acceleration = m_config.default_acceleration.value;
}
@ -2905,6 +2912,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm
);
}
bool variable_speed = false;
std::vector<ProcessedPoint> new_points{};
if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && is_perimeter(path.role())) {
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, m_config.overhang_overlap_levels,
m_config.dynamic_overhang_speeds,
m_config.get_abs_value("external_perimeter_speed"), speed);
variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; });
}
double F = speed * 60; // convert mm/sec to mm/min
// extrude arc or line
@ -2966,10 +2983,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
comment += ";_EXTERNAL_PERIMETER";
}
// F is mm per minute.
gcode += m_writer.set_speed(F, "", comment);
double path_length = 0.;
{
if (!variable_speed) {
// F is mm per minute.
gcode += m_writer.set_speed(F, "", comment);
double path_length = 0.;
std::string comment;
if (m_config.gcode_comments) {
comment = description;
@ -2985,7 +3002,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
prev = p;
}
} else {
std::string comment;
if (m_config.gcode_comments) {
comment = description;
comment += description_bridge;
}
double last_set_speed = new_points[0].speed * 60.0;
gcode += m_writer.set_speed(last_set_speed, "", comment);
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
for (size_t i = 1; i < new_points.size(); i++) {
const ProcessedPoint& processed_point = new_points[i];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
const double line_length = (p - prev).norm();
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
prev = p;
double new_speed = processed_point.speed * 60.0;
if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", comment);
last_set_speed = new_speed;
}
}
}
if (m_enable_cooling_markers)
gcode += is_bridge(path.role()) ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n";
@ -3001,10 +3040,15 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
this->origin in order to get G-code coordinates. */
Polyline travel { this->last_pos(), point };
if (this->config().avoid_curled_filament_during_travels) {
Point scaled_origin = Point(scaled(this->origin()));
travel = m_avoid_curled_filaments.find_path(this->last_pos() + scaled_origin, point + scaled_origin);
travel.translate(-scaled_origin);
if (this->config().avoid_crossing_curled_overhangs) {
if (m_config.avoid_crossing_perimeters) {
BOOST_LOG_TRIVIAL(warning)
<< "Option >avoid crossing curled overhangs< is not compatible with avoid crossing perimeters and it will be ignored!";
} else {
Point scaled_origin = Point(scaled(this->origin()));
travel = m_avoid_crossing_curled_overhangs.find_path(this->last_pos() + scaled_origin, point + scaled_origin);
travel.translate(-scaled_origin);
}
}
// check whether a straight travel move would need retraction

View File

@ -1,6 +1,7 @@
#ifndef slic3r_GCode_hpp_
#define slic3r_GCode_hpp_
#include "GCode/ExtrusionProcessor.hpp"
#include "JumpPointSearch.hpp"
#include "libslic3r.h"
#include "ExPolygon.hpp"
@ -333,6 +334,8 @@ private:
// Cache for custom seam enforcers/blockers for each layer.
SeamPlacer m_seam_placer;
ExtrusionQualityEstimator m_extrusion_quality_estimator;
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
methods. */
@ -349,7 +352,7 @@ private:
OozePrevention m_ooze_prevention;
Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
JPSPathFinder m_avoid_curled_filaments;
JPSPathFinder m_avoid_crossing_curled_overhangs;
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;
bool m_enable_loop_clipping;
// If enabled, the G-code generator will put following comments at the ends

View File

@ -0,0 +1,325 @@
#ifndef slic3r_ExtrusionProcessor_hpp_
#define slic3r_ExtrusionProcessor_hpp_
#include "../AABBTreeLines.hpp"
#include "../SupportSpotsGenerator.hpp"
#include "../libslic3r.h"
#include "../ExtrusionEntity.hpp"
#include "../Layer.hpp"
#include "../Point.hpp"
#include "../SVG.hpp"
#include "../BoundingBox.hpp"
#include "../Polygon.hpp"
#include "../ClipperUtils.hpp"
#include "../Flow.hpp"
#include "../Config.hpp"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <limits>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>
namespace Slic3r {
class SlidingWindowCurvatureAccumulator
{
float window_size;
float total_distance = 0; // accumulated distance
float total_curvature = 0; // accumulated signed ccw angles
deque<float> distances;
deque<float> angles;
public:
SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {}
void add_point(float distance, float angle)
{
total_distance += distance;
total_curvature += angle;
distances.push_back(distance);
angles.push_back(angle);
while (distances.size() > 1 && total_distance > window_size) {
total_distance -= distances.front();
total_curvature -= angles.front();
distances.pop_front();
angles.pop_front();
}
}
float get_curvature() const
{
return total_curvature / window_size;
}
void reset()
{
total_curvature = 0;
total_distance = 0;
distances.clear();
angles.clear();
}
};
class CurvatureEstimator
{
static const size_t sliders_count = 3;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.0},{4.0}, {10.0}};
public:
void add_point(float distance, float angle)
{
if (distance < EPSILON)
return;
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.add_point(distance, angle);
}
}
float get_curvature()
{
float max_curvature = 0.0f;
for (const SlidingWindowCurvatureAccumulator &slider : sliders) {
if (abs(slider.get_curvature()) > abs(max_curvature)) {
max_curvature = slider.get_curvature();
}
}
return max_curvature;
}
void reset()
{
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.reset();
}
}
};
struct ExtendedPoint
{
ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0)
: position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature)
{}
Vec2d position;
float distance;
size_t nearest_prev_layer_line;
float curvature;
};
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L>
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points,
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
float flow_width,
float max_line_length = -1.0f)
{
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
if (input_points.empty())
return {};
float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
CurvatureEstimator cestim;
auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
std::vector<P> extrusion_points;
{
if (max_line_length <= 0) {
extrusion_points = input_points;
} else {
extrusion_points.reserve(input_points.size() * 2);
for (size_t i = 0; i + 1 < input_points.size(); i++) {
const P &curr = input_points[i];
const P &next = input_points[i + 1];
extrusion_points.push_back(curr);
auto len = maybe_unscale(next - curr).squaredNorm();
double t = sqrt((max_line_length * max_line_length) / len);
size_t new_point_count = 1.0 / (t + EPSILON);
for (size_t j = 1; j < new_point_count + 1; j++) {
extrusion_points.push_back(curr * (1.0 - j * t) + next * (j * t));
}
}
extrusion_points.push_back(input_points.back());
}
}
std::vector<ExtendedPoint> points;
points.reserve(extrusion_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1));
{
ExtendedPoint start_point{maybe_unscale(extrusion_points.front())};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>());
start_point.distance = distance + boundary_offset;
start_point.nearest_prev_layer_line = nearest_line;
points.push_back(start_point);
}
for (size_t i = 1; i < extrusion_points.size(); i++) {
ExtendedPoint next_point{maybe_unscale(extrusion_points[i])};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
next_point.distance = distance + boundary_offset;
next_point.nearest_prev_layer_line = nearest_line;
if (ADD_INTERSECTIONS &&
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
const ExtendedPoint &prev_point = points.back();
auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()});
for (const auto &intersection : intersections) {
points.emplace_back(intersection.first.template cast<double>(), boundary_offset, intersection.second);
}
}
points.push_back(next_point);
}
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
std::vector<ExtendedPoint> new_points;
new_points.reserve(points.size() * 2);
new_points.push_back(points.front());
for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) {
const ExtendedPoint &curr = points[point_idx];
const ExtendedPoint &next = points[point_idx + 1];
if ((curr.distance > 0 && curr.distance < boundary_offset + 2.0f) ||
(next.distance > 0 && next.distance < boundary_offset + 2.0f)) {
double line_len = (next.position - curr.position).norm();
if (line_len > 4.0f) {
double a0 = std::clamp((curr.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
double a1 = std::clamp(1.0f - (next.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
double t0 = std::min(a0, a1);
double t1 = std::max(a0, a1);
if (t0 < 1.0) {
auto p0 = curr.position + t0 * (next.position - curr.position);
auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l});
}
if (t1 > 0.0) {
auto p1 = curr.position + t1 * (next.position - curr.position);
auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l});
}
}
}
new_points.push_back(next);
}
points = std::move(new_points);
}
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
ExtendedPoint &a = points[point_idx];
ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
int prev_point_idx = point_idx;
while (prev_point_idx > 0) {
prev_point_idx--;
if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; }
}
int next_point_index = point_idx;
while (next_point_index < int(points.size()) - 1) {
next_point_index++;
if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; }
}
if (prev_point_idx != point_idx && next_point_index != point_idx) {
float distance = (prev.position - a.position).norm();
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
cestim.add_point(distance, alfa);
}
a.curvature = cestim.get_curvature();
}
return points;
}
struct ProcessedPoint
{
Point p;
float speed = 1.0f;
};
class ExtrusionQualityEstimator
{
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
const PrintObject *current_object;
public:
void set_current_object(const PrintObject *object) { current_object = object; }
void prepare_for_new_layer(const Layer *layer)
{
if (layer == nullptr) return;
const PrintObject *object = layer->object();
prev_layer_boundaries[object] = next_layer_boundaries[object];
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
}
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path,
const ConfigOptionPercents &overlaps,
const ConfigOptionFloatsOrPercents &speeds,
float ext_perimeter_speed,
float original_speed)
{
size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
std::vector<std::pair<float, float>> speed_sections;
for (size_t i = 0; i < speed_sections_count; i++) {
float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
speed_sections.push_back({distance, speed});
}
std::sort(speed_sections.begin(), speed_sections.end(),
[](const std::pair<float, float> &a, const std::pair<float, float> &b) {
if (a.first == b.first) {
return a.second > b.second;
}
return a.first < b.first; });
std::pair<float, float> last_section{INFINITY, 0};
for (auto &section : speed_sections) {
if (section.first == last_section.first) {
section.second = last_section.second;
} else {
last_section = section;
}
}
std::vector<ExtendedPoint> extended_points =
estimate_points_properties<true, true, true, true>(path.polyline.points, prev_layer_boundaries[current_object], path.width);
std::vector<ProcessedPoint> processed_points;
processed_points.reserve(extended_points.size());
for (size_t i = 0; i < extended_points.size(); i++) {
const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
float final_speed;
if (distance <= speed_sections.front().first) {
final_speed = original_speed;
} else if (distance >= speed_sections.back().first) {
final_speed = speed_sections.back().second;
} else {
size_t section_idx = 0;
while (distance > speed_sections[section_idx + 1].first) {
section_idx++;
}
float t = (distance - speed_sections[section_idx].first) /
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
t = std::clamp(t, 0.0f, 1.0f);
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
}
return final_speed;
};
float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
processed_points.push_back({scaled(curr.position), extrusion_speed});
}
return processed_points;
}
};
} // namespace Slic3r
#endif // slic3r_ExtrusionProcessor_hpp_

View File

@ -367,7 +367,16 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
case 'T':
{
// Activate an extruder head.
int new_extruder = parse_int(line);
int new_extruder = -1;
try {
new_extruder = parse_int(line);
} catch (Slic3r::InvalidArgument &) {
// Ignore invalid GCodes starting with T.
eatws(line);
break;
}
assert(new_extruder != -1);
if (new_extruder != int(m_current_extruder)) {
m_current_extruder = new_extruder;
m_retracted = true;

View File

@ -1071,7 +1071,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
Vec2f point = Vec2f { perimeter_point.position.head<2>() };
if (prev_layer_distancer.get() != nullptr) {
perimeter_point.overhang = prev_layer_distancer->signed_distance_from_lines(point.cast<double>())
perimeter_point.overhang = prev_layer_distancer->distance_from_lines<true>(point.cast<double>())
+ 0.6f * perimeter_point.perimeter.flow_width
- tan(SeamPlacer::overhang_angle_threshold)
* po->layers()[layer_idx]->height;
@ -1080,7 +1080,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
}
if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
perimeter_point.embedded_distance = current_layer_distancer->signed_distance_from_lines(point.cast<double>())
perimeter_point.embedded_distance = current_layer_distancer->distance_from_lines<true>(point.cast<double>())
+ 0.6f * perimeter_point.perimeter.flow_width;
}
}

View File

@ -430,7 +430,26 @@ static bool contains_skew(const Transform3d& trafo)
Matrix3d rotation;
Matrix3d scale;
trafo.computeRotationScaling(&rotation, &scale);
return !scale.isDiagonal();
if (scale.isDiagonal())
return false;
if (scale.determinant() >= 0.0)
return true;
// the matrix contains mirror
const Matrix3d ratio = scale.cwiseQuotient(trafo.matrix().block<3,3>(0,0));
auto check_skew = [&ratio](int i, int j, bool& skew) {
if (!std::isnan(ratio(i, j)) && !std::isnan(ratio(j, i)))
skew |= std::abs(ratio(i, j) * ratio(j, i) - 1.0) > EPSILON;
};
bool has_skew = false;
check_skew(0, 1, has_skew);
check_skew(0, 2, has_skew);
check_skew(1, 2, has_skew);
return has_skew;
}
Vec3d Transformation::get_rotation() const

View File

@ -498,6 +498,7 @@ void MedialAxis::build(ThickPolylines* polylines)
polyline.width.emplace_back(seed_edge_data.width_end);
// Grow the polyline in a forward direction.
this->process_edge_neighbors(&*seed_edge, &polyline);
assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// Grow the polyline in a backward direction.
reverse_polyline.clear();
@ -505,7 +506,6 @@ void MedialAxis::build(ThickPolylines* polylines)
polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend());
polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend());
polyline.endpoints.first = reverse_polyline.endpoints.second;
assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// Prevent loop endpoints from being extended.
@ -538,7 +538,9 @@ void MedialAxis::build(Polylines* polylines)
{
ThickPolylines tp;
this->build(&tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
polylines->reserve(polylines->size() + tp.size());
for (auto &pl : tp)
polylines->emplace_back(pl.points);
}
void MedialAxis::process_edge_neighbors(const VD::edge_type *edge, ThickPolyline* polyline)

View File

@ -8,17 +8,135 @@
#include "VoronoiUtilsCgal.hpp"
using VD = Slic3r::Geometry::VoronoiDiagram;
using namespace Slic3r::Arachne;
namespace Slic3r::Geometry {
using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2;
using CGAL_Segment = CGAL::Arr_segment_traits_2<CGAL::Exact_predicates_exact_constructions_kernel>::Curve_2;
// The tangent vector of the parabola is computed based on the Proof of the reflective property.
// https://en.wikipedia.org/wiki/Parabola#Proof_of_the_reflective_property
// https://math.stackexchange.com/q/2439647/2439663#comment5039739_2439663
namespace impl {
using K = CGAL::Simple_cartesian<double>;
using FK = CGAL::Simple_cartesian<CGAL::Interval_nt_advanced>;
using EK = CGAL::Simple_cartesian<CGAL::MP_Float>;
using C2E = CGAL::Cartesian_converter<K, EK>;
using C2F = CGAL::Cartesian_converter<K, FK>;
class Epick : public CGAL::Filtered_kernel_adaptor<CGAL::Type_equality_wrapper<K::Base<Epick>::Type, Epick>, true> {};
inline static CGAL_Point to_cgal_point(const VD::vertex_type &pt) { return {pt.x(), pt.y()}; }
template<typename K>
inline typename K::Vector_2 calculate_parabolic_tangent_vector(
// Test point on the parabola, where the tangent will be calculated.
const typename K::Point_2 &p,
// Focus point of the parabola.
const typename K::Point_2 &f,
// Points of a directrix of the parabola.
const typename K::Point_2 &u,
const typename K::Point_2 &v,
// On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
const typename K::Orientation &tangent_orientation)
{
using RT = typename K::RT;
using Vector_2 = typename K::Vector_2;
const Vector_2 directrix_vec = v - u;
const RT directrix_vec_sqr_length = CGAL::scalar_product(directrix_vec, directrix_vec);
Vector_2 focus_vec = (f - u) * directrix_vec_sqr_length - directrix_vec * CGAL::scalar_product(directrix_vec, p - u);
Vector_2 tangent_vec = focus_vec.perpendicular(tangent_orientation);
return tangent_vec;
}
template<typename K> struct ParabolicTangentToSegmentOrientationPredicate
{
using Point_2 = typename K::Point_2;
using Vector_2 = typename K::Vector_2;
using Orientation = typename K::Orientation;
using result_type = typename K::Orientation;
result_type operator()(
// Test point on the parabola, where the tangent will be calculated.
const Point_2 &p,
// End of the linear segment (p, q), for which orientation towards the tangent to parabola will be evaluated.
const Point_2 &q,
// Focus point of the parabola.
const Point_2 &f,
// Points of a directrix of the parabola.
const Point_2 &u,
const Point_2 &v,
// On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
const Orientation &tangent_orientation) const
{
assert(tangent_orientation == CGAL::Orientation::LEFT_TURN || tangent_orientation == CGAL::Orientation::RIGHT_TURN);
Vector_2 tangent_vec = calculate_parabolic_tangent_vector<K>(p, f, u, v, tangent_orientation);
Vector_2 linear_vec = q - p;
return CGAL::sign(tangent_vec.x() * linear_vec.y() - tangent_vec.y() * linear_vec.x());
}
};
template<typename K> struct ParabolicTangentToParabolicTangentOrientationPredicate
{
using Point_2 = typename K::Point_2;
using Vector_2 = typename K::Vector_2;
using Orientation = typename K::Orientation;
using result_type = typename K::Orientation;
result_type operator()(
// Common point on both parabolas, where the tangent will be calculated.
const Point_2 &p,
// Focus point of the first parabola.
const Point_2 &f_0,
// Points of a directrix of the first parabola.
const Point_2 &u_0,
const Point_2 &v_0,
// On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
const Orientation &tangent_orientation_0,
// Focus point of the second parabola.
const Point_2 &f_1,
// Points of a directrix of the second parabola.
const Point_2 &u_1,
const Point_2 &v_1,
// On which side of the parabolic segment endpoints the focus point is, which determines the orientation of the tangent.
const Orientation &tangent_orientation_1) const
{
assert(tangent_orientation_0 == CGAL::Orientation::LEFT_TURN || tangent_orientation_0 == CGAL::Orientation::RIGHT_TURN);
assert(tangent_orientation_1 == CGAL::Orientation::LEFT_TURN || tangent_orientation_1 == CGAL::Orientation::RIGHT_TURN);
Vector_2 tangent_vec_0 = calculate_parabolic_tangent_vector<K>(p, f_0, u_0, v_0, tangent_orientation_0);
Vector_2 tangent_vec_1 = calculate_parabolic_tangent_vector<K>(p, f_1, u_1, v_1, tangent_orientation_1);
return CGAL::sign(tangent_vec_0.x() * tangent_vec_1.y() - tangent_vec_0.y() * tangent_vec_1.x());
}
};
using ParabolicTangentToSegmentOrientationPredicateFiltered = CGAL::Filtered_predicate<ParabolicTangentToSegmentOrientationPredicate<EK>, ParabolicTangentToSegmentOrientationPredicate<FK>, C2E, C2F>;
using ParabolicTangentToParabolicTangentOrientationPredicateFiltered = CGAL::Filtered_predicate<ParabolicTangentToParabolicTangentOrientationPredicate<EK>, ParabolicTangentToParabolicTangentOrientationPredicate<FK>, C2E, C2F>;
} // namespace impl
using ParabolicTangentToSegmentOrientation = impl::ParabolicTangentToSegmentOrientationPredicateFiltered;
using ParabolicTangentToParabolicTangentOrientation = impl::ParabolicTangentToParabolicTangentOrientationPredicateFiltered;
using CGAL_Point = impl::K::Point_2;
inline static CGAL_Point to_cgal_point(const VD::vertex_type *pt) { return {pt->x(), pt->y()}; }
inline static CGAL_Point to_cgal_point(const Point &pt) { return {pt.x(), pt.y()}; }
inline static CGAL_Point to_cgal_point(const Vec2d &pt) { return {pt.x(), pt.y()}; }
inline static Linef make_linef(const VD::edge_type &edge)
{
const VD::vertex_type *v0 = edge.vertex0();
const VD::vertex_type *v1 = edge.vertex1();
return {Vec2d(v0->x(), v0->y()), Vec2d(v1->x(), v1->y())};
}
inline static bool is_equal(const VD::vertex_type &first, const VD::vertex_type &second) { return first.x() == second.x() && first.y() == second.y(); }
// FIXME Lukas H.: Also includes parabolic segments.
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_diagram)
{
using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2;
using CGAL_Segment = CGAL::Arr_segment_traits_2<CGAL::Exact_predicates_exact_constructions_kernel>::Curve_2;
auto to_cgal_point = [](const VD::vertex_type &pt) -> CGAL_Point { return {pt.x(), pt.y()}; };
assert(std::all_of(voronoi_diagram.edges().cbegin(), voronoi_diagram.edges().cend(),
[](const VD::edge_type &edge) { return edge.color() == 0; }));
@ -30,7 +148,7 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_
continue;
if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr &&
Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) {
VoronoiUtils::is_finite(*edge.vertex0()) && VoronoiUtils::is_finite(*edge.vertex1())) {
segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1()));
edge.color(1);
assert(edge.twin() != nullptr);
@ -46,37 +164,101 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_
return intersections_pt.empty();
}
static bool check_if_three_vectors_are_ccw(const CGAL_Point &common_pt, const CGAL_Point &pt_1, const CGAL_Point &pt_2, const CGAL_Point &test_pt) {
CGAL::Orientation orientation = CGAL::orientation(common_pt, pt_1, pt_2);
struct ParabolicSegment
{
const Point focus;
const Line directrix;
// Two points on the parabola;
const Linef segment;
// Indicate if focus point is on the left side or right side relative to parabolic segment endpoints.
const CGAL::Orientation is_focus_on_left;
};
inline static ParabolicSegment get_parabolic_segment(const VD::edge_type &edge, const std::vector<VoronoiUtils::Segment> &segments)
{
assert(edge.is_curved());
const VD::cell_type *left_cell = edge.cell();
const VD::cell_type *right_cell = edge.twin()->cell();
const Point focus_pt = VoronoiUtils::getSourcePoint(*(left_cell->contains_point() ? left_cell : right_cell), segments);
const VoronoiUtils::Segment &directrix = VoronoiUtils::getSourceSegment(*(left_cell->contains_point() ? right_cell : left_cell), segments);
CGAL::Orientation focus_side = CGAL::opposite(CGAL::orientation(to_cgal_point(edge.vertex0()), to_cgal_point(edge.vertex1()), to_cgal_point(focus_pt)));
assert(focus_side == CGAL::Orientation::LEFT_TURN || focus_side == CGAL::Orientation::RIGHT_TURN);
return {focus_pt, Line(directrix.from(), directrix.to()), make_linef(edge), focus_side};
}
inline static CGAL::Orientation orientation_of_two_edges(const VD::edge_type &edge_a, const VD::edge_type &edge_b, const std::vector<VoronoiUtils::Segment> &segments) {
assert(is_equal(*edge_a.vertex0(), *edge_b.vertex0()));
CGAL::Orientation orientation;
if (edge_a.is_linear() && edge_b.is_linear()) {
orientation = CGAL::orientation(to_cgal_point(edge_a.vertex0()), to_cgal_point(edge_a.vertex1()), to_cgal_point(edge_b.vertex1()));
} else if (edge_a.is_curved() && edge_b.is_curved()) {
const ParabolicSegment parabolic_a = get_parabolic_segment(edge_a, segments);
const ParabolicSegment parabolic_b = get_parabolic_segment(edge_b, segments);
orientation = ParabolicTangentToParabolicTangentOrientation{}(to_cgal_point(parabolic_a.segment.a),
to_cgal_point(parabolic_a.focus),
to_cgal_point(parabolic_a.directrix.a),
to_cgal_point(parabolic_a.directrix.b),
parabolic_a.is_focus_on_left,
to_cgal_point(parabolic_b.focus),
to_cgal_point(parabolic_b.directrix.a),
to_cgal_point(parabolic_b.directrix.b),
parabolic_b.is_focus_on_left);
return orientation;
} else {
assert(edge_a.is_curved() != edge_b.is_curved());
const VD::edge_type &linear_edge = edge_a.is_curved() ? edge_b : edge_a;
const VD::edge_type &parabolic_edge = edge_a.is_curved() ? edge_a : edge_b;
const ParabolicSegment parabolic = get_parabolic_segment(parabolic_edge, segments);
orientation = ParabolicTangentToSegmentOrientation{}(to_cgal_point(parabolic.segment.a), to_cgal_point(linear_edge.vertex1()),
to_cgal_point(parabolic.focus),
to_cgal_point(parabolic.directrix.a),
to_cgal_point(parabolic.directrix.b),
parabolic.is_focus_on_left);
if (edge_b.is_curved())
orientation = CGAL::opposite(orientation);
}
return orientation;
}
static bool check_if_three_edges_are_ccw(const VD::edge_type &first, const VD::edge_type &second, const VD::edge_type &third, const std::vector<VoronoiUtils::Segment> &segments)
{
assert(is_equal(*first.vertex0(), *second.vertex0()) && is_equal(*second.vertex0(), *third.vertex0()));
CGAL::Orientation orientation = orientation_of_two_edges(first, second, segments);
if (orientation == CGAL::Orientation::COLLINEAR) {
// The first two edges are collinear, so the third edge must be on the right side on the first of them.
return CGAL::orientation(common_pt, pt_1, test_pt) == CGAL::Orientation::RIGHT_TURN;
return orientation_of_two_edges(first, third, segments) == CGAL::Orientation::RIGHT_TURN;
} else if (orientation == CGAL::Orientation::LEFT_TURN) {
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is bellow PI.
// So we need to check if test_pt isn't between them.
CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
CGAL::Orientation orientation1 = orientation_of_two_edges(first, third, segments);
CGAL::Orientation orientation2 = orientation_of_two_edges(second, third, segments);
return (orientation1 != CGAL::Orientation::LEFT_TURN || orientation2 != CGAL::Orientation::RIGHT_TURN);
} else {
assert(orientation == CGAL::Orientation::RIGHT_TURN);
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is upper PI.
// So we need to check if test_pt is between them.
CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
CGAL::Orientation orientation1 = orientation_of_two_edges(first, third, segments);
CGAL::Orientation orientation2 = orientation_of_two_edges(second, third, segments);
return (orientation1 == CGAL::Orientation::RIGHT_TURN || orientation2 == CGAL::Orientation::LEFT_TURN);
}
}
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram)
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments)
{
for (const VD::vertex_type &vertex : voronoi_diagram.vertices()) {
std::vector<const VD::edge_type *> edges;
const VD::edge_type *edge = vertex.incident_edge();
do {
// FIXME Lukas H.: Also process parabolic segments.
if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr &&
Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1()))
if (edge->is_finite() && edge->vertex0() != nullptr && edge->vertex1() != nullptr &&
VoronoiUtils::is_finite(*edge->vertex0()) && VoronoiUtils::is_finite(*edge->vertex1()))
edges.emplace_back(edge);
edge = edge->rot_next();
@ -89,8 +271,7 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor
const Geometry::VoronoiDiagram::edge_type *curr_edge = *edge_it;
const Geometry::VoronoiDiagram::edge_type *next_edge = std::next(edge_it) == edges.end() ? edges.front() : *std::next(edge_it);
if (!check_if_three_vectors_are_ccw(to_cgal_point(*prev_edge->vertex0()), to_cgal_point(*prev_edge->vertex1()),
to_cgal_point(*curr_edge->vertex1()), to_cgal_point(*next_edge->vertex1())))
if (!check_if_three_edges_are_ccw(*prev_edge, *curr_edge, *next_edge, segments))
return false;
}
}
@ -99,5 +280,4 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor
return true;
}
} // namespace Slic3r::Geometry

View File

@ -2,6 +2,7 @@
#define slic3r_VoronoiUtilsCgal_hpp_
#include "Voronoi.hpp"
#include "../Arachne/utils/VoronoiUtils.hpp"
namespace Slic3r::Geometry {
class VoronoiDiagram;
@ -13,7 +14,7 @@ public:
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram);
// Check if the Voronoi diagram is planar using verification that all neighboring edges are ordered CCW for each vertex.
static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram);
static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector<Arachne::VoronoiUtils::Segment> &segments);
};
} // namespace Slic3r::Geometry

View File

@ -133,7 +133,7 @@ Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
BoundingBox bb_(min_, max_);
// intersect of BB compare min max
if (bb.intersects(bb_) &&
if (bb.overlap(bb_) &&
l.intersection(l_, &i))
pts.push_back(i.cast<double>());
}

View File

@ -317,13 +317,16 @@ void Layer::build_up_down_graph(Layer& below, Layer& above)
coord_t* end = srcs + 4;
std::sort(begin, end);
end = std::unique(begin, end);
assert(begin + 2 == end);
if (begin + 1 == end)
if (begin + 1 == end) {
// Self intersection may happen on source contour. Just copy the Z value.
pt.z() = *begin;
else if (begin + 2 <= end) {
// store a -1 based negative index into the "intersections" vector here.
m_intersections.emplace_back(srcs[0], srcs[1]);
pt.z() = -coord_t(m_intersections.size());
} else {
assert(begin + 2 == end);
if (begin + 2 <= end) {
// store a -1 based negative index into the "intersections" vector here.
m_intersections.emplace_back(srcs[0], srcs[1]);
pt.z() = -coord_t(m_intersections.size());
}
}
}
const std::vector<std::pair<coord_t, coord_t>>& intersections() const { return m_intersections; }
@ -494,15 +497,18 @@ void Layer::make_perimeters()
} else {
SurfaceCollection new_slices;
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence.
LayerRegion *layerm_config = m_regions[layer_region_ids.front()];
{
uint32_t region_id_config = layer_region_ids.front();
LayerRegion* layerm_config = m_regions[region_id_config];
{
// Merge slices (surfaces) according to number of extra perimeters.
for (uint32_t region_id : layer_region_ids) {
LayerRegion &layerm = *m_regions[region_id];
for (const Surface &surface : layerm.slices())
surfaces_to_merge.emplace_back(&surface);
if (layerm.region().config().fill_density > layerm_config->region().config().fill_density)
layerm_config = &layerm;
if (layerm.region().config().fill_density > layerm_config->region().config().fill_density) {
region_id_config = region_id;
layerm_config = &layerm;
}
}
std::sort(surfaces_to_merge.begin(), surfaces_to_merge.end(), [](const Surface *l, const Surface *r){ return l->extra_perimeters < r->extra_perimeters; });
for (size_t i = 0; i < surfaces_to_merge.size();) {
@ -522,7 +528,7 @@ void Layer::make_perimeters()
}
// make perimeters
layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges);
this->sort_perimeters_into_islands(new_slices, region_id, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
this->sort_perimeters_into_islands(new_slices, region_id_config, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids);
}
}
}

View File

@ -324,7 +324,7 @@ public:
coordf_t height; // layer height in unscaled coordinates
coordf_t bottom_z() const { return this->print_z - this->height; }
//Lines estimated to be seriously malformed, info from the IssueSearch algorithm. These lines should probably be avoided during fast travels.
//Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels.
Lines malformed_lines;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry

View File

@ -91,28 +91,7 @@ bool Line::perpendicular_to(const Line& line) const
bool Line::intersection(const Line &l2, Point *intersection) const
{
const Line &l1 = *this;
const Vec2d v1 = (l1.b - l1.a).cast<double>();
const Vec2d v2 = (l2.b - l2.a).cast<double>();
double denom = cross2(v1, v2);
if (fabs(denom) < EPSILON)
#if 0
// Lines are collinear. Return true if they are coincident (overlappign).
return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
#else
return false;
#endif
const Vec2d v12 = (l1.a - l2.a).cast<double>();
double nume_a = cross2(v2, v12);
double nume_b = cross2(v1, v12);
double t1 = nume_a / denom;
double t2 = nume_b / denom;
if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
// Get the intersection point.
(*intersection) = (l1.a.cast<double>() + t1 * v1).cast<coord_t>();
return true;
}
return false; // not intersecting
return line_alg::intersection(*this, l2, intersection);
}
bool Line::clip_with_bbox(const BoundingBox &bbox)

View File

@ -120,6 +120,33 @@ double distance_to_infinite(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
return std::sqrt(distance_to_infinite_squared(line, point));
}
template<class L> bool intersection(const L &l1, const L &l2, Vec<Dim<L>, Scalar<L>> *intersection_pt)
{
using Floating = typename std::conditional<std::is_floating_point<Scalar<L>>::value, Scalar<L>, double>::type;
using VecType = const Vec<Dim<L>, Floating>;
const VecType v1 = (l1.b - l1.a).template cast<Floating>();
const VecType v2 = (l2.b - l2.a).template cast<Floating>();
Floating denom = cross2(v1, v2);
if (fabs(denom) < EPSILON)
#if 0
// Lines are collinear. Return true if they are coincident (overlappign).
return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
#else
return false;
#endif
const VecType v12 = (l1.a - l2.a).template cast<Floating>();
Floating nume_a = cross2(v2, v12);
Floating nume_b = cross2(v1, v12);
Floating t1 = nume_a / denom;
Floating t2 = nume_b / denom;
if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
// Get the intersection point.
(*intersection_pt) = (l1.a.template cast<Floating>() + t1 * v1).template cast<Scalar<L>>();
return true;
}
return false; // not intersecting
}
} // namespace line_alg
class Line

View File

@ -1641,13 +1641,47 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
return res;
}
/// <summary>
/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
/// From Front(Z) Upper(Y) TopLeft(X) corner.
/// 1. Seraparate group not overlaped i Z axis
/// 2. Seraparate group not overlaped i Y axis
/// 3. Start earlier in X (More on left side)
/// </summary>
/// <param name="triangle_mesh1">Compare from</param>
/// <param name="triangle_mesh2">Compare to</param>
/// <returns>True when triangle mesh 1 is closer, upper or lefter than triangle mesh 2 other wise false</returns>
static bool is_front_up_left(const TriangleMesh &trinagle_mesh1, const TriangleMesh &triangle_mesh2)
{
// stats form t1
const Vec3f &min1 = trinagle_mesh1.stats().min;
const Vec3f &max1 = trinagle_mesh1.stats().max;
// stats from t2
const Vec3f &min2 = triangle_mesh2.stats().min;
const Vec3f &max2 = triangle_mesh2.stats().max;
// priority Z, Y, X
for (int axe = 2; axe > 0; --axe) {
if (max1[axe] < min2[axe])
return true;
if (min1[axe] > max2[axe])
return false;
}
return min1.x() < min2.x();
}
void ModelObject::split(ModelObjectPtrs* new_objects)
{
for (ModelVolume* volume : this->volumes) {
if (volume->type() != ModelVolumeType::MODEL_PART)
continue;
// splited volume should not be text object
if (volume->text_configuration.has_value())
volume->text_configuration.reset();
std::vector<TriangleMesh> meshes = volume->mesh().split();
std::sort(meshes.begin(), meshes.end(), is_front_up_left);
size_t counter = 1;
for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied
@ -2131,9 +2165,15 @@ size_t ModelVolume::split(unsigned int max_extruders)
if (meshes.size() <= 1)
return 1;
std::sort(meshes.begin(), meshes.end(), is_front_up_left);
// splited volume should not be text object
if (text_configuration.has_value())
text_configuration.reset();
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
const std::string name = this->name;
const std::string& name = this->name;
unsigned int extruder_counter = 0;
const Vec3d offset = this->get_offset();

View File

@ -14,7 +14,6 @@
#include "Arrange.hpp"
#include "CustomGCode.hpp"
#include "enum_bitmask.hpp"
//#include "ModelVolumeType.hpp"
#include "TextConfiguration.hpp"
#include <map>

View File

@ -1,16 +0,0 @@
#ifndef slic3r_ModelVolumeType_hpp_
#define slic3r_ModelVolumeType_hpp_
namespace Slic3r {
enum class ModelVolumeType : int {
INVALID = -1,
MODEL_PART = 0,
NEGATIVE_VOLUME,
PARAMETER_MODIFIER,
SUPPORT_BLOCKER,
SUPPORT_ENFORCER,
};
} // namespace Slic3r
#endif /* slic3r_ModelVolumeType_hpp_ */

View File

@ -3,6 +3,8 @@
using namespace Slic3r;
// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
float tessTol,
Vec2f p1,

View File

@ -397,22 +397,37 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path &subject, con
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
// The clipping contour may be simplified by clipping it with a bounding box of "subject" path.
// The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are
// harmless to the result of the clipping operation,
// Both ends of each edge belong to the same source: Either they are from subject or from clipping path.
assert(e1bot.z() >= 0 && e1top.z() >= 0);
assert(e2bot.z() >= 0 && e2top.z() >= 0);
assert((e1bot.z() == 0) == (e1top.z() == 0));
assert((e2bot.z() == 0) == (e2top.z() == 0));
// Start & end points of the clipped polyline (extrusion path with a non-zero width).
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
end = e2top;
}
assert(start.z() > 0 && end.z() > 0);
if (start.z() <= 0 && end.z() <= 0) {
// Self intersection on the source contour.
assert(start.z() == 0 && end.z() == 0);
pt.z() = 0;
} else {
// Interpolate extrusion line width.
assert(start.z() > 0 && end.z() > 0);
// Interpolate extrusion line width.
double length_sqr = (end - start).cast<double>().squaredNorm();
double dist_sqr = (pt - start).cast<double>().squaredNorm();
double t = std::sqrt(dist_sqr / length_sqr);
double length_sqr = (end - start).cast<double>().squaredNorm();
double dist_sqr = (pt - start).cast<double>().squaredNorm();
double t = std::sqrt(dist_sqr / length_sqr);
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
}
});
clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
@ -649,11 +664,11 @@ bool paths_touch(const ExtrusionPath &path_one, const ExtrusionPath &path_two, d
AABBTreeLines::LinesDistancer<Line> lines_two{path_two.as_polyline().lines()};
for (size_t pt_idx = 0; pt_idx < path_one.polyline.size(); pt_idx++) {
if (std::abs(lines_two.signed_distance_from_lines(path_one.polyline.points[pt_idx])) < limit_distance) { return true; }
if (lines_two.distance_from_lines<false>(path_one.polyline.points[pt_idx]) < limit_distance) { return true; }
}
for (size_t pt_idx = 0; pt_idx < path_two.polyline.size(); pt_idx++) {
if (std::abs(lines_one.signed_distance_from_lines(path_two.polyline.points[pt_idx])) < limit_distance) { return true; }
if (lines_one.distance_from_lines<false>(path_two.polyline.points[pt_idx]) < limit_distance) { return true; }
}
return false;
}
@ -802,10 +817,12 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
{
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
Polygons anchors = intersection(infill_area, lower_slices_polygons);
Polygons overhangs = diff(infill_area, lower_slices_polygons);
if (overhangs.empty()) { return {}; }
BoundingBox infill_area_bb = get_extents(infill_area).inflated(SCALED_EPSILON);
Polygons optimized_lower_slices = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons, infill_area_bb);
Polygons overhangs = diff(infill_area, optimized_lower_slices);
if (overhangs.empty()) { return {}; }
Polygons anchors = intersection(infill_area, optimized_lower_slices);
Polygons inset_anchors; // anchored area inset by the anchor length
{
std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(),
@ -814,23 +831,21 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
std::vector<Polygons> anchor_areas_w_delta_anchor_size{};
for (double delta : deltas) {
// for each delta, store anchors without the delta region around overhangs
anchor_areas_w_delta_anchor_size.push_back(diff(anchors, expand(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
}
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) {
Polygons clipped = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
// Then, clip off each anchor area by the next area expanded back to original size, so that this smaller anchor region is only where larger wouldnt fit
anchor_areas_w_delta_anchor_size[i] = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS));
anchor_areas_w_delta_anchor_size[i] = intersection(anchor_areas_w_delta_anchor_size[i],
expand(clipped, deltas[i+1] + 0.1*overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS));
}
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
}
inset_anchors = opening(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
inset_anchors = closing(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
inset_anchors = expand(inset_anchors, 0.1*overhang_flow.scaled_width());
#ifdef EXTRA_PERIM_DEBUG_FILES
{
@ -884,12 +899,6 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring);
double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull}));
// penalize also holes
for (const Polygon &poly : perimeter_polygon) {
if (poly.is_clockwise()) { // hole, penalize bridges.
unbridgeable_area += std::abs(area(poly));
}
}
auto [dir, unsupp_dist] = detect_bridging_direction(real_overhang, anchors);
@ -903,16 +912,19 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15));
for (const Line &line : to_lines(anchoring)) svg.draw(line, "yellow", scale_(0.10));
for (const Line &line : to_lines(diff_ex(perimeter_polygon, {anchoring_convex_hull}))) svg.draw(line, "black", scale_(0.10));
for (const Line &line : to_lines(diff_pl(to_polylines(diff(real_overhang, anchors)), expand(anchors, float(SCALED_EPSILON)))))
svg.draw(line, "blue", scale_(0.30));
svg.Close();
}
#endif
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.125) {
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.2) {
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end());
perimeter_polygon.clear();
} else {
// fill the overhang with perimeters
int continuation_loops = 2;
while (continuation_loops > 0) {
while (continuation_loops >= 0) {
auto prev = perimeter_polygon;
// prepare next perimeter lines
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
@ -936,7 +948,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
//gap = expolygons_simplify(gap, overhang_flow.scaled_spacing());
for (const ExPolygon &ep : gap) {
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.3 * overhang_flow.scaled_width(), &fills);
ep.medial_axis(0.3 * overhang_flow.scaled_width(), overhang_flow.scaled_spacing() * 2.0, &fills);
}
if (!fills.empty()) {
fills = intersection_pl(fills, inset_overhang_area);

View File

@ -66,9 +66,9 @@ bool has_duplicate_points(std::vector<Point> &&pts)
return false;
}
Points collect_duplications(Points pts /* Copy */)
Points collect_duplicates(Points pts /* Copy */)
{
std::stable_sort(pts.begin(), pts.end());
std::sort(pts.begin(), pts.end());
Points duplicits;
const Point *prev = &pts.front();
for (size_t i = 1; i < pts.size(); ++i) {

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
namespace Slic3r {
class Polyline;
class ThickPolyline;
struct ThickPolyline;
typedef std::vector<Polyline> Polylines;
typedef std::vector<ThickPolyline> ThickPolylines;
@ -160,20 +160,28 @@ bool remove_degenerate(Polylines &polylines);
// Returns index of a segment of a polyline and foot point of pt on polyline.
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt);
class ThickPolyline : public Polyline {
public:
ThickPolyline() : endpoints(std::make_pair(false, false)) {}
struct ThickPolyline {
ThickPolyline() = default;
ThickLines thicklines() const;
const Point& first_point() const { return this->points.front(); }
const Point& last_point() const { return this->points.back(); }
bool is_valid() const { return this->points.size() >= 2; }
double length() const { return Slic3r::length(this->points); }
void clear() { this->points.clear(); this->width.clear(); }
void reverse() {
Polyline::reverse();
std::reverse(this->points.begin(), this->points.end());
std::reverse(this->width.begin(), this->width.end());
std::swap(this->endpoints.first, this->endpoints.second);
}
void clip_end(double distance);
std::vector<coordf_t> width;
std::pair<bool,bool> endpoints;
Points points;
std::vector<coordf_t> width;
std::pair<bool,bool> endpoints { false, false };
};
inline ThickPolylines to_thick_polylines(Polylines &&polylines, const coordf_t width)

View File

@ -420,7 +420,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
static std::vector<std::string> s_Preset_print_options {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "avoid_curled_filament_during_travels", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"extra_perimeters", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
@ -429,6 +429,7 @@ static std::vector<std::string> s_Preset_print_options {
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "dynamic_overhang_speeds", "overhang_overlap_levels",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
@ -1215,18 +1216,19 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys&
}
// list of options with vector variable, which is independent from number of extruders
static const std::vector<std::string> independent_from_extruder_number_options = {
static const std::set<std::string> independent_from_extruder_number_options = {
"bed_shape",
"thumbnails",
"compatible_printers",
"compatible_prints",
"filament_ramming_parameters",
"gcode_substitutions",
"compatible_prints",
"compatible_printers"
"post_process",
"thumbnails",
};
bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key)
{
return std::find(independent_from_extruder_number_options.begin(), independent_from_extruder_number_options.end(), opt_key) != independent_from_extruder_number_options.end();
return independent_from_extruder_number_options.find(opt_key) != independent_from_extruder_number_options.end();
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
@ -1253,6 +1255,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
case coFloatsOrPercents: add_correct_opts_to_diff<ConfigOptionFloatsOrPercents >(opt_key, diff, config_other, config_this); break;
default: diff.emplace_back(opt_key); break;
}
}

View File

@ -7,6 +7,7 @@
#include <memory>
#include <unordered_map>
#include <array>
#include <boost/filesystem/path.hpp>
namespace Slic3r {
@ -155,6 +156,13 @@ public:
const std::string& new_name, const std::vector<std::string>& options);
static const char *PRUSA_BUNDLE;
static std::array<Preset::Type, 3> types_list(PrinterTechnology pt) {
if (pt == ptFFF)
return { Preset::TYPE_PRINTER, Preset::TYPE_PRINT, Preset::TYPE_FILAMENT };
return { Preset::TYPE_PRINTER, Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL };
}
private:
std::pair<PresetsConfigSubstitutions, std::string> load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule);
// Merge one vendor's presets with the other vendor's presets, report duplicates.

View File

@ -222,7 +222,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
osteps.emplace_back(posInfill);
osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirtBrim);
} else if (opt_key == "avoid_curled_filament_during_travels") {
} else if (opt_key == "avoid_crossing_curled_overhangs") {
osteps.emplace_back(posEstimateCurledExtrusions);
} else {
// for legacy, if we can't handle this option let's invalidate all steps

View File

@ -8,6 +8,7 @@
#include "Flow.hpp"
#include "Point.hpp"
#include "Slicing.hpp"
#include "SupportSpotsGenerator.hpp"
#include "TriangleMeshSlicer.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
@ -20,6 +21,7 @@
#include <Eigen/Geometry>
#include <functional>
#include <optional>
#include <set>
#include <tcbspan/span.hpp>
@ -200,6 +202,11 @@ public:
}
};
struct GeneratedSupportPoints{
Transform3d object_transform; // for frontend object mapping
SupportSpotsGenerator::SupportPoints support_points;
};
std::vector<std::unique_ptr<PrintRegion>> all_regions;
std::vector<LayerRangeRegions> layer_ranges;
// Transformation of this ModelObject into one of the associated PrintObjects (all PrintObjects derived from a single modelObject differ by a Z rotation only).
@ -207,6 +214,8 @@ public:
Transform3d trafo_bboxes;
std::vector<ObjectID> cached_volume_ids;
std::optional<GeneratedSupportPoints> generated_support_points;
void ref_cnt_inc() { ++ m_ref_cnt; }
void ref_cnt_dec() { if (-- m_ref_cnt == 0) delete this; }
void clear() {
@ -507,6 +516,7 @@ public:
void set_task(const TaskParams &params) override { PrintBaseWithState<PrintStep, psCount>::set_task_impl(params, m_objects); }
void process() override;
void finalize() override { PrintBaseWithState<PrintStep, psCount>::finalize_impl(m_objects); }
void cleanup() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
@ -599,6 +609,12 @@ private:
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners() const;
// Returns true if any of the print_objects has print_object_step valid.
// That means data shared by all print objects of the print_objects span may still use the shared data.
// Otherwise the shared data shall be released.
// Unguarded variant, thus it shall only be called from main thread with background processing stopped.
static bool is_shared_print_object_step_valid_unguarded(SpanOfConstPtrs<PrintObject> print_objects, PrintObjectStep print_object_step);
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;

View File

@ -1451,6 +1451,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
if (apply_status == APPLY_STATUS_CHANGED || apply_status == APPLY_STATUS_INVALIDATED)
this->cleanup();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
@ -1458,4 +1461,25 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
return static_cast<ApplyStatus>(apply_status);
}
void Print::cleanup()
{
// Invalidate data of a single ModelObject shared by multiple PrintObjects.
// Find spans of PrintObjects sharing the same PrintObjectRegions.
std::vector<PrintObject*> all_objects(m_objects);
std::sort(all_objects.begin(), all_objects.end(), [](const PrintObject *l, const PrintObject *r){ return l->shared_regions() < r->shared_regions(); } );
for (auto it = all_objects.begin(); it != all_objects.end();) {
PrintObjectRegions *shared_regions = (*it)->m_shared_regions;
auto it_begin = it;
for (++ it; it != all_objects.end() && shared_regions == (*it)->shared_regions(); ++ it);
auto this_objects = SpanOfConstPtrs<PrintObject>(const_cast<const PrintObject* const* const>(&(*it_begin)), it - it_begin);
if (Print::is_shared_print_object_step_valid_unguarded(this_objects, posSupportSpotsSearch))
shared_regions->generated_support_points.reset();
}
}
bool Print::is_shared_print_object_step_valid_unguarded(SpanOfConstPtrs<PrintObject> print_objects, PrintObjectStep print_object_step)
{
return std::any_of(print_objects.begin(), print_objects.end(), [print_object_step](auto po){ return po->is_step_done_unguarded(print_object_step); });
}
} // namespace Slic3r

View File

@ -13,7 +13,7 @@
namespace Slic3r
{
void PrintTryCancel::operator()()
void PrintTryCancel::operator()() const
{
m_print->throw_if_canceled();
}

View File

@ -340,7 +340,7 @@ class PrintTryCancel
{
public:
// calls print.throw_if_canceled().
void operator()();
void operator()() const;
private:
friend PrintBase;
PrintTryCancel() = delete;
@ -408,6 +408,10 @@ public:
// Clean up after process() finished, either with success, error or if canceled.
// The adjustments on the Print / PrintObject data due to set_task() are to be reverted here.
virtual void finalize() = 0;
// Clean up print step / print object step data after
// 1) some print step / print object step was invalidated inside PrintBase::apply() while holding the milestone mutex locked.
// 2) background thread finished being canceled.
virtual void cleanup() = 0;
struct SlicingStatus {
SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {}

View File

@ -70,6 +70,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(MachineLimitsUsage)
static const t_config_enum_values s_keys_map_PrintHostType {
{ "prusalink", htPrusaLink },
{ "prusaconnect", htPrusaConnect },
{ "octoprint", htOctoPrint },
{ "duet", htDuet },
{ "flashair", htFlashAir },
@ -399,10 +400,10 @@ void PrintConfigDef::init_fff_params()
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
const int max_temp = 1500;
def = this->add("avoid_curled_filament_during_travels", coBool);
def->label = L("Avoid curled filament during travels");
def->tooltip = L("Plan travel moves such that the extruder avoids areas where filament may be curled up. "
"This is mostly happening on steeper rounded overhangs and may cause crash or borken print. "
def = this->add("avoid_crossing_curled_overhangs", coBool);
def->label = L("Avoid crossing curled overhangs (Experimental)");
def->tooltip = L("Plan travel moves such that the extruder avoids areas where the filament may be curled up. "
"This is mostly happening on steeper rounded overhangs and may cause a crash with the nozzle. "
"This feature slows down both the print and the G-code generation.");
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
@ -528,6 +529,40 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60));
def = this->add("enable_dynamic_overhang_speeds", coBool);
def->label = L("Enable dynamic overhang speeds (Experimental)");
def->category = L("Speed");
def->tooltip = L("This setting enables dynamic speed control on overhangs.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("overhang_overlap_levels", coPercents);
def->full_label = L("Overhang overlap levels");
def->category = L("Speed");
def->tooltip = L("Controls overhang levels, expressed as a percentage of overlap of the extrusion with the previous layer - "
"100% represents full overlap - no overhang is present, while 0% represents full overhang (floating extrusion). "
"Each overhang level then corresponds with the overhang speed below. Speeds for overhang levels in between are "
"calculated via linear interpolation."
"If you set multiple different speeds for the same overhang level, only the largest speed is used. "
);
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercents({60, 40, 20, 0}));
def = this->add("dynamic_overhang_speeds", coFloatsOrPercents);
def->full_label = L("Dynamic speed on overhangs");
def->category = L("Speed");
def->tooltip = L("This setting controls the speed on the overhang with the overlap value set above. "
"The speed of the extrusion is calculated as a linear interpolation of the speeds for higher and lower overlap. "
"If set as percentage, the speed is calculated over the external perimeter speed."
);
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatsOrPercents({{25, false}, {20, false}, {15, false}, {15, false}}));
def = this->add("brim_width", coFloat);
def->label = L("Brim width");
def->category = L("Skirt and brim");
@ -804,9 +839,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionBool(true));
def = this->add("extra_perimeters_on_overhangs", coBool);
def->label = L("Extra perimeters on overhangs");
def->label = L("Extra perimeters on overhangs (Experimental)");
def->category = L("Layers and Perimeters");
def->tooltip = L("Create additional perimeter paths over steep overhangs and areas where bridges cannot be anchored. ");
def->tooltip = L("Create additional perimeter paths over steep overhangs and areas where bridges cannot be anchored.");
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
@ -1909,6 +1944,7 @@ void PrintConfigDef::init_fff_params()
"the kind of the host.");
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
def->enum_values.push_back("prusalink");
def->enum_values.push_back("prusaconnect");
def->enum_values.push_back("octoprint");
def->enum_values.push_back("duet");
def->enum_values.push_back("flashair");
@ -1916,6 +1952,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("repetier");
def->enum_values.push_back("mks");
def->enum_labels.push_back("PrusaLink");
def->enum_labels.push_back("PrusaConnect");
def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir");
@ -1924,7 +1961,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("MKS");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htPrusaLink));
def = this->add("only_retract_when_crossing_perimeters", coBool);
def->label = L("Only retract when crossing perimeters");
@ -4607,6 +4644,10 @@ CLITransformConfigDef::CLITransformConfigDef()
def->label = L("Scale to Fit");
def->tooltip = L("Scale to fit the given volume.");
def->set_default_value(new ConfigOptionPoint3(Vec3d(0,0,0)));
def = this->add("delete-after-load", coString);
def->label = L("Delete files after loading");
def->tooltip = L("Delete files after loading.");
}
CLIMiscConfigDef::CLIMiscConfigDef()

View File

@ -43,7 +43,7 @@ enum class MachineLimitsUsage {
};
enum PrintHostType {
htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
};
enum AuthorizationType {
@ -516,7 +516,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, min_feature_size))
((ConfigOptionFloatOrPercent, min_bead_width))
((ConfigOptionBool, support_material))
// Automatic supports (generated based on support_material_threshold).
// Automatic supports (generated based fdm support point generator).
((ConfigOptionBool, support_material_auto))
// Direction of the support pattern (in XY plane).`
((ConfigOptionFloat, support_material_angle))
@ -564,6 +564,9 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<InfillPattern>, bottom_fill_pattern))
((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width))
((ConfigOptionFloatOrPercent, external_perimeter_speed))
((ConfigOptionBool, enable_dynamic_overhang_speeds))
((ConfigOptionPercents, overhang_overlap_levels))
((ConfigOptionFloatsOrPercents, dynamic_overhang_speeds))
((ConfigOptionBool, external_perimeters_first))
((ConfigOptionBool, extra_perimeters))
((ConfigOptionBool, extra_perimeters_on_overhangs))
@ -729,7 +732,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
PrintConfig,
(MachineEnvelopeConfig, GCodeConfig),
((ConfigOptionBool, avoid_curled_filament_during_travels))
((ConfigOptionBool, avoid_crossing_curled_overhangs))
((ConfigOptionBool, avoid_crossing_perimeters))
((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour))
((ConfigOptionPoints, bed_shape))

View File

@ -7,6 +7,7 @@
#include "I18N.hpp"
#include "Layer.hpp"
#include "MutablePolygon.hpp"
#include "PrintBase.hpp"
#include "SupportMaterial.hpp"
#include "TreeSupport.hpp"
#include "Surface.hpp"
@ -417,44 +418,16 @@ std::vector<size_t> problematic_layers = SupportSpotsGenerator::quick_search(thi
void PrintObject::generate_support_spots()
{
if (this->set_started(posSupportSpotsSearch)) {
BOOST_LOG_TRIVIAL(debug)
<< "Searching support spots - start";
BOOST_LOG_TRIVIAL(debug) << "Searching support spots - start";
m_print->set_status(75, L("Searching support spots"));
if (m_config.support_material && !m_config.support_material_auto &&
std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(),
[](const ModelVolume* mv){return mv->supported_facets.empty();})
) {
SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values};
SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params);
auto obj_transform = this->trafo_centered();
for (ModelVolume *model_volume : this->model_object()->volumes) {
if (model_volume->is_model_part()) {
Transform3d mesh_transformation = obj_transform * model_volume->get_matrix();
Transform3d inv_transform = mesh_transformation.inverse();
TriangleSelectorWrapper selector { model_volume->mesh(), mesh_transformation};
for (const SupportSpotsGenerator::SupportPoint &support_point : issues.support_points) {
Vec3f point = Vec3f(inv_transform.cast<float>() * support_point.position);
Vec3f origin = Vec3f(
inv_transform.cast<float>() * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f));
selector.enforce_spot(point, origin, support_point.spot_radius);
}
model_volume->supported_facets.set(selector.selector);
#if 0 //DEBUG export
indexed_triangle_set copy = model_volume->mesh().its;
its_transform(copy, obj_transform * model_transformation);
its_write_obj(copy,
debug_out_path(("model"+std::to_string(model_volume->id().id)+".obj").c_str()).c_str());
#endif
}
}
if (!this->shared_regions()->generated_support_points.has_value()) {
PrintTryCancel cancel_func = m_print->make_try_cancel();
SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values};
SupportSpotsGenerator::SupportPoints supp_points = SupportSpotsGenerator::full_search(this, cancel_func, params);
this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points};
m_print->throw_if_canceled();
}
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug)
<< "Searching support spots - end";
BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end";
this->set_done(posSupportSpotsSearch);
}
}
@ -483,7 +456,7 @@ void PrintObject::generate_support_material()
void PrintObject::estimate_curled_extrusions()
{
if (this->set_started(posEstimateCurledExtrusions)) {
if (this->print()->config().avoid_curled_filament_during_travels) {
if (this->print()->config().avoid_crossing_curled_overhangs) {
BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start";
m_print->set_status(88, L("Estimating curled extrusions"));
@ -492,7 +465,6 @@ void PrintObject::estimate_curled_extrusions()
SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values};
SupportSpotsGenerator::estimate_supports_malformations(this->support_layers(), support_flow_width, params);
SupportSpotsGenerator::estimate_malformations(this->layers(), params);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - end";
}
@ -777,6 +749,9 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_material_speed"
|| opt_key == "support_material_interface_speed"
|| opt_key == "bridge_speed"
|| opt_key == "enable_dynamic_overhang_speeds"
|| opt_key == "overhang_overlap_levels"
|| opt_key == "dynamic_overhang_speeds"
|| opt_key == "external_perimeter_speed"
|| opt_key == "infill_speed"
|| opt_key == "perimeter_speed"
@ -813,10 +788,10 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posPrepareInfill) {
invalidated |= this->invalidate_steps({ posInfill, posIroning });
} else if (step == posInfill) {
invalidated |= this->invalidate_steps({ posIroning });
invalidated |= this->invalidate_steps({ posIroning, posSupportSpotsSearch });
invalidated |= m_print->invalidate_steps({ psSkirtBrim });
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial, posEstimateCurledExtrusions });
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions });
invalidated |= m_print->invalidate_steps({ psSkirtBrim });
m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {

View File

@ -459,6 +459,7 @@ public:
void set_task(const TaskParams &params) override { PrintBaseWithState<SLAPrintStep, slapsCount>::set_task_impl(params, m_objects); }
void process() override;
void finalize() override { PrintBaseWithState<SLAPrintStep, slapsCount>::finalize_impl(m_objects); }
void cleanup() override {}
// Returns true if an object step is done on all objects and there's at least one object.
bool is_step_done(SLAPrintObjectStep step) const;
// Returns true if the last step was finished with success.

View File

@ -179,8 +179,8 @@ void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std:
void SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
{
for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw((Polyline)*it, stroke, stroke_width);
for (const ThickPolyline &pl : polylines)
this->draw(Polyline(pl.points), stroke, stroke_width);
}
void SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)

View File

@ -1322,7 +1322,7 @@ namespace SupportMaterialInternal {
// remove the entire bridges and only support the unsupported edges
//FIXME the brided regions are already collected as layerm.bridged. Use it?
for (const Surface &surface : layerm.fill_surfaces())
if (surface.surface_type == stBottomBridge && surface.bridge_angle < 0.0)
if (surface.surface_type == stBottomBridge && surface.bridge_angle >= 0.0)
polygons_append(bridges, surface.expolygon);
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
// Remove the unsupported ends of the bridges from the bridged areas.

View File

@ -3,23 +3,28 @@
#include "ExPolygon.hpp"
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "GCode/ExtrusionProcessor.hpp"
#include "Line.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "Print.hpp"
#include "PrintBase.hpp"
#include "Tesselate.hpp"
#include "libslic3r.h"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"
#include "tbb/blocked_range2d.h"
#include "tbb/parallel_reduce.h"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <utility>
#include <vector>
#include "AABBTreeLines.hpp"
@ -41,12 +46,14 @@ namespace Slic3r {
class ExtrusionLine
{
public:
ExtrusionLine() : a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) {}
ExtrusionLine(const Vec2f &a, const Vec2f &b, const ExtrusionEntity *origin_entity)
: a(a), b(b), len((a - b).norm()), origin_entity(origin_entity)
ExtrusionLine() : a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0), origin_entity(nullptr) {}
ExtrusionLine(const Vec2f &a, const Vec2f &b, float len, const ExtrusionEntity *origin_entity)
: a(a), b(b), len(len), origin_entity(origin_entity)
{}
float length() { return (a - b).norm(); }
ExtrusionLine(const Vec2f &a, const Vec2f &b)
: a(a), b(b), len((a-b).norm()), origin_entity(nullptr)
{}
bool is_external_perimeter() const
{
@ -60,7 +67,8 @@ public:
const ExtrusionEntity *origin_entity;
bool support_point_generated = false;
float malformation = 0.0f;
float form_quality = 1.0f;
float curled_up_height = 0.0f;
static const constexpr int Dim = 2;
using Scalar = Vec2f::Scalar;
@ -121,7 +129,7 @@ public:
Vec3f get_cell_center(const Vec3i &cell_coords) const
{
return origin + cell_coords.cast<float>().cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0));
return origin + cell_coords.cast<float>().cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0f));
}
void take_position(const Vec3f &position) { taken_cells.insert(to_cell_index(to_cell_coords(position))); }
@ -174,48 +182,12 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role)
}
}
// Accumulator of current extrusion path properties
// It remembers unsuported distance and maximum accumulated curvature over that distance.
// Used to determine local stability issues (too long bridges, extrusion curves into air)
struct ExtrusionPropertiesAccumulator
{
float distance = 0; // accumulated distance
float curvature = 0; // accumulated signed ccw angles
float max_curvature = 0; // max absolute accumulated value
void add_distance(float dist) { distance += dist; }
void add_angle(float ccw_angle)
{
curvature += ccw_angle;
max_curvature = std::max(max_curvature, std::abs(curvature));
}
void reset()
{
distance = 0;
curvature = 0;
max_curvature = 0;
}
};
// base function: ((e^(((1)/(x^(2)+1)))-1)/(e-1))
// checkout e.g. here: https://www.geogebra.org/calculator
float gauss(float value, float mean_x_coord, float mean_value, float falloff_speed)
{
float shifted = value - mean_x_coord;
float denominator = falloff_speed * shifted * shifted + 1.0f;
float exponent = 1.0f / denominator;
return mean_value * (std::exp(exponent) - 1.0f) / (std::exp(1.0f) - 1.0f);
}
std::vector<ExtrusionLine> to_short_lines(const ExtrusionEntity *e, float length_limit)
{
assert(!e->is_collection());
Polyline pl = e->as_polyline();
std::vector<ExtrusionLine> lines;
lines.reserve(pl.points.size() * 1.5f);
lines.emplace_back(unscaled(pl.points[0]).cast<float>(), unscaled(pl.points[0]).cast<float>(), e);
for (int point_idx = 0; point_idx < int(pl.points.size()) - 1; ++point_idx) {
Vec2f start = unscaled(pl.points[point_idx]).cast<float>();
Vec2f next = unscaled(pl.points[point_idx + 1]).cast<float>();
@ -227,87 +199,102 @@ std::vector<ExtrusionLine> to_short_lines(const ExtrusionEntity *e, float length
for (int i = 0; i < lines_count; ++i) {
Vec2f a(start + v * (i * step_size));
Vec2f b(start + v * ((i + 1) * step_size));
lines.emplace_back(a, b, e);
lines.emplace_back(a, b, (a-b).norm(), e);
}
}
return lines;
}
std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntity *entity,
const LayerRegion *layer_region,
const LD &prev_layer_lines,
const Params &params)
float estimate_curled_up_height(
const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params)
{
float curled_up_height = 0.0f;
if (fabs(point.distance) < 1.5 * flow_width) {
curled_up_height = 0.85 * prev_line_curled_height;
}
if (point.distance > params.malformation_distance_factors.first * flow_width &&
point.distance < params.malformation_distance_factors.second * flow_width && point.curvature > -0.1f) {
float dist_factor = std::max(point.distance - params.malformation_distance_factors.first * flow_width, 0.01f) /
((params.malformation_distance_factors.second - params.malformation_distance_factors.first) * flow_width);
curled_up_height = layer_height * 2.0f * sqrt(sqrt(dist_factor)) * std::clamp(6.0f * point.curvature, 1.0f, 6.0f);
curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height);
}
return curled_up_height;
}
std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntity *entity,
const LayerRegion *layer_region,
const LD &prev_layer_lines,
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
const Params &params)
{
if (entity->is_collection()) {
std::vector<ExtrusionLine> checked_lines_out;
checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3);
for (const auto *e : static_cast<const ExtrusionEntityCollection *>(entity)->entities) {
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, params);
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params);
checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end());
}
return checked_lines_out;
} else { // single extrusion path, with possible varying parameters
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; }
std::vector<ExtrusionLine> lines = to_short_lines(entity, params.bridge_distance);
ExtrusionPropertiesAccumulator bridging_acc{};
ExtrusionPropertiesAccumulator malformation_acc{};
bridging_acc.add_distance(params.bridge_distance + 1.0f);
const float flow_width = get_flow_width(layer_region, entity->role());
float min_malformation_dist = flow_width - params.malformation_overlap_factor.first * flow_width;
float max_malformation_dist = flow_width - params.malformation_overlap_factor.second * flow_width;
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ExtrusionLine &current_line = lines[line_idx];
if (line_idx + 1 == lines.size() && current_line.b != lines.begin()->a) {
bridging_acc.add_distance(params.bridge_distance + 1.0f);
}
float curr_angle = 0;
if (line_idx + 1 < lines.size()) {
const Vec2f v1 = current_line.b - current_line.a;
const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a;
curr_angle = angle(v1, v2);
}
bridging_acc.add_angle(curr_angle);
// malformation in concave angles does not happen
malformation_acc.add_angle(std::max(0.0f, curr_angle));
if (curr_angle < -20.0 * PI / 180.0) { malformation_acc.reset(); }
auto [dist_from_prev_layer, nearest_line_idx, nearest_point] = prev_layer_lines.signed_distance_from_lines_extra(current_line.b);
if (dist_from_prev_layer < flow_width) {
bridging_acc.reset();
} else {
bridging_acc.add_distance(current_line.len);
// if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports.
bool in_layer_dist_condition = bridging_acc.distance >
params.bridge_distance / (1.0f + (bridging_acc.max_curvature *
params.bridge_distance_decrease_by_curvature_factor / PI));
bool between_layers_condition = dist_from_prev_layer > max_malformation_dist;
if (in_layer_dist_condition && between_layers_condition) {
current_line.support_point_generated = true;
bridging_acc.reset();
}
}
// malformation propagation from below
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx);
current_line.malformation += 0.85 * nearest_line.malformation;
}
// current line maformation
if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) {
float factor = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
(max_malformation_dist - min_malformation_dist);
malformation_acc.add_distance(current_line.len);
current_line.malformation += layer_region->layer()->height * factor * (2.0f + 3.0f * (malformation_acc.max_curvature / PI));
current_line.malformation = std::min(current_line.malformation,
float(layer_region->layer()->height * params.max_malformation_factor));
} else {
malformation_acc.reset();
}
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
return {};
}
return lines;
const float flow_width = get_flow_width(layer_region, entity->role());
std::vector<ExtendedPoint> annotated_points = estimate_points_properties<true, true, false, false>(entity->as_polyline().points,
prev_layer_lines, flow_width,
params.bridge_distance);
std::vector<ExtrusionLine> lines_out;
lines_out.reserve(annotated_points.size());
float bridged_distance = annotated_points.front().position != annotated_points.back().position ? (params.bridge_distance + 1.0f) :
0.0f;
for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
curr_point.position.cast<float>(), line_len, entity};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
ExtrusionLine{};
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
curr_point.distance *= sign;
float max_bridge_len = params.bridge_distance /
((1.0f + std::abs(curr_point.curvature)) * (1.0f + std::abs(curr_point.curvature)));
if (curr_point.distance > 2.0f * flow_width) {
line_out.form_quality = 0.8f;
bridged_distance += line_len;
if (bridged_distance > max_bridge_len) {
line_out.support_point_generated = true;
bridged_distance = 0.0f;
}
} else if (curr_point.distance > flow_width * (1.0 + std::clamp(curr_point.curvature, -0.30f, 0.20f))) {
bridged_distance += line_len;
line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f;
if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) {
line_out.support_point_generated = true;
line_out.form_quality = 0.5f;
bridged_distance = 0.0f;
}
} else {
bridged_distance = 0.0f;
}
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
nearest_prev_layer_line.curled_up_height, params);
lines_out.push_back(line_out);
}
return lines_out;
}
}
@ -484,7 +471,7 @@ public:
float movement_force = params.max_acceleration * mass;
float extruder_conflict_force = params.standard_extruder_conflict_force +
std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force;
std::min(extruded_line.curled_up_height, 1.0f) * params.malformations_additive_conflict_extruder_force;
// section for bed calculations
{
@ -522,7 +509,8 @@ public:
BOOST_LOG_TRIVIAL(debug) << "SSG: bed_movement_arm: " << bed_movement_arm;
BOOST_LOG_TRIVIAL(debug) << "SSG: bed_movement_torque: " << bed_movement_torque;
BOOST_LOG_TRIVIAL(debug) << "SSG: bed_conflict_torque_arm: " << bed_conflict_torque_arm;
BOOST_LOG_TRIVIAL(debug) << "SSG: extruded_line.malformation: " << extruded_line.malformation;
BOOST_LOG_TRIVIAL(debug) << "SSG: extruded_line.curled_up_height: " << extruded_line.curled_up_height;
BOOST_LOG_TRIVIAL(debug) << "SSG: extruded_line.form_quality: " << extruded_line.form_quality;
BOOST_LOG_TRIVIAL(debug) << "SSG: extruder_conflict_force: " << extruder_conflict_force;
BOOST_LOG_TRIVIAL(debug) << "SSG: bed_extruder_conflict_torque: " << bed_extruder_conflict_torque;
BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z;
@ -556,7 +544,7 @@ public:
float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque - conn_yield_torque;
#ifdef DETAILED_DEBUG_LOGS
BOOST_LOG_TRIVIAL(debug) << "bed_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z();
BOOST_LOG_TRIVIAL(debug) << "conn_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z();
BOOST_LOG_TRIVIAL(debug) << "SSG: conn_yield_torque: " << conn_yield_torque;
BOOST_LOG_TRIVIAL(debug) << "SSG: conn_weight_arm: " << conn_weight_arm;
BOOST_LOG_TRIVIAL(debug) << "SSG: conn_weight_torque: " << conn_weight_torque;
@ -590,7 +578,7 @@ std::tuple<ObjectPart, float> build_object_part_from_slice(const LayerSlice &sli
new_object_part.volume += volume;
new_object_part.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), slice_z) * volume;
if (l->id() == 0) { // first layer
if (l->bottom_z() < EPSILON) { // layer attached on bed
float sticking_area = line.len * flow_width;
new_object_part.sticking_area += sticking_area;
Vec2f middle = Vec2f((line.a + line.b) / 2.0f);
@ -627,9 +615,8 @@ std::tuple<ObjectPart, float> build_object_part_from_slice(const LayerSlice &sli
}
}
}
const LayerRegion *thin_fill_region = layer->get_region(island.fill_region_id);
for (const auto &thin_fill_idx : island.thin_fills) {
add_extrusions_to_object(thin_fill_region->thin_fills().entities[thin_fill_idx], perimeter_region);
add_extrusions_to_object(perimeter_region->thin_fills().entities[thin_fill_idx], perimeter_region);
}
}
@ -675,12 +662,12 @@ public:
}
};
Issues check_stability(const PrintObject *po, const Params &params)
SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{
Issues issues{};
SupportPoints supp_points{};
SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points);
ActiveObjectParts active_object_parts{};
LD prev_layer_ext_perim_lines({});
LD prev_layer_ext_perim_lines;
std::unordered_map<size_t, size_t> prev_slice_idx_to_object_part_mapping;
std::unordered_map<size_t, size_t> next_slice_idx_to_object_part_mapping;
@ -688,6 +675,7 @@ Issues check_stability(const PrintObject *po, const Params &params)
std::unordered_map<size_t, SliceConnection> next_slice_idx_to_weakest_connection;
for (size_t layer_idx = 0; layer_idx < po->layer_count(); ++layer_idx) {
cancel_func();
const Layer *layer = po->get_layer(layer_idx);
float bottom_z = layer->bottom_z();
auto create_support_point_position = [bottom_z](const Vec2f &layer_pos) { return Vec3f{layer_pos.x(), layer_pos.y(), bottom_z}; };
@ -767,6 +755,15 @@ Issues check_stability(const PrintObject *po, const Params &params)
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]);
SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx];
std::vector<Linef> boundary_lines;
for (const auto &link : slice.overlaps_below) {
auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]});
boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end());
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_slice_ext_perims_lines{};
current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size());
#ifdef DETAILED_DEBUG_LOGS
@ -774,16 +771,19 @@ Issues check_stability(const PrintObject *po, const Params &params)
#endif
// Function that is used when new support point is generated. It will update the ObjectPart stability, weakest conneciton info,
// and the support presence grid and add the point to the issues.
auto reckon_new_support_point = [&part, &weakest_conn, &issues, &supports_presence_grid, &params,
auto reckon_new_support_point = [&part, &weakest_conn, &supp_points, &supports_presence_grid, &params,
&layer_idx](const Vec3f &support_point, float force, const Vec2f &dir) {
if (supports_presence_grid.position_taken(support_point) || layer_idx <= 1) { return; }
if ((supports_presence_grid.position_taken(support_point) && force > 0) || layer_idx <= 1) {
return;
}
float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI);
part.add_support_point(support_point, area);
float radius = params.support_points_interface_radius;
issues.support_points.emplace_back(support_point, force, radius, dir);
supports_presence_grid.take_position(support_point);
supp_points.emplace_back(support_point, force, radius, dir);
if (force > 0) {
supports_presence_grid.take_position(support_point);
}
if (weakest_conn.area > EPSILON) { // Do not add it to the weakest connection if it is not valid - does not exist
weakest_conn.area += area;
weakest_conn.centroid_accumulator += support_point * area;
@ -802,7 +802,7 @@ Issues check_stability(const PrintObject *po, const Params &params)
const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx];
if (entity->role() == erBridgeInfill) {
for (const ExtrusionLine &bridge :
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, params)) {
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines,prev_layer_boundary, params)) {
if (bridge.support_point_generated) {
reckon_new_support_point(create_support_point_position(bridge.b), -EPSILON, Vec2f::Zero());
}
@ -815,7 +815,7 @@ Issues check_stability(const PrintObject *po, const Params &params)
for (const auto &perimeter_idx : island.perimeters) {
const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx];
std::vector<ExtrusionLine> perims = check_extrusion_entity_stability(entity, perimeter_region,
prev_layer_ext_perim_lines, params);
prev_layer_ext_perim_lines,prev_layer_boundary, params);
for (const ExtrusionLine &perim : perims) {
if (perim.support_point_generated) {
reckon_new_support_point(create_support_point_position(perim.b), -EPSILON, Vec2f::Zero());
@ -829,13 +829,13 @@ Issues check_stability(const PrintObject *po, const Params &params)
float unchecked_dist = params.min_distance_between_support_points + 1.0f;
for (const ExtrusionLine &line : current_slice_ext_perims_lines) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.malformation < 0.3f) || line.len == 0) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < 0.3f) || line.len < EPSILON) {
unchecked_dist += line.len;
} else {
unchecked_dist = line.len;
Vec2f pivot_site_search_point = Vec2f(line.b + (line.b - line.a).normalized() * 300.0f);
auto [dist, nidx,
nearest_point] = current_slice_lines_distancer.signed_distance_from_lines_extra(pivot_site_search_point);
nearest_point] = current_slice_lines_distancer.distance_from_lines_extra<false>(pivot_site_search_point);
Vec3f support_point = create_support_point_position(nearest_point);
auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, bottom_z, params);
if (force > 0) { reckon_new_support_point(support_point, force, (line.b - line.a).normalized()); }
@ -846,11 +846,11 @@ Issues check_stability(const PrintObject *po, const Params &params)
} // slice iterations
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
} // layer iterations
return issues;
return supp_points;
}
#ifdef DEBUG_FILES
void debug_export(Issues issues, std::string file_name)
void debug_export(SupportPoints support_points, std::string file_name)
{
Slic3r::CNumericLocalesSetter locales_setter;
{
@ -860,13 +860,13 @@ void debug_export(Issues issues, std::string file_name)
return;
}
for (size_t i = 0; i < issues.support_points.size(); ++i) {
if (issues.support_points[i].force <= 0) {
fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), issues.support_points[i].position(1),
issues.support_points[i].position(2), 0.0, 1.0, 0.0);
for (size_t i = 0; i < support_points.size(); ++i) {
if (support_points[i].force <= 0) {
fprintf(fp, "v %f %f %f %f %f %f\n", support_points[i].position(0), support_points[i].position(1),
support_points[i].position(2), 0.0, 1.0, 0.0);
} else {
fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), issues.support_points[i].position(1),
issues.support_points[i].position(2), 1.0, 0.0, 0.0);
fprintf(fp, "v %f %f %f %f %f %f\n", support_points[i].position(0), support_points[i].position(1),
support_points[i].position(2), 1.0, 0.0, 0.0);
}
}
@ -878,113 +878,84 @@ void debug_export(Issues issues, std::string file_name)
// std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
// return {};
// }
Issues full_search(const PrintObject *po, const Params &params)
SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{
Issues issues = check_stability(po, params);
SupportPoints supp_points = check_stability(po, cancel_func, params);
#ifdef DEBUG_FILES
debug_export(issues, "issues");
debug_export(supp_points, "issues");
#endif
return issues;
return supp_points;
}
struct LayerCurlingEstimator
{
LD prev_layer_lines = LD({});
Params params;
std::function<float(const ExtrusionLine &)> flow_width_getter;
LayerCurlingEstimator(std::function<float(const ExtrusionLine &)> flow_width_getter, const Params &params)
: flow_width_getter(flow_width_getter), params(params)
{}
void estimate_curling(std::vector<ExtrusionLine> &extrusion_lines, Layer *l)
{
ExtrusionPropertiesAccumulator malformation_acc{};
for (size_t line_idx = 0; line_idx < extrusion_lines.size(); ++line_idx) {
ExtrusionLine &current_line = extrusion_lines[line_idx];
float flow_width = flow_width_getter(current_line);
float min_malformation_dist = flow_width - params.malformation_overlap_factor.first * flow_width;
float max_malformation_dist = flow_width - params.malformation_overlap_factor.second * flow_width;
float curr_angle = 0;
if (line_idx + 1 < extrusion_lines.size()) {
const Vec2f v1 = current_line.b - current_line.a;
const Vec2f v2 = extrusion_lines[line_idx + 1].b - extrusion_lines[line_idx + 1].a;
curr_angle = angle(v1, v2);
}
// malformation in concave angles does not happen
malformation_acc.add_angle(std::max(0.0f, curr_angle));
if (curr_angle < -20.0 * PI / 180.0) { malformation_acc.reset(); }
auto [dist_from_prev_layer, nearest_line_idx, nearest_point] = prev_layer_lines.signed_distance_from_lines_extra(current_line.b);
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx);
current_line.malformation += 0.9 * nearest_line.malformation;
}
if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) {
float factor = 0.5f + 0.5f * std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
(max_malformation_dist - min_malformation_dist);
malformation_acc.add_distance(current_line.len);
current_line.malformation += l->height * factor * (1.5f + 3.0f * (malformation_acc.max_curvature / PI));
current_line.malformation = std::min(current_line.malformation, float(l->height * params.max_malformation_factor));
} else {
malformation_acc.reset();
}
}
for (const ExtrusionLine &line : extrusion_lines) {
if (line.malformation > 0.3f) { l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); }
}
prev_layer_lines = LD(extrusion_lines);
}
};
void estimate_supports_malformations(SupportLayerPtrs &layers, float supports_flow_width, const Params &params)
void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, const Params &params)
{
#ifdef DEBUG_FILES
FILE *debug_file = boost::nowide::fopen(debug_out_path("supports_malformations.obj").c_str(), "w");
FILE *full_file = boost::nowide::fopen(debug_out_path("supports_full.obj").c_str(), "w");
#endif
auto flow_width_getter = [=](const ExtrusionLine& l) {
return supports_flow_width;
};
LayerCurlingEstimator lce{flow_width_getter, params};
AABBTreeLines::LinesDistancer<ExtrusionLine> prev_layer_lines{};
for (SupportLayer *l : layers) {
std::vector<ExtrusionLine> extrusion_lines;
std::vector<ExtrusionLine> current_layer_lines;
for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) {
Polyline pl = extrusion->as_polyline();
Polygon pol(pl.points);
Polygon pol(pl.points);
pol.make_counter_clockwise();
pl = pol.split_at_first_point();
for (int point_idx = 0; point_idx < int(pl.points.size() - 1); ++point_idx) {
Vec2f start = unscaled(pl.points[point_idx]).cast<float>();
Vec2f next = unscaled(pl.points[point_idx + 1]).cast<float>();
ExtrusionLine line{start, next, extrusion};
extrusion_lines.push_back(line);
auto annotated_points = estimate_points_properties<true, true, false, false>(pol.points, prev_layer_lines, flow_width);
for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
curr_point.position.cast<float>(), line_len, extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
ExtrusionLine{};
Vec2f v1 = (nearest_prev_layer_line.b - nearest_prev_layer_line.a);
Vec2f v2 = (curr_point.position.cast<float>() - nearest_prev_layer_line.a);
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
if (d > 0) {
curr_point.distance *= -1.0f;
}
line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width,
nearest_prev_layer_line.curled_up_height, params);
current_layer_lines.push_back(line_out);
}
}
lce.estimate_curling(extrusion_lines, l);
for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > 0.3f) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)});
}
}
#ifdef DEBUG_FILES
for (const ExtrusionLine &line : extrusion_lines) {
if (line.malformation > 0.3f) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_malformation_factor, line.malformation);
for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > 0.3f) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
}
}
for (const ExtrusionLine &line : current_layer_lines) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
}
#endif
prev_layer_lines = LD{current_layer_lines};
}
#ifdef DEBUG_FILES
fclose(debug_file);
fclose(full_file);
#endif
}
@ -992,52 +963,71 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
{
#ifdef DEBUG_FILES
FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w");
FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w");
#endif
auto flow_width_getter = [](const ExtrusionLine &l) { return 0.0; };
LayerCurlingEstimator lce{flow_width_getter, params};
LD prev_layer_lines{};
for (Layer *l : layers) {
if (l->regions().empty()) {
continue;
}
struct Visitor {
Visitor(const Params &params) : params(params) {}
void recursive_do(const ExtrusionEntityCollection &collection, const LayerRegion *region) {
for (const ExtrusionEntity* entity : collection.entities)
if (entity->is_collection())
this->recursive_do(*static_cast<const ExtrusionEntityCollection*>(entity), region);
else {
append(extrusion_lines, to_short_lines(entity, params.bridge_distance));
extrusions_widths.emplace(entity, get_flow_width(region, entity->role()));
}
std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>();
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_layer_lines;
for (const LayerRegion *layer_region : l->regions()) {
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
Points extrusion_pts;
extrusion->collect_points(extrusion_pts);
float flow_width = get_flow_width(layer_region, extrusion->role());
auto annotated_points = estimate_points_properties<true, false, false, false>(extrusion_pts, prev_layer_lines, flow_width,
params.bridge_distance);
for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
curr_point.position.cast<float>(), line_len, extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
ExtrusionLine{};
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f :
1.0f;
curr_point.distance *= sign;
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
nearest_prev_layer_line.curled_up_height, params);
current_layer_lines.push_back(line_out);
}
}
const Params &params;
std::unordered_map<const ExtrusionEntity*, float> extrusions_widths;
std::vector<ExtrusionLine> extrusion_lines;
} visitor(params);
}
for (const LayerRegion *region : l->regions())
visitor.recursive_do(region->perimeters(), region);
lce.flow_width_getter = [&](const ExtrusionLine &l) { return visitor.extrusions_widths[l.origin_entity]; };
lce.estimate_curling(visitor.extrusion_lines, l);
for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > 0.3f) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)});
}
}
#ifdef DEBUG_FILES
for (const ExtrusionLine &line : extrusion_lines) {
if (line.malformation > 0.3f) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_malformation_factor, line.malformation);
for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > 0.3f) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
}
}
for (const ExtrusionLine &line : current_layer_lines) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
}
#endif
prev_layer_lines = LD{current_layer_lines};
}
#ifdef DEBUG_FILES
fclose(debug_file);
fclose(full_file);
#endif
}
} //SupportableIssues End
}
} // namespace SupportSpotsGenerator
} // namespace Slic3r

View File

@ -1,8 +1,11 @@
#ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
#define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
#include "libslic3r/Print.hpp"
#include "Layer.hpp"
#include "Line.hpp"
#include "PrintBase.hpp"
#include <boost/log/trivial.hpp>
#include <vector>
namespace Slic3r {
@ -20,19 +23,20 @@ struct Params {
filament_type = std::string("PLA");
} else {
filament_type = filament_types[0];
BOOST_LOG_TRIVIAL(debug)
<< "SupportSpotsGenerator: applying filament type: " << filament_type;
}
}
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
const float bridge_distance = 12.0f; //mm
const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) )
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.50, -0.1 };
const float max_malformation_factor = 10.0f;
const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.4, 1.2 };
const float max_curled_height_factor = 10.0f;
const float min_distance_between_support_points = 3.0f; //mm
const float support_points_interface_radius = 1.5f; // mm
const float connections_min_considerable_area = 1.5f; //mm^2
const float min_distance_to_allow_local_supports = 2.0f; //mm
const float min_distance_to_allow_local_supports = 1.0f; //mm
std::string filament_type;
const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position.
@ -40,7 +44,7 @@ struct Params {
const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important
const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials.
const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... );
const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments
const float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments
// MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface
double get_bed_adhesion_yield_strength() const {
@ -67,19 +71,17 @@ struct SupportPoint {
Vec2f direction;
};
struct Issues {
std::vector<SupportPoint> support_points;
};
using SupportPoints = std::vector<SupportPoint>;
struct Malformations {
std::vector<Lines> layers; //for each layer
};
// std::vector<size_t> quick_search(const PrintObject *po, const Params &params);
Issues full_search(const PrintObject *po, const Params &params);
SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params);
void estimate_supports_malformations(SupportLayerPtrs &layers, float supports_flow_width, const Params &params);
void estimate_malformations(LayerPtrs &layers, const Params &params);
void estimate_supports_malformations(std::vector<SupportLayer*> &layers, float supports_flow_width, const Params &params);
void estimate_malformations(std::vector<Layer*> &layers, const Params &params);
} // namespace SupportSpotsGenerator
}

View File

@ -55,6 +55,8 @@
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_6_0_ALPHA1)
// Enable editing volumes transformation in world coordinates and instances in local coordinates
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_6_0_ALPHA1)
// Shows an imgui dialog containing the matrices of the selected volumes
#define ENABLE_WORLD_COORDINATE_DEBUG (0 && ENABLE_WORLD_COORDINATE)
// Enable alternative version of file_wildcards()
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)

View File

@ -622,7 +622,7 @@ std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::funct
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
}
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask)
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask)
{
return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
}
@ -977,8 +977,10 @@ indexed_triangle_set its_make_cone(double r, double h, double fa)
vertices.emplace_back(Vec3f(0., 0., h));
size_t i = 0;
const auto vec = Eigen::Vector2f(0, float(r));
for (double angle=0; angle<2*PI; angle+=fa) {
vertices.emplace_back(r*std::cos(angle), r*std::sin(angle), 0.);
Vec2f p = Eigen::Rotation2Df(angle) * vec;
vertices.emplace_back(Vec3f(p(0), p(1), 0.f));
if (angle > 0.) {
facets.emplace_back(0, i+2, i+1);
facets.emplace_back(1, i+1, i+2);
@ -1013,58 +1015,121 @@ indexed_triangle_set its_make_pyramid(float base, float height)
// Generates mesh for a sphere centered about the origin, using the generated angle
// to determine the granularity.
// Default angle is 1 degree.
//FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
indexed_triangle_set its_make_sphere(double radius, double fa)
{
int sectorCount = int(ceil(2. * M_PI / fa));
int stackCount = int(ceil(M_PI / fa));
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
// First build an icosahedron (taken from http://www.songho.ca/opengl/gl_sphere.html)
indexed_triangle_set mesh;
const float PI = 3.1415926f;
const float H_ANGLE = PI / 180 * 72; // 72 degree = 360 / 5
const float V_ANGLE = atanf(1.0f / 2); // elevation = 26.565 degree
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++ i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(z)));
else
for (int j = 0; j < sectorCount; ++ j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& indices = mesh.indices;
vertices.resize(12);
indices.reserve(20);
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++ i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++ j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
float z, xy;
float hAngle1 = -PI / 2 - H_ANGLE / 2;
vertices[0] = stl_vertex(0, 0, radius); // the first top vertex at (0, 0, r)
for (int i = 1; i <= 5; ++i) {
z = radius * sinf(V_ANGLE);
xy = radius * cosf(V_ANGLE);
vertices[i] = stl_vertex(xy * cosf(hAngle1), xy * sinf(hAngle1), z);
vertices[i+5] = stl_vertex(xy * cosf(hAngle1 + H_ANGLE / 2), xy * sinf(hAngle1 + H_ANGLE / 2), -z);
hAngle1 += H_ANGLE;
indices.emplace_back(stl_triangle_vertex_indices(i, i < 5 ? i+1 : 1, 0));
indices.emplace_back(stl_triangle_vertex_indices(i, i+5, i < 5 ? i+1 : 1));
indices.emplace_back(stl_triangle_vertex_indices(i+5, i+6 < 11 ? i+6 : 6, i+6 < 11 ? i+1 : 1));
indices.emplace_back(stl_triangle_vertex_indices(i+5, 11, i+6 < 11 ? i+6 : 6));
}
vertices[11] = stl_vertex(0, 0, -radius); // the last bottom vertex at (0, 0, -r)
// We have a beautiful icosahedron. Now subdivide the triangles.
std::vector<Vec3i> neighbors = its_face_neighbors(mesh); // This is cheap, the mesh is small.
const double side_len_limit = radius * fa;
const double side_len = (vertices[1] - vertices[0]).norm();
const int iterations = std::ceil(std::log2(side_len / side_len_limit));
indices.reserve(indices.size() * std::pow(4, iterations));
vertices.reserve(vertices.size() * std::pow(2, iterations));
struct DividedEdge {
int neighbor = -1;
int middle_vertex_idx;
std::pair<int, int> children_idxs;
};
for (int iter=0; iter<iterations; ++iter) {
std::vector<std::array<DividedEdge, 3>> divided_triangles(indices.size());
std::vector<Vec3i> new_neighbors(4*indices.size());
size_t orig_indices_size = indices.size();
for (int i=0; i<orig_indices_size; ++i) { // iterate over all old triangles
// We are going to split this triangle. Let's foresee what will be the indices
// of the new internal triangles along individual edges.
int last_triangle_idx = indices.size()-1;
std::array<std::pair<int, int>, 3> edge_children = { std::make_pair(i,last_triangle_idx + 2),
std::make_pair(last_triangle_idx + 2,last_triangle_idx + 3),
std::make_pair(last_triangle_idx + 3,i) };
std::array<int, 3> middle_vertices_idxs;
std::array<std::pair<int, int>, 3> new_neighbors_per_edge;
for (int n=0; n<3; ++n) { // for all three edges
const int edge_neighbor = neighbors[i][n];
if (divided_triangles[edge_neighbor][0].neighbor == -1) {
// This n-th edge is not yet divided. Divide it now.
vertices.emplace_back(0.5 * (vertices[indices[i][n]] + vertices[indices[i][n == 2 ? 0 : n+1]]));
vertices.back() *= radius / vertices.back().norm();
middle_vertices_idxs[n] = vertices.size()-1;
// Save information about what we did.
int j = -1;
while (divided_triangles[i][++j].neighbor != -1);
divided_triangles[i][j] = { edge_neighbor, int(vertices.size()-1), edge_children[n] };
new_neighbors_per_edge[n] = std::make_pair(-1,-1);
} else {
// This edge is already divided. Get the index of the middle point.
int j = -1;
while (divided_triangles[edge_neighbor][++j].neighbor != i);
middle_vertices_idxs[n] = divided_triangles[edge_neighbor][j].middle_vertex_idx;
new_neighbors_per_edge[n] = divided_triangles[edge_neighbor][j].children_idxs;
std::swap(new_neighbors_per_edge[n].first, new_neighbors_per_edge[n].second);
// We have saved the middle-point. We are looking for edges leading to/from it.
int idx = -1; while (indices[new_neighbors_per_edge[n].first][++idx] != middle_vertices_idxs[n]);
new_neighbors[new_neighbors_per_edge[n].first][idx] = edge_children[n].first;
new_neighbors[new_neighbors_per_edge[n].second][idx] = edge_children[n].second;
}
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
// Add three new triangles, reindex the old one.
const int last_index = indices.size() - 1;
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[0], middle_vertices_idxs[1], middle_vertices_idxs[2]));
new_neighbors[indices.size()-1] = Vec3i(last_index+2, last_index+3, i);
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[0], indices[i][1], middle_vertices_idxs[1]));
new_neighbors[indices.size()-1] = Vec3i(new_neighbors_per_edge[0].second, new_neighbors_per_edge[1].first, last_index+1);
indices.emplace_back(stl_triangle_vertex_indices(middle_vertices_idxs[2], middle_vertices_idxs[1], indices[i][2]));
new_neighbors[indices.size()-1] = Vec3i(last_index+1, new_neighbors_per_edge[1].second, new_neighbors_per_edge[2].first);
indices[i][1] = middle_vertices_idxs[0];
indices[i][2] = middle_vertices_idxs[2];
new_neighbors[i] = Vec3i(new_neighbors_per_edge[0].first, last_index+1, new_neighbors_per_edge[2].second);
}
neighbors = std::move(new_neighbors);
}
return mesh;
}
@ -1089,7 +1154,7 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j;
double sectorAngle = sectorStep * j + 0.25 * M_PI;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}

View File

@ -188,7 +188,7 @@ private:
// Used for chaining slice lines into polygons.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask);
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);

View File

@ -137,18 +137,41 @@ enum class FacetSliceType {
Cutting = 2
};
// Return true, if the facet has been sliced and line_out has been filled.
static FacetSliceType slice_facet(
// Z height of the slice in XY plane. Scaled or unscaled (same as vertices[].z()).
float slice_z,
// 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
const stl_vertex *vertices,
const stl_triangle_vertex_indices &indices,
const Vec3i &edge_ids,
const int idx_vertex_lowest,
const bool horizontal,
IntersectionLine &line_out)
// Convert an int32_t scaled coordinate into an unscaled 3D floating point coordinate (mesh vertex).
template<typename T>
inline Vec3f contour_point_to_v3f(const Point &pt, const T z)
{
return to_3d(
// unscale using doubles for higher accuracy
unscaled<double>(pt).
// then convert to floats
cast<float>(),
float(z));
}
// Convert 2D projection of an int32_t scaled coordinate into an unscaled 3D floating point coordinate (mesh vertex).
template<typename Derived>
inline Point v3f_scaled_to_contour_point(const Eigen::MatrixBase<Derived> &v)
{
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) >= 2, "v3f_scaled_to_contour_point(): Not a 2D or 3D vector.");
using T = typename Derived::Scalar;
return { coord_t(std::floor(v.x() + T(0.5))), coord_t(std::floor(v.y() + T(0.5))) };
}
// Return true, if the facet has been sliced and line_out has been filled.
template<typename T>
inline FacetSliceType slice_facet(
// Z height of the slice in XY plane. Scaled or unscaled (same as vertices[].z()).
T slice_z,
// 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
const Eigen::Matrix<T, 3, 1, Eigen::DontAlign> *vertices,
const stl_triangle_vertex_indices &indices,
const Vec3i &edge_ids,
const int idx_vertex_lowest,
const bool horizontal,
IntersectionLine &line_out)
{
using Vector = Eigen::Matrix<T, 3, 1, Eigen::DontAlign>;
IntersectionPoint points[3];
size_t num_points = 0;
auto point_on_layer = size_t(-1);
@ -158,7 +181,7 @@ static FacetSliceType slice_facet(
// (external on the right of the line)
for (int j = 0; j < 3; ++ j) { // loop through facet edges
int edge_id;
const stl_vertex *a, *b;
const Vector *a, *b;
int a_id, b_id;
{
int k = (idx_vertex_lowest + j) % 3;
@ -174,16 +197,16 @@ static FacetSliceType slice_facet(
if (a->z() == slice_z && b->z() == slice_z) {
// Edge is horizontal and belongs to the current layer.
// The following rotation of the three vertices may not be efficient, but this branch happens rarely.
const stl_vertex &v0 = vertices[0];
const stl_vertex &v1 = vertices[1];
const stl_vertex &v2 = vertices[2];
const Vector &v0 = vertices[0];
const Vector &v1 = vertices[1];
const Vector &v2 = vertices[2];
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = FacetSliceType::Slicing;
if (horizontal) {
// All three vertices are aligned with slice_z.
line_out.edge_type = IntersectionLine::FacetEdgeType::Horizontal;
result = FacetSliceType::Cutting;
double normal = (v1.x() - v0.x()) * (v2.y() - v1.y()) - (v1.y() - v0.y()) * (v2.x() - v1.x());
double normal = cross2((to_2d(v1) - to_2d(v0)).template cast<double>(), (to_2d(v2) - to_2d(v1)).template cast<double>());
if (normal < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b);
@ -205,10 +228,8 @@ static FacetSliceType slice_facet(
} else
line_out.edge_type = IntersectionLine::FacetEdgeType::Bottom;
}
line_out.a.x() = a->x();
line_out.a.y() = a->y();
line_out.b.x() = b->x();
line_out.b.y() = b->y();
line_out.a = v3f_scaled_to_contour_point(*a);
line_out.b = v3f_scaled_to_contour_point(*b);
line_out.a_id = a_id;
line_out.b_id = b_id;
assert(line_out.a != line_out.b);
@ -220,8 +241,7 @@ static FacetSliceType slice_facet(
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point.x() = a->x();
point.y() = a->y();
static_cast<Point&>(point) = v3f_scaled_to_contour_point(*a);
point.point_id = a_id;
}
} else if (b->z() == slice_z) {
@ -229,8 +249,7 @@ static FacetSliceType slice_facet(
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point.x() = b->x();
point.y() = b->y();
static_cast<Point&>(point) = v3f_scaled_to_contour_point(*b);
point.point_id = b_id;
}
} else if ((a->z() < slice_z && b->z() > slice_z) || (b->z() < slice_z && a->z() > slice_z)) {
@ -270,16 +289,10 @@ static FacetSliceType slice_facet(
}
#else
// Just clamp the intersection point to source triangle edge.
if (t <= 0.) {
point.x() = a->x();
point.y() = a->y();
} else if (t >= 1.) {
point.x() = b->x();
point.y() = b->y();
} else {
point.x() = coord_t(floor(double(a->x()) + (double(b->x()) - double(a->x())) * t + 0.5));
point.y() = coord_t(floor(double(a->y()) + (double(b->y()) - double(a->y())) * t + 0.5));
}
static_cast<Point&>(point) =
t <= 0. ? v3f_scaled_to_contour_point(*a) :
t >= 1. ? v3f_scaled_to_contour_point(*b) :
v3f_scaled_to_contour_point(a->template head<2>().template cast<double>() * (1. - t) + b->template head<2>().template cast<double>() * t + Vec2d(0.5, 0.5));
point.edge_id = edge_id;
++ num_points;
#endif
@ -1805,7 +1818,7 @@ Polygons slice_mesh(
{
bool trafo_identity = is_identity(params.trafo);
Transform3f tf;
std::vector<bool> face_mask(mesh.indices.size(), false);
std::vector<char> face_mask(mesh.indices.size(), 0);
{
// 1) Mark vertices as below or above the slicing plane.
@ -2182,28 +2195,44 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
FacetSliceType slice_type = FacetSliceType::NoSlice;
if (z > min_z - EPSILON && z < max_z + EPSILON) {
Vec3f vertices_scaled[3];
Vec3d vertices_scaled[3];
for (int i = 0; i < 3; ++ i) {
const Vec3f &src = vertices[i];
Vec3f &dst = vertices_scaled[i];
dst.x() = scale_(src.x());
dst.y() = scale_(src.y());
Vec3d &dst = vertices_scaled[i];
dst.x() = scaled<double>(src.x());
dst.y() = scaled<double>(src.y());
dst.z() = src.z();
}
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
slice_type = slice_facet(double(z), vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
}
if (slice_type != FacetSliceType::NoSlice) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == IntersectionLine::FacetEdgeType::Top) {
lower_lines.emplace_back(line);
lower_slice_vertices.emplace_back(line.a_id);
lower_slice_vertices.emplace_back(line.b_id);
if (lower) {
lower_lines.emplace_back(line);
if (triangulate_caps) {
// Snap these vertices to coord_t grid, so that they will be matched with the vertices produced
// by triangulating opening on the cut.
lower->vertices[line.a_id] = contour_point_to_v3f(line.a, z);
lower->vertices[line.b_id] = contour_point_to_v3f(line.b, z);
}
}
} else if (line.edge_type == IntersectionLine::FacetEdgeType::Bottom) {
upper_lines.emplace_back(line);
upper_slice_vertices.emplace_back(line.a_id);
upper_slice_vertices.emplace_back(line.b_id);
} else if (line.edge_type == IntersectionLine::FacetEdgeType::General) {
if (upper) {
upper_lines.emplace_back(line);
if (triangulate_caps) {
// Snap these vertices to coord_t grid, so that they will be matched with the vertices produced
// by triangulating opening on the cut.
upper->vertices[line.a_id] = contour_point_to_v3f(line.a, z);
upper->vertices[line.b_id] = contour_point_to_v3f(line.b, z);
}
}
} else if (line.edge_type == IntersectionLine::FacetEdgeType::General && triangulate_caps) {
lower_lines.emplace_back(line);
upper_lines.emplace_back(line);
}
@ -2235,11 +2264,11 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
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) {
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v0v1 = contour_point_to_v3f(line.a, z);
v2v0 = contour_point_to_v3f(line.b, z);
} else {
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v0v1 = contour_point_to_v3f(line.b, z);
v2v0 = contour_point_to_v3f(line.a, z);
}
const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[iv];

View File

@ -6,7 +6,6 @@ namespace Slic3r {
TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh, const Transform3d& mesh_transform) :
mesh(mesh), mesh_transform(mesh_transform), selector(mesh), triangles_tree(
AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) {
}
void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &origin, float radius) {

View File

@ -241,7 +241,7 @@ Triangulation::Indices Triangulation::triangulate(const ExPolygon &expolygon){
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons){
Points pts = to_points(expolygons);
Points d_pts = collect_duplications(pts);
Points d_pts = collect_duplicates(pts);
if (d_pts.empty()) return triangulate(expolygons, pts);
Changes changes = create_changes(pts, d_pts);
@ -262,7 +262,7 @@ Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons,
{
assert(count_points(expolygons) == points.size());
// when contain duplicit coordinate in points will not work properly
assert(collect_duplications(points).empty());
assert(collect_duplicates(points).empty());
HalfEdges edges;
edges.reserve(points.size());

View File

@ -267,8 +267,6 @@ class ScopeGuard
{
public:
typedef std::function<void()> Closure;
private:
// bool committed;
Closure closure;
public:

View File

@ -113,7 +113,6 @@ inline void append(std::vector<T>& dest, const std::vector<T>& src)
dest = src; // copy
else
dest.insert(dest.end(), src.begin(), src.end());
// NOTE: insert reserve space when needed
}
template <typename T>

View File

@ -956,7 +956,8 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
}
// Definition of escape symbols https://www.w3.org/TR/REC-xml/#AVNormalize
// During the read of xml attribute normalization of white spaces is applied
// Soo for not lose white space character it is escaped before store
std::string xml_escape_double_quotes_attribute_value(std::string text)
{
std::string::size_type pos = 0;

View File

@ -7922,6 +7922,40 @@ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, cha
return n + 1;
}
mz_uint mz_zip_reader_get_filename_from_extra(mz_zip_archive* pZip, mz_uint file_index, char* buffer, mz_uint extra_buf_size)
{
if (extra_buf_size == 0)
return 0;
mz_uint nf;
mz_uint ne;
const mz_uint8* p = mz_zip_get_cdh(pZip, file_index);
if (!p)
{
if (extra_buf_size)
buffer[0] = '\0';
mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
return 0;
}
nf = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
ne = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
int copy = 0;
char const* p_nf = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + nf;
char const* e = p_nf + ne + 1;
while (p_nf + 4 < e) {
mz_uint16 len = ((mz_uint16)p_nf[2]) | ((mz_uint16)p_nf[3] << 8);
if (p_nf[0] == '\x75' && p_nf[1] == '\x70' && len >= 5 && p_nf + 4 + len < e && p_nf[4] == '\x01') {
mz_uint length = MZ_MIN(len - 5, extra_buf_size - 1);
memcpy(buffer, p_nf + 9, length);
return length;
}
else {
p_nf += 4 + len;
}
}
return 0;
}
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
{
return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);

View File

@ -1166,6 +1166,9 @@ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, cha
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
/* Retrieves the filename of an archive file entry from EXTRA ID. */
mz_uint mz_zip_reader_get_filename_from_extra(mz_zip_archive * pZip, mz_uint file_index, char* buffer, mz_uint extra_buf_size);
/* Returns detailed information about an archive file entry. */
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);

View File

@ -110,6 +110,17 @@
<string>Alternate</string>
</dict>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>PrusaSlicer Downloads</string>
<key>CFBundleURLSchemes</key>
<array>
<string>prusaslicer</string>
</array>
</dict>
</array>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>NSPrincipalClass</key>

View File

@ -234,6 +234,12 @@ set(SLIC3R_GUI_SOURCES
GUI/DesktopIntegrationDialog.hpp
GUI/HintNotification.cpp
GUI/HintNotification.hpp
GUI/FileArchiveDialog.cpp
GUI/FileArchiveDialog.hpp
GUI/Downloader.cpp
GUI/Downloader.hpp
GUI/DownloaderFileGet.cpp
GUI/DownloaderFileGet.hpp
Utils/AppUpdater.cpp
Utils/AppUpdater.hpp
Utils/Http.cpp

View File

@ -39,8 +39,8 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
#else
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour
#endif
dc.SetPen(*new wxPen(color, 1, wxPENSTYLE_SOLID));
dc.SetBrush(*new wxBrush(color, wxBRUSHSTYLE_SOLID));
dc.SetPen(wxPen(color, 1, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(color, wxBRUSHSTYLE_SOLID));
auto rect = GetUpdateRegion().GetBox();
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
}
@ -75,11 +75,15 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
// draw bed fill
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
wxPointList pt_list;
for (auto pt : shape)
{
Point pt_pix = to_pixels(pt, ch);
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
const size_t pt_cnt = shape.size();
std::vector<wxPoint> points;
points.reserve(pt_cnt);
for (const auto& shape_pt : shape) {
Point pt_pix = to_pixels(shape_pt, ch);
points.emplace_back(wxPoint(pt_pix(0), pt_pix(1)));
pt_list.Append(&points.back());
}
dc.DrawPolygon(&pt_list, 0, 0);

View File

@ -127,7 +127,9 @@ void GLVolume::SinkingContours::update()
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
}
}
m_model.init_from(std::move(init_data));
if (init_data.vertices_count() > 0)
m_model.init_from(std::move(init_data));
}
void GLVolume::NonManifoldEdges::render()
@ -437,9 +439,9 @@ std::vector<int> GLVolumeCollection::load_object(
int GLVolumeCollection::load_object_volume(
const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx)
int obj_idx,
int volume_idx,
int instance_idx)
{
const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id();
@ -448,12 +450,16 @@ int GLVolumeCollection::load_object_volume(
this->volumes.emplace_back(new GLVolume());
GLVolume& v = *this->volumes.back();
v.set_color(color_from_model_volume(*model_volume));
// apply printable value from the instance
v.printable = instance->printable;
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(*mesh, true);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
if (m_use_raycasters)
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
#else
v.model.init_from(*mesh);
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
if (m_use_raycasters)
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part()) {

View File

@ -397,6 +397,7 @@ private:
Slope m_slope;
bool m_show_sinking_contours{ false };
bool m_show_non_manifold_edges{ true };
bool m_use_raycasters{ true };
public:
GLVolumePtrs volumes;
@ -435,6 +436,7 @@ public:
bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume* vol : this->volumes) vol->set_range(low, high); }
void set_use_raycasters(bool value) { m_use_raycasters = value; }
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }

View File

@ -251,6 +251,9 @@ void BackgroundSlicingProcess::thread_proc()
(m_state == STATE_CANCELED) ? SlicingProcessCompletedEvent::Cancelled :
exception ? SlicingProcessCompletedEvent::Error : SlicingProcessCompletedEvent::Finished, exception);
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
// Cancelled by the user, not internally, thus cleanup() was not called yet.
// Otherwise cleanup() is called from Print::apply()
m_print->cleanup();
}
m_print->restart();
lck.unlock();

View File

@ -123,8 +123,8 @@ void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
break;
default:
// rectangle, convex, concave...
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ - to_2d(m_build_volume.bounding_volume().min) });
optgroup->set_value("rect_size" , to_2d(m_build_volume.bounding_volume().size()));
optgroup->set_value("rect_origin" , to_2d(-1 * m_build_volume.bounding_volume().min));
}
}
@ -425,8 +425,6 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points)
m_loaded_shape = points.values;
update_shape();
return;
}
void BedShapePanel::update_preview()

View File

@ -1,5 +1,6 @@
#include "ButtonsDescription.hpp"
#include <wx/sizer.h>
#include <wx/string.h>
#include <wx/stattext.h>
#include <wx/statbmp.h>
#include <wx/clrpicker.h>
@ -7,11 +8,82 @@
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "I18N.hpp"
#include "OptionsGroup.hpp"
#include "wxExtensions.hpp"
#include "BitmapCache.hpp"
namespace Slic3r {
namespace GUI {
//static ModePaletteComboBox::PalettesMap MODE_PALETTES =
static std::vector<std::pair<std::string, std::vector<std::string>>> MODE_PALETTES =
{
{ L("Palette 1 (default)"), { "#00B000", "#FFDC00", "#E70000" } },
{ L("Palette 2"), { "#FC766A", "#B0B8B4", "#184A45" } },
{ L("Palette 3"), { "#567572", "#964F4C", "#696667" } },
{ L("Palette 4"), { "#DA291C", "#56A8CB", "#53A567" } },
{ L("Palette 5"), { "#F65058", "#FBDE44", "#28334A" } },
{ L("Palette 6"), { "#FF3EA5", "#EDFF00", "#00A4CC" } },
{ L("Palette 7"), { "#E95C20", "#006747", "#4F2C1D" } },
{ L("Palette 8"), { "#D9514E", "#2A2B2D", "#2DA8D8" } }
};
ModePaletteComboBox::ModePaletteComboBox(wxWindow* parent) :
BitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY)
{
for (const auto& palette : MODE_PALETTES)
Append(_(palette.first), *get_bmp(palette.second));
}
void ModePaletteComboBox::UpdateSelection(const std::vector<wxColour> &palette_in)
{
for (size_t idx = 0; idx < MODE_PALETTES.size(); ++idx ) {
const auto& palette = MODE_PALETTES[idx].second;
bool is_selected = true;
for (size_t mode = 0; mode < palette_in.size(); mode++)
if (wxColour(palette[mode]) != palette_in[mode]) {
is_selected = false;
break;
}
if (is_selected) {
Select(int(idx));
return;
}
}
Select(-1);
}
BitmapCache& ModePaletteComboBox::bitmap_cache()
{
static BitmapCache bmps;
return bmps;
}
wxBitmapBundle * ModePaletteComboBox::get_bmp(const std::vector<std::string> &palette)
{
std::string bitmap_key;
for (const auto& color : palette)
bitmap_key += color + "+";
const int icon_height = wxOSX ? 10 : 12;
wxBitmapBundle* bmp_bndl = bitmap_cache().find_bndl(bitmap_key);
if (bmp_bndl == nullptr) {
// Create the bitmap with color bars.
std::vector<wxBitmapBundle*> bmps;
for (const auto& color : palette) {
bmps.emplace_back(get_bmp_bundle("mode", icon_height, color));
bmps.emplace_back(get_empty_bmp_bundle(wxOSX ? 5 : 6, icon_height));
}
bmp_bndl = bitmap_cache().insert_bndl(bitmap_key, bmps);
}
return bmp_bndl;
}
void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour)
{
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5);
@ -48,13 +120,81 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi
grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL);
};
add_color(sys_colour, wxGetApp().get_label_clr_sys(), wxGetApp().get_label_default_clr_system(), _L("Value is the same as the system value"));
add_color(mod_colour, wxGetApp().get_label_clr_modified(),wxGetApp().get_label_default_clr_modified(), _L("Value was changed and is not equal to the system value or the last saved preset"));
}
void ButtonsDescription::FillSizerWithModeColorDescriptions(
wxSizer* sizer, wxWindow* parent,
std::vector<wxColourPickerCtrl**> clr_pickers,
std::vector<wxColour>& mode_palette)
{
const int margin = em_unit(parent);
auto palette_cb = new ModePaletteComboBox(parent);
palette_cb->UpdateSelection(mode_palette);
palette_cb->Bind(wxEVT_COMBOBOX, [clr_pickers, &mode_palette](wxCommandEvent& evt) {
const int selection = evt.GetSelection();
if (selection < 0)
return;
const auto& palette = MODE_PALETTES[selection];
for (int mode = 0; mode < 3; mode++)
if (*clr_pickers[mode]) {
wxColour clr = wxColour(palette.second[mode]);
(*clr_pickers[mode])->SetColour(clr);
mode_palette[mode] = clr;
}
});
wxBoxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL);
h_sizer->Add(new wxStaticText(parent, wxID_ANY, _L("Default palette for mode markers") + ": "), 0, wxALIGN_CENTER_VERTICAL);
h_sizer->Add(palette_cb, 1, wxEXPAND);
sizer->Add(h_sizer, 0, wxEXPAND | wxBOTTOM, margin);
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(9, 5, 5);
sizer->Add(grid_sizer, 0, wxEXPAND);
const std::vector<wxString> names = { _L("Simple"), _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Expert") };
for (size_t mode = 0; mode < names.size(); ++mode) {
wxColour& color = mode_palette[mode];
wxColourPickerCtrl** color_picker = clr_pickers[mode];
*color_picker = new wxColourPickerCtrl(parent, wxID_ANY, color);
wxGetApp().UpdateDarkUI((*color_picker)->GetPickerCtrl(), true);
(*color_picker)->Bind(wxEVT_COLOURPICKER_CHANGED, [color_picker, &color, palette_cb, &mode_palette](wxCommandEvent&) {
const wxColour new_color = (*color_picker)->GetColour();
if (new_color != color) {
color = new_color;
palette_cb->UpdateSelection(mode_palette);
}
});
wxColour def_color = color;
auto btn = new ScalableButton(parent, wxID_ANY, "undo");
btn->SetToolTip(_L("Revert color"));
btn->Bind(wxEVT_BUTTON, [color_picker, &color, def_color, palette_cb, &mode_palette](wxEvent& event) {
color = def_color;
(*color_picker)->SetColour(def_color);
palette_cb->UpdateSelection(mode_palette);
});
parent->Bind(wxEVT_UPDATE_UI, [color_picker, def_color](wxUpdateUIEvent& evt) {
evt.Enable((*color_picker)->GetColour() != def_color);
}, btn->GetId());
grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(new wxStaticText(parent, wxID_ANY, names[mode]), 0, wxALIGN_CENTRE_VERTICAL | wxRIGHT, 2*margin);
}
}
ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry> &entries) :
wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize),
m_entries(entries)
@ -74,13 +214,20 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry
auto description = new wxStaticText(this, wxID_ANY, _(entry.symbol));
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
description = new wxStaticText(this, wxID_ANY, _(entry.explanation));
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
}
// Text color description
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
FillSizerWithTextColorDescriptions(sizer, this, &sys_colour, &mod_colour);
main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20);
main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20);
// Mode color markers description
mode_palette = wxGetApp().get_mode_palette();
wxSizer* mode_sizer = new wxBoxSizer(wxVERTICAL);
FillSizerWithModeColorDescriptions(mode_sizer, this, { &simple, &advanced, &expert }, mode_palette);
main_sizer->Add(mode_sizer, 0, wxEXPAND | wxALL, 20);
auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
@ -89,8 +236,10 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
wxGetApp().set_label_clr_sys(sys_colour->GetColour());
wxGetApp().set_label_clr_modified(mod_colour->GetColour());
wxGetApp().set_mode_palette(mode_palette);
EndModal(wxID_OK);
});
});
wxGetApp().UpdateDarkUI(btn);
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this)));

View File

@ -4,16 +4,48 @@
#include <wx/dialog.h>
#include <vector>
#include <wx/bmpbndl.h>
#include "BitmapComboBox.hpp"
class ScalableBitmap;
class wxColourPickerCtrl;
namespace Slic3r {
namespace GUI {
class BitmapCache;
// ---------------------------------
// *** PaletteComboBox ***
// ---------------------------------
// BitmapComboBox used to palets list in GUI Preferences
class ModePaletteComboBox : public BitmapComboBox
{
public:
ModePaletteComboBox(wxWindow* parent);
~ModePaletteComboBox() = default;
void UpdateSelection(const std::vector<wxColour>& palette_in);
protected:
// Caching bitmaps for the all bitmaps, used in preset comboboxes
static BitmapCache& bitmap_cache();
wxBitmapBundle* get_bmp( const std::vector<std::string>& palette);
};
class ButtonsDescription : public wxDialog
{
wxColourPickerCtrl* sys_colour{ nullptr };
wxColourPickerCtrl* mod_colour{ nullptr };
wxColourPickerCtrl* simple { nullptr };
wxColourPickerCtrl* advanced { nullptr };
wxColourPickerCtrl* expert { nullptr };
std::vector<wxColour> mode_palette;
public:
struct Entry {
Entry(ScalableBitmap *bitmap, const std::string &symbol, const std::string &explanation) : bitmap(bitmap), symbol(symbol), explanation(explanation) {}
@ -27,6 +59,9 @@ public:
~ButtonsDescription() {}
static void FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour);
static void FillSizerWithModeColorDescriptions(wxSizer* sizer, wxWindow* parent,
std::vector<wxColourPickerCtrl**> clr_pickers,
std::vector<wxColour>& mode_palette);
private:
std::vector<Entry> m_entries;

View File

@ -1,5 +1,6 @@
#include "CameraUtils.hpp"
#include <igl/project.h> // projecting points
#include <igl/unproject.h>
#include "slic3r/GUI/3DScene.hpp" // GLVolume
#include "libslic3r/Geometry/ConvexHull.hpp"
@ -79,37 +80,44 @@ Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera,
return Geometry::convex_hull(vertices_2d);
}
#include <igl/unproject.h>
Vec3d CameraUtils::create_ray(const Camera &camera, const Vec2d &coor) {
if (camera.get_type() == Camera::EType::Ortho)
return camera.get_dir_forward();
// check that it is known camera no other tha ORTHO or Persepective
assert(camera.get_type() == Camera::EType::Perspective);
void CameraUtils::ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) {
switch (camera.get_type()) {
case Camera::EType::Ortho: return ray_from_ortho_screen_pos(camera, position, point, direction);
case Camera::EType::Perspective: return ray_from_persp_screen_pos(camera, position, point, direction);
default: break;
}
}
Vec3d CameraUtils::screen_point(const Camera &camera, const Vec2d &position)
{
double height = camera.get_viewport().data()[3];
// Y coordinate has opposit direction
return Vec3d(position.x(), height - position.y(), 0.);
}
void CameraUtils::ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction)
{
assert(camera.get_type() == Camera::EType::Ortho);
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection = camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
Vec3d scene_point(coor.x(), viewport[3] - coor.y(), 0.);
Vec3d unprojected_point;
igl::unproject(scene_point, modelview, projection, viewport, unprojected_point);
Vec3d p0 = camera.get_position();
Vec3d dir = unprojected_point - p0;
dir.normalize();
return dir;
igl::unproject(screen_point(camera,position), modelview, projection, viewport, point);
direction = camera.get_dir_forward();
}
void CameraUtils::ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction)
{
assert(camera.get_type() == Camera::EType::Perspective);
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection = camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
igl::unproject(screen_point(camera, position), modelview, projection, viewport, point);
direction = point - camera.get_position();
}
Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor)
{
Vec3d dir = CameraUtils::create_ray(camera, coor);
Vec3d p0 = camera.get_position();
if (camera.get_type() == Camera::EType::Ortho) {
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection = camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
igl::unproject(Vec3d(coor.x(), viewport[3] - coor.y(), 0.), modelview, projection, viewport, p0);
}
Vec3d p0, dir;
ray_from_screen_pos(camera, coor, p0, dir);
// is approx zero
if ((fabs(dir.z()) - 1e-4) < 0)
@ -117,7 +125,7 @@ Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor)
std::numeric_limits<double>::max());
// find position of ray cross plane(z = 0)
double t = p0.z() / dir.z();
double t = p0.z() / dir.z();
Vec3d p = p0 - t * dir;
return Vec2d(p.x(), p.y());
}

View File

@ -36,12 +36,15 @@ public:
static Polygon create_hull2d(const Camera &camera, const GLVolume &volume);
/// <summary>
/// Unproject screen coordinate to scene direction start from camera position
/// Create ray(point and direction) for screen coordinate
/// </summary>
/// <param name="camera">Projection params</param>
/// <param name="coor">Coordinate on screen</param>
/// <returns>Scene direction</returns>
static Vec3d create_ray(const Camera &camera, const Vec2d &coor);
/// <param name="camera">Definition of camera</param>
/// <param name="position">Position on screen(aka mouse position) </param>
/// <param name="point">OUT start of ray</param>
/// <param name="direction">OUT direction of ray</param>
static void ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction);
static void ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction);
static void ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction);
/// <summary>
/// Unproject mouse coordinate to get position in space where z coor is zero
@ -52,6 +55,14 @@ public:
/// <returns>Position on platter under mouse</returns>
static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor);
/// <summary>
/// Create 3d screen point from 2d position
/// </summary>
/// <param name="camera">Define camera viewport</param>
/// <param name="position">Position on screen(aka mouse position)</param>
/// <returns>Point represented screen coor in 3d</returns>
static Vec3d screen_point(const Camera &camera, const Vec2d &position);
};
} // Slic3r::GUI

View File

@ -7,6 +7,7 @@
#include "libslic3r/PresetBundle.hpp"
#include "MsgDialog.hpp"
#include <string>
#include <wx/msgdlg.h>
namespace Slic3r {
@ -220,9 +221,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
bool have_perimeters = config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" })
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds", "overhang_overlap_levels", "dynamic_overhang_speeds" })
toggle_field(el, have_perimeters);
for (size_t i = 0; i < 4; i++) {
toggle_field("overhang_overlap_levels#" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds"));
toggle_field("dynamic_overhang_speeds#" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds"));
}
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
// infill_extruder uses the same logic as in Print::extruders()
for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed",
@ -315,6 +321,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
"wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" })
toggle_field(el, have_wipe_tower);
toggle_field("avoid_crossing_curled_overhangs", !config->opt_bool("avoid_crossing_perimeters"));
toggle_field("avoid_crossing_perimeters", !config->opt_bool("avoid_crossing_curled_overhangs"));
bool have_avoid_crossing_perimeters = config->opt_bool("avoid_crossing_perimeters");
toggle_field("avoid_crossing_perimeters_max_detour", have_avoid_crossing_perimeters);

View File

@ -11,6 +11,7 @@
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <wx/settings.h>
#include <wx/stattext.h>
@ -27,6 +28,12 @@
#include <wx/wupdlock.h>
#include <wx/debug.h>
#ifdef WIN32
#include <wx/msw/registry.h>
#include <KnownFolders.h>
#include <Shlobj_core.h>
#endif // WIN32
#ifdef _MSW_DARK_MODE
#include <wx/msw/dark_mode.h>
#endif // _MSW_DARK_MODE
@ -48,6 +55,7 @@
#include "format.hpp"
#include "MsgDialog.hpp"
#include "UnsavedChangesDialog.hpp"
#include "slic3r/Utils/AppUpdater.hpp"
#if defined(__linux__) && defined(__WXGTK3__)
#define wxLinux_gtk3 true
@ -1176,31 +1184,20 @@ PageCustom::PageCustom(ConfigWizard *parent)
: ConfigWizardPage(parent, _L("Custom Printer Setup"), _L("Custom Printer"))
{
cb_custom = new wxCheckBox(this, wxID_ANY, _L("Define a custom printer profile"));
tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name);
auto *label = new wxStaticText(this, wxID_ANY, _L("Custom profile name:"));
wxGetApp().UpdateDarkUI(tc_profile_name);
wxBoxSizer* profile_name_sizer = new wxBoxSizer(wxVERTICAL);
profile_name_editor = new SavePresetDialog::Item{ this, profile_name_sizer, default_profile_name };
profile_name_editor->Enable(false);
tc_profile_name->Enable(false);
tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
if (tc_profile_name->GetValue().IsEmpty()) {
if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(default_profile_name); }
else { tc_profile_name->SetValue(profile_name_prev); }
} else {
profile_name_prev = tc_profile_name->GetValue();
}
evt.Skip();
});
cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) {
tc_profile_name->Enable(custom_wanted());
cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &) {
profile_name_editor->Enable(custom_wanted());
wizard_p()->on_custom_setup(custom_wanted());
});
append(cb_custom);
append(label);
append(tc_profile_name);
append(profile_name_sizer);
}
PageUpdate::PageUpdate(ConfigWizard *parent)
@ -1240,6 +1237,227 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
}
namespace DownloaderUtils
{
#ifdef _WIN32
wxString get_downloads_path()
{
wxString ret;
PWSTR path = NULL;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);
if (SUCCEEDED(hr)) {
ret = wxString(path);
}
CoTaskMemFree(path);
return ret;
}
#elif __APPLE__
wxString get_downloads_path()
{
// call objective-c implementation
return wxString::FromUTF8(get_downloads_path_mac());
}
#else
wxString get_downloads_path()
{
wxString command = "xdg-user-dir DOWNLOAD";
wxArrayString output;
GUI::desktop_execute_get_result(command, output);
if (output.GetCount() > 0) {
return output[0];
}
return wxString();
}
#endif
Worker::Worker(wxWindow* parent)
: wxBoxSizer(wxHORIZONTAL)
, m_parent(parent)
{
m_input_path = new wxTextCtrl(m_parent, wxID_ANY);
set_path_name(get_app_config()->get("url_downloader_dest"));
auto* path_label = new wxStaticText(m_parent, wxID_ANY, _L("Download path") + ":");
this->Add(path_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
this->Add(m_input_path, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
auto* button_path = new wxButton(m_parent, wxID_ANY, _L("Browse"));
this->Add(button_path, 0, wxEXPAND | wxTOP | wxLEFT, 5);
button_path->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
boost::filesystem::path chosen_dest(boost::nowide::narrow(m_input_path->GetValue()));
wxDirDialog dialog(m_parent, L("Choose folder:"), chosen_dest.string() );
if (dialog.ShowModal() == wxID_OK)
this->m_input_path->SetValue(dialog.GetPath());
});
for (wxSizerItem* item : this->GetChildren())
if (item->IsWindow()) {
wxWindow* win = item->GetWindow();
wxGetApp().UpdateDarkUI(win);
}
}
void Worker::set_path_name(wxString path)
{
if (path.empty())
path = boost::nowide::widen(get_app_config()->get("url_downloader_dest"));
if (path.empty()) {
// What should be default path? Each system has Downloads folder, that could be good one.
// Other would be program location folder - not so good: access rights, apple bin is inside bundle...
// default_path = boost::dll::program_location().parent_path().string();
path = get_downloads_path();
}
m_input_path->SetValue(path);
}
void Worker::set_path_name(const std::string& name)
{
if (!m_input_path)
return;
set_path_name(boost::nowide::widen(name));
}
} // DownLoader
PageDownloader::PageDownloader(ConfigWizard* parent)
: ConfigWizardPage(parent, _L("Downloads from URL"), _L("Downloads"))
{
const AppConfig* app_config = wxGetApp().app_config;
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
append_spacer(VERTICAL_SPACING);
auto* box_allow_downloads = new wxCheckBox(this, wxID_ANY, _L("Allow build-in downloader"));
// TODO: Do we want it like this? The downloader is allowed for very first time the wizard is run.
bool box_allow_value = (app_config->has("downloader_url_registered") ? app_config->get("downloader_url_registered") == "1" : true);
box_allow_downloads->SetValue(box_allow_value);
append(box_allow_downloads);
append_text(wxString::Format(_L(
"If enabled, %s registers to start on custom URL on www.printables.com."
" You will be able to use button with %s logo to open models in this %s."
" The model will be downloaded into folder you choose bellow."
), SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME));
#ifdef __linux__
append_text(wxString::Format(_L(
"On Linux systems the process of registration also creates desktop integration files for this version of application."
)));
#endif
box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->downloader->allow(event.IsChecked()); });
downloader = new DownloaderUtils::Worker(this);
append(downloader);
downloader->allow(box_allow_value);
}
bool PageDownloader::on_finish_downloader() const
{
return downloader->on_finish();
}
bool DownloaderUtils::Worker::perform_register()
{
//boost::filesystem::path chosen_dest/*(path_text_ctrl->GetValue());*/(boost::nowide::narrow(path_text_ctrl->GetValue()));
boost::filesystem::path chosen_dest (GUI::format(path_name()));
boost::system::error_code ec;
if (chosen_dest.empty() || !boost::filesystem::is_directory(chosen_dest, ec) || ec) {
std::string err_msg = GUI::format("%1%\n\n%2%",_L("Chosen directory for downloads does not Exists.") ,chosen_dest.string());
BOOST_LOG_TRIVIAL(error) << err_msg;
show_error(m_parent, err_msg);
return false;
}
BOOST_LOG_TRIVIAL(info) << "Downloader registration: Directory for downloads: " << chosen_dest.string();
wxGetApp().app_config->set("url_downloader_dest", chosen_dest.string());
#ifdef _WIN32
// Registry key creation for "prusaslicer://" URL
boost::filesystem::path binary_path(boost::filesystem::canonical(boost::dll::program_location()));
// the path to binary needs to be correctly saved in string with respect to localized characters
wxString wbinary = wxString::FromUTF8(binary_path.string());
std::string binary_string = (boost::format("%1%") % wbinary).str();
BOOST_LOG_TRIVIAL(info) << "Downloader registration: Path of binary: " << binary_string;
//std::string key_string = "\"" + binary_string + "\" \"-u\" \"%1\"";
//std::string key_string = "\"" + binary_string + "\" \"%1\"";
std::string key_string = "\"" + binary_string + "\" \"--single-instance\" \"%1\"";
wxRegKey key_first(wxRegKey::HKCU, "Software\\Classes\\prusaslicer");
wxRegKey key_full(wxRegKey::HKCU, "Software\\Classes\\prusaslicer\\shell\\open\\command");
if (!key_first.Exists()) {
key_first.Create(false);
}
key_first.SetValue("URL Protocol", "");
if (!key_full.Exists()) {
key_full.Create(false);
}
//key_full = "\"C:\\Program Files\\Prusa3D\\PrusaSlicer\\prusa-slicer-console.exe\" \"%1\"";
key_full = key_string;
#elif __APPLE__
// Apple registers for custom url in info.plist thus it has to be already registered since build.
// The url will always trigger opening of prusaslicer and we have to check that user has allowed it. (GUI_App::MacOpenURL is the triggered method)
#else
// the performation should be called later during desktop integration
perform_registration_linux = true;
#endif
return true;
}
void DownloaderUtils::Worker::deregister()
{
#ifdef _WIN32
std::string key_string = "";
wxRegKey key_full(wxRegKey::HKCU, "Software\\Classes\\prusaslicer\\shell\\open\\command");
if (!key_full.Exists()) {
return;
}
key_full = key_string;
#elif __APPLE__
// TODO
#else
BOOST_LOG_TRIVIAL(debug) << "DesktopIntegrationDialog::undo_downloader_registration";
DesktopIntegrationDialog::undo_downloader_registration();
perform_registration_linux = false;
#endif
}
bool DownloaderUtils::Worker::on_finish() {
AppConfig* app_config = wxGetApp().app_config;
bool ac_value = app_config->get("downloader_url_registered") == "1";
BOOST_LOG_TRIVIAL(debug) << "PageDownloader::on_finish_downloader ac_value " << ac_value << " downloader_checked " << downloader_checked;
if (ac_value && downloader_checked) {
// already registered but we need to do it again
if (!perform_register())
return false;
app_config->set("downloader_url_registered", "1");
} else if (!ac_value && downloader_checked) {
// register
if (!perform_register())
return false;
app_config->set("downloader_url_registered", "1");
} else if (ac_value && !downloader_checked) {
// deregister, downloads are banned now
deregister();
app_config->set("downloader_url_registered", "0");
} /*else if (!ac_value && !downloader_checked) {
// not registered and we dont want to do it
// do not deregister as other instance might be registered
} */
return true;
}
PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent)
: ConfigWizardPage(parent, _L("Reload from disk"), _L("Reload from disk"))
, full_pathnames(false)
@ -1386,6 +1604,45 @@ void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
}
}
static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
{
e.Skip();
wxString str = ctrl->GetValue();
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
// Replace the first incorrect separator in decimal number.
bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0;
double val = 0.0;
if (!str.ToDouble(&val)) {
if (val == 0.0)
val = def_value;
ctrl->SetValue(double_to_string(val));
show_error(nullptr, _L("Invalid numeric input."));
// On Windows, this SetFocus creates an invisible marker.
//ctrl->SetFocus();
}
else if (was_replaced)
ctrl->SetValue(double_to_string(val));
}
class DiamTextCtrl : public wxTextCtrl
{
public:
DiamTextCtrl(wxWindow* parent)
{
#ifdef _WIN32
long style = wxBORDER_SIMPLE;
#else
long style = 0;
#endif
Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord), style);
wxGetApp().UpdateDarkUI(this);
}
~DiamTextCtrl() {}
};
PageBedShape::PageBedShape(ConfigWizard *parent)
: ConfigWizardPage(parent, _L("Bed Shape and Size"), _L("Bed Shape"), 1)
, shape_panel(new BedShapePanel(this))
@ -1409,43 +1666,63 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
}
static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
PageBuildVolume::PageBuildVolume(ConfigWizard* parent)
: ConfigWizardPage(parent, _L("Build Volume"), _L("Build Volume"), 1)
, build_volume(new DiamTextCtrl(this))
{
e.Skip();
wxString str = ctrl->GetValue();
append_text(_L("Set verctical size of your printer."));
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
// Replace the first incorrect separator in decimal number.
bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0;
wxString value = "200";
build_volume->SetValue(value);
double val = 0.0;
if (!str.ToDouble(&val)) {
if (val == 0.0)
build_volume->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
double def_value = 200.0;
double max_value = 1200.0;
e.Skip();
wxString str = build_volume->GetValue();
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
// Replace the first incorrect separator in decimal number.
bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0;
double val = 0.0;
if (!str.ToDouble(&val)) {
val = def_value;
ctrl->SetValue(double_to_string(val));
show_error(nullptr, _L("Invalid numeric input."));
ctrl->SetFocus();
}
else if (was_replaced)
ctrl->SetValue(double_to_string(val));
build_volume->SetValue(double_to_string(val));
show_error(nullptr, _L("Invalid numeric input."));
//build_volume->SetFocus();
} else if (val < 0.0) {
val = def_value;
build_volume->SetValue(double_to_string(val));
show_error(nullptr, _L("Invalid numeric input."));
//build_volume->SetFocus();
} else if (val > max_value) {
val = max_value;
build_volume->SetValue(double_to_string(val));
show_error(nullptr, _L("Invalid numeric input."));
//build_volume->SetFocus();
} else if (was_replaced)
build_volume->SetValue(double_to_string(val));
}, build_volume->GetId());
auto* sizer_volume = new wxFlexGridSizer(3, 5, 5);
auto* text_volume = new wxStaticText(this, wxID_ANY, _L("Max print height:"));
auto* unit_volume = new wxStaticText(this, wxID_ANY, _L("mm"));
sizer_volume->AddGrowableCol(0, 1);
sizer_volume->Add(text_volume, 0, wxALIGN_CENTRE_VERTICAL);
sizer_volume->Add(build_volume);
sizer_volume->Add(unit_volume, 0, wxALIGN_CENTRE_VERTICAL);
append(sizer_volume);
}
class DiamTextCtrl : public wxTextCtrl
void PageBuildVolume::apply_custom_config(DynamicPrintConfig& config)
{
public:
DiamTextCtrl(wxWindow* parent)
{
#ifdef _WIN32
long style = wxBORDER_SIMPLE;
#else
long style = 0;
#endif
Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord), style);
wxGetApp().UpdateDarkUI(this);
}
~DiamTextCtrl() {}
};
double val = 0.0;
build_volume->GetValue().ToDouble(&val);
auto* opt_volume = new ConfigOptionFloat(val);
config.set_key_value("max_print_height", opt_volume);
}
PageDiameters::PageDiameters(ConfigWizard *parent)
: ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
@ -1915,6 +2192,7 @@ void ConfigWizard::priv::load_pages()
if (page_custom->custom_wanted()) {
index->add_page(page_firmware);
index->add_page(page_bed);
index->add_page(page_bvolume);
index->add_page(page_diams);
index->add_page(page_temps);
}
@ -1928,6 +2206,7 @@ void ConfigWizard::priv::load_pages()
btn_finish->Enable(any_fff_selected || any_sla_selected || custom_printer_selected);
index->add_page(page_update);
index->add_page(page_downloader);
index->add_page(page_reload_from_disk);
#ifdef _WIN32
index->add_page(page_files_association);
@ -2357,6 +2636,11 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i
bool ConfigWizard::priv::on_bnt_finish()
{
wxBusyCursor wait;
if (!page_downloader->on_finish_downloader()) {
index->go_to(page_downloader);
return false;
}
/* When Filaments or Sla Materials pages are activated,
* materials for this pages are automaticaly updated and presets are reloaded.
*
@ -2597,8 +2881,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
#ifdef __linux__
// Desktop integration on Linux
if (page_welcome->integrate_desktop())
DesktopIntegrationDialog::perform_desktop_integration();
BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->downloader->get_perform_registration_linux();
if (page_welcome->integrate_desktop() || page_downloader->downloader->get_perform_registration_linux())
DesktopIntegrationDialog::perform_desktop_integration(page_downloader->downloader->get_perform_registration_linux());
#endif
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
@ -2765,7 +3050,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
{preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
if (!only_sla_mode && page_custom->custom_wanted()) {
if (!only_sla_mode && page_custom->custom_wanted() && page_custom->is_valid_profile_name()) {
// if unsaved changes was not cheched till this moment
if (!check_unsaved_preset_changes &&
!wxGetApp().check_and_keep_current_preset_changes(caption, _L("Custom printer was installed and it will be activated."), act_btns, &apply_keeped_changes))
@ -2773,6 +3058,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
page_firmware->apply_custom_config(*custom_config);
page_bed->apply_custom_config(*custom_config);
page_bvolume->apply_custom_config(*custom_config);
page_diams->apply_custom_config(*custom_config);
page_temps->apply_custom_config(*custom_config);
@ -2916,6 +3202,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->add_page(p->page_update = new PageUpdate(this));
p->add_page(p->page_downloader = new PageDownloader(this));
p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this));
#ifdef _WIN32
p->add_page(p->page_files_association = new PageFilesAssociation(this));
@ -2923,9 +3210,10 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->add_page(p->page_mode = new PageMode(this));
p->add_page(p->page_firmware = new PageFirmware(this));
p->add_page(p->page_bed = new PageBedShape(this));
p->add_page(p->page_bvolume = new PageBuildVolume(this));
p->add_page(p->page_diams = new PageDiameters(this));
p->add_page(p->page_temps = new PageTemperatures(this));
p->load_pages();
p->index->go_to(size_t{0});

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