Merge branch 'master' into et_tm_sla_volumes_6-SPE-1285
@ -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
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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
@ -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 |
63
resources/icons/notification_open.svg
Normal 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 |
64
resources/icons/notification_open_hover.svg
Normal 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 |
75
resources/icons/notification_pause.svg
Normal 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 |
75
resources/icons/notification_pause_hover.svg
Normal 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 |
75
resources/icons/notification_play.svg
Normal 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 |
75
resources/icons/notification_play_hover.svg
Normal 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 |
61
resources/icons/open_browser.svg
Normal 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 |
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 142 KiB |
BIN
resources/profiles/Creality/ENDER5S1_thumbnail.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
resources/profiles/Creality/SERMOONV1PRO_thumbnail.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
resources/profiles/Creality/SERMOONV1_thumbnail.png
Normal file
After Width: | Height: | Size: 24 KiB |
4
resources/profiles/Creality/sermoonv1.svg
Normal 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 |
BIN
resources/profiles/Creality/sermoonv1_bed.stl
Normal file
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 16 KiB |
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
@ -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);
|
||||
}
|
||||
|
@ -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 ¢roid() 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; }
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "../../../clipper/clipper_z.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
class ThickPolyline;
|
||||
struct ThickPolyline;
|
||||
}
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -174,7 +174,6 @@ public:
|
||||
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||
void rotate(double angle, const Point ¢er) { (*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);
|
||||
|
@ -193,7 +193,6 @@ set(SLIC3R_SOURCES
|
||||
Model.hpp
|
||||
ModelArrange.hpp
|
||||
ModelArrange.cpp
|
||||
#ModelVolumeType.hpp
|
||||
MultiMaterialSegmentation.cpp
|
||||
MultiMaterialSegmentation.hpp
|
||||
MeshNormals.hpp
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
325
src/libslic3r/GCode/ExtrusionProcessor.hpp
Normal 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 §ion : 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_
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 ¶bolic_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
|
@ -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
|
||||
|
@ -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>());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "Arrange.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "enum_bitmask.hpp"
|
||||
//#include "ModelVolumeType.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
|
||||
#include <map>
|
||||
|
@ -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_ */
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 ¶ms) 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;
|
||||
|
@ -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
|
||||
|
@ -13,7 +13,7 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void PrintTryCancel::operator()()
|
||||
void PrintTryCancel::operator()() const
|
||||
{
|
||||
m_print->throw_if_canceled();
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
@ -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) {
|
||||
|
@ -459,6 +459,7 @@ public:
|
||||
void set_task(const TaskParams ¶ms) 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.
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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 ¶ms)
|
||||
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 ¶ms)
|
||||
{
|
||||
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 ¤t_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 ¶ms)
|
||||
SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
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 ¶ms)
|
||||
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 ¶ms)
|
||||
#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, ¶ms,
|
||||
auto reckon_new_support_point = [&part, &weakest_conn, &supp_points, &supports_presence_grid, ¶ms,
|
||||
&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 ¶ms)
|
||||
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 ¶ms)
|
||||
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 ¶ms)
|
||||
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 ¶ms)
|
||||
} // 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 ¶ms) {
|
||||
// return {};
|
||||
// }
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms)
|
||||
SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
: 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 ¤t_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 ¶ms)
|
||||
void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, const Params ¶ms)
|
||||
{
|
||||
#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 ¶ms)
|
||||
{
|
||||
#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 ¶ms) : 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 ¶ms;
|
||||
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
|
||||
|
@ -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 ¶ms);
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms);
|
||||
SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms);
|
||||
|
||||
void estimate_supports_malformations(SupportLayerPtrs &layers, float supports_flow_width, const Params ¶ms);
|
||||
void estimate_malformations(LayerPtrs &layers, const Params ¶ms);
|
||||
void estimate_supports_malformations(std::vector<SupportLayer*> &layers, float supports_flow_width, const Params ¶ms);
|
||||
void estimate_malformations(std::vector<Layer*> &layers, const Params ¶ms);
|
||||
|
||||
} // namespace SupportSpotsGenerator
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -267,8 +267,6 @@ class ScopeGuard
|
||||
{
|
||||
public:
|
||||
typedef std::function<void()> Closure;
|
||||
private:
|
||||
// bool committed;
|
||||
Closure closure;
|
||||
|
||||
public:
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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)));
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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});
|
||||
|
||||
|