NEW:add measure gizmo new files

Jira: STUDIO-6166
most of the code come from orca
The original code is from PrusaSlicer and referring to the implementation of OrcaSlicer. Thanks for PrusaSlicer and OrcaSlicer , thanks for Lukas Matena and enricoturri1966 and so on.
commit f72d42f920334a05b0efa01f18eb4cc066809c00
Author: enricoturri1966 <enricoturri@seznam.cz>
Date:   Tue Oct 31 11:43:04 2023 +0800

    Measure: Initial porting of Measure Gizmo

diff --git a/resources/images/copy_menu.svg b/resources/images/copy_menu.svg
new file mode 100644

Change-Id: I81afa52165f14c34e0c63f2c40c953a04d76fc8b
This commit is contained in:
enricoturri1966 2024-02-22 19:32:54 +08:00 committed by Lane.Wei
parent 89f40dd6fc
commit 06b17c4fdc
27 changed files with 3146 additions and 80 deletions

View File

@ -0,0 +1,37 @@
<?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 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="copy">
<g>
<path fill="#009688" d="M115.76,51.2l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h66.04c4.2,0,7.62-3.42,7.62-7.62v-50.8C120.09,58.17,118.23,53.67,115.76,51.2z M111.42,54.04h-6.57
v-6.57L111.42,54.04z M115.01,112.47c0,1.4-1.14,2.54-2.54,2.54H46.43c-1.4,0-2.54-1.14-2.54-2.54V46.42
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46v12.24c0,1.4,1.14,2.54,2.54,2.54h12.24c0.28,0.91,0.46,1.8,0.46,2.54
V112.47z"/>
<path fill="#009688" d="M53.97,59.13h35.72c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H53.97c-1.4,0-2.54,1.14-2.54,2.54
S52.56,59.13,53.97,59.13z"/>
<path fill="#009688" d="M104.93,69.29H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,69.29,104.93,69.29z"/>
<path fill="#009688" d="M104.93,84.53H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,84.53,104.93,84.53z"/>
<path fill="#009688" d="M104.93,99.77H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,99.77,104.93,99.77z"/>
</g>
<g>
<path fill="#262E30" d="M85.27,20.71l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h17.78c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H15.94c-1.4,0-2.54-1.14-2.54-2.54V15.94
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46V26.1c0,1.4,1.14,2.54,2.54,2.54h12.45c0.16,0.49,0.25,0.93,0.25,1.27
v3.81c0,1.4,1.14,2.54,2.54,2.54c1.4,0,2.54-1.14,2.54-2.54v-3.81C89.61,27.14,87.75,23.19,85.27,20.71z M74.37,16.99l6.57,6.57
h-6.57V16.99z"/>
<path fill="#262E30" d="M59.21,23.56H23.48c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h35.72c1.4,0,2.54-1.14,2.54-2.54
S60.61,23.56,59.21,23.56z"/>
<path fill="#262E30" d="M28.73,38.8h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,38.8,28.73,38.8z"/>
<path fill="#262E30" d="M28.73,54.04h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,54.04,28.73,54.04z"/>
<path fill="#262E30" d="M28.73,69.29h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,69.29,28.73,69.29z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,37 @@
<?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 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="copy">
<g>
<path fill="#009688" d="M115.76,51.2l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h66.04c4.2,0,7.62-3.42,7.62-7.62v-50.8C120.09,58.17,118.23,53.67,115.76,51.2z M111.42,54.04h-6.57
v-6.57L111.42,54.04z M115.01,112.47c0,1.4-1.14,2.54-2.54,2.54H46.43c-1.4,0-2.54-1.14-2.54-2.54V46.42
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46v12.24c0,1.4,1.14,2.54,2.54,2.54h12.24c0.28,0.91,0.46,1.8,0.46,2.54
V112.47z"/>
<path fill="#009688" d="M53.97,59.13h35.72c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H53.97c-1.4,0-2.54,1.14-2.54,2.54
S52.56,59.13,53.97,59.13z"/>
<path fill="#009688" d="M104.93,69.29H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,69.29,104.93,69.29z"/>
<path fill="#009688" d="M104.93,84.53H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,84.53,104.93,84.53z"/>
<path fill="#009688" d="M104.93,99.77H53.97c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h50.96c1.4,0,2.54-1.14,2.54-2.54
S106.33,99.77,104.93,99.77z"/>
</g>
<g>
<path fill="#B6B6B6" d="M85.27,20.71l-8.06-8.06c-2.47-2.47-6.97-4.34-10.47-4.34h-50.8c-4.2,0-7.62,3.42-7.62,7.62v66.04
c0,4.2,3.42,7.62,7.62,7.62h17.78c1.4,0,2.54-1.14,2.54-2.54s-1.14-2.54-2.54-2.54H15.94c-1.4,0-2.54-1.14-2.54-2.54V15.94
c0-1.4,1.14-2.54,2.54-2.54h50.8c0.74,0,1.63,0.18,2.54,0.46V26.1c0,1.4,1.14,2.54,2.54,2.54h12.45c0.16,0.49,0.25,0.93,0.25,1.27
v3.81c0,1.4,1.14,2.54,2.54,2.54c1.4,0,2.54-1.14,2.54-2.54v-3.81C89.61,27.14,87.75,23.19,85.27,20.71z M74.37,16.99l6.57,6.57
h-6.57V16.99z"/>
<path fill="#B6B6B6" d="M59.21,23.56H23.48c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h35.72c1.4,0,2.54-1.14,2.54-2.54
S60.61,23.56,59.21,23.56z"/>
<path fill="#B6B6B6" d="M28.73,38.8h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,38.8,28.73,38.8z"/>
<path fill="#B6B6B6" d="M28.73,54.04h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,54.04,28.73,54.04z"/>
<path fill="#B6B6B6" d="M28.73,69.29h-5.24c-1.4,0-2.54,1.14-2.54,2.54s1.14,2.54,2.54,2.54h5.24c1.4,0,2.54-1.14,2.54-2.54
S30.13,69.29,28.73,69.29z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.0"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 128 128"
enable-background="new 0 0 128 128"
xml:space="preserve"
sodipodi:docname="measure2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs976">
</defs><sodipodi:namedview
id="namedview974"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="6.34375"
inkscape:cx="63.921182"
inkscape:cy="64.078818"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="3191"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<g
id="g1872"><path
fill="#009688"
d="m 26.966044,54.457929 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 36.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-1"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 46.966202,60.202938 0.07558,-14.999888 c 0.0063,-1.244993 0.681318,-2.242399 1.511294,-2.232988 0.829976,0.0087 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999887 c -0.0063,1.244995 -0.681318,2.242401 -1.511294,2.232988 -0.829976,-0.0087 -1.494893,-1.021997 -1.48862,-2.266998 z"
id="path958-8-9"
style="fill:#009688;fill-opacity:1;stroke-width:1.22476;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 56.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 66.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-0"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 76.966202,60.202932 0.07558,-14.999881 c 0.0063,-1.244994 0.681318,-2.2424 1.511294,-2.23299 0.829976,0.0085 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999882 c -0.0063,1.244995 -0.681318,2.2424 -1.511294,2.232987 -0.829976,-0.0085 -1.494893,-1.021996 -1.48862,-2.266997 z"
id="path958-8-9-3-1"
style="fill:#009688;fill-opacity:1;stroke-width:1.22474;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 86.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 96.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494892,0.681318 1.488622,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4-2"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /></g><g
id="g964"
transform="translate(0,34.9)">
<path
fill="#262E30"
d="M 108.79,51.6 H 19.21 c -1.93,0 -3.5,-1.57 -3.5,-3.5 V 10.12 c 0,-1.93 1.57,-3.5 3.5,-3.5 h 89.57 c 1.93,0 3.5,1.57 3.5,3.5 V 48.1 c 0.01,1.93 -1.57,3.5 -3.49,3.5 z M 19.21,9.62 c -0.27,0 -0.5,0.23 -0.5,0.5 V 48.1 c 0,0.27 0.23,0.5 0.5,0.5 h 89.57 c 0.27,0 0.5,-0.23 0.5,-0.5 V 10.12 c 0,-0.27 -0.23,-0.5 -0.5,-0.5 z"
id="path962" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.0"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 128 128"
enable-background="new 0 0 128 128"
xml:space="preserve"
sodipodi:docname="measure2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs976">
</defs><sodipodi:namedview
id="namedview974"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="6.34375"
inkscape:cx="63.921182"
inkscape:cy="64.078818"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="3191"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<g
id="g1872"><path
fill="#009688"
d="m 26.966044,54.457929 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 36.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-1"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 46.966202,60.202938 0.07558,-14.999888 c 0.0063,-1.244993 0.681318,-2.242399 1.511294,-2.232988 0.829976,0.0087 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999887 c -0.0063,1.244995 -0.681318,2.242401 -1.511294,2.232988 -0.829976,-0.0087 -1.494893,-1.021997 -1.48862,-2.266998 z"
id="path958-8-9"
style="fill:#009688;fill-opacity:1;stroke-width:1.22476;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 56.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 66.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-0"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 76.966202,60.202932 0.07558,-14.999881 c 0.0063,-1.244994 0.681318,-2.2424 1.511294,-2.23299 0.829976,0.0085 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999882 c -0.0063,1.244995 -0.681318,2.2424 -1.511294,2.232987 -0.829976,-0.0085 -1.494893,-1.021996 -1.48862,-2.266997 z"
id="path958-8-9-3-1"
style="fill:#009688;fill-opacity:1;stroke-width:1.22474;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 86.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /><path
fill="#009688"
d="m 96.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494892,0.681318 1.488622,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
id="path958-8-9-3-1-4-2"
style="fill:#009688;fill-opacity:1;paint-order:stroke fill markers"
sodipodi:nodetypes="sccsccs" /></g><g
id="g964"
transform="translate(0,34.9)">
<path
fill="#B6B6B6"
d="M 108.79,51.6 H 19.21 c -1.93,0 -3.5,-1.57 -3.5,-3.5 V 10.12 c 0,-1.93 1.57,-3.5 3.5,-3.5 h 89.57 c 1.93,0 3.5,1.57 3.5,3.5 V 48.1 c 0.01,1.93 -1.57,3.5 -3.49,3.5 z M 19.21,9.62 c -0.27,0 -0.5,0.23 -0.5,0.5 V 48.1 c 0,0.27 0.23,0.5 0.5,0.5 h 89.57 c 0.27,0 0.5,-0.23 0.5,-0.5 V 10.12 c 0,-0.27 -0.23,-0.5 -0.5,-0.5 z"
id="path962" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -212,6 +212,8 @@ namespace ImGui
const wchar_t AddFilamentDarkIcon = 0x0845;
const wchar_t DeleteFilamentIcon = 0x0846;
const wchar_t DeleteFilamentDarkIcon = 0x0847;
const wchar_t ClipboardBtnIcon = 0x0848;
const wchar_t ClipboardBtnDarkIcon = 0x0849;
// void MyFunction(const char* name, const MyMatrix44& v);
}

View File

@ -48,6 +48,8 @@ set(lisbslic3r_sources
ClipperUtils.hpp
Clipper2Utils.cpp
Clipper2Utils.hpp
Color.cpp
Color.hpp
Config.cpp
Config.hpp
CurveAnalyzer.cpp
@ -197,6 +199,9 @@ set(lisbslic3r_sources
ModelArrange.cpp
MultiMaterialSegmentation.cpp
MultiMaterialSegmentation.hpp
Measure.hpp
Measure.cpp
MeasureUtils.hpp
CustomGCode.cpp
CustomGCode.hpp
Arrange.hpp
@ -280,6 +285,7 @@ set(lisbslic3r_sources
Surface.hpp
SurfaceCollection.cpp
SurfaceCollection.hpp
SurfaceMesh.hpp
SVG.cpp
SVG.hpp
Technologies.hpp

425
src/libslic3r/Color.cpp Normal file
View File

@ -0,0 +1,425 @@
#include "libslic3r.h"
#include "Color.hpp"
#include <random>
static const float INV_255 = 1.0f / 255.0f;
namespace Slic3r {
// Conversion from RGB to HSV color space
// The input RGB values are in the range [0, 1]
// The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
static void RGBtoHSV(float r, float g, float b, float& h, float& s, float& v)
{
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
const float max_comp = std::max(std::max(r, g), b);
const float min_comp = std::min(std::min(r, g), b);
const float delta = max_comp - min_comp;
if (delta > 0.0f) {
if (max_comp == r)
h = 60.0f * (std::fmod(((g - b) / delta), 6.0f));
else if (max_comp == g)
h = 60.0f * (((b - r) / delta) + 2.0f);
else if (max_comp == b)
h = 60.0f * (((r - g) / delta) + 4.0f);
s = (max_comp > 0.0f) ? delta / max_comp : 0.0f;
}
else {
h = 0.0f;
s = 0.0f;
}
v = max_comp;
while (h < 0.0f) { h += 360.0f; }
while (h > 360.0f) { h -= 360.0f; }
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
}
// Conversion from HSV to RGB color space
// The input HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
// The output RGB values are in the range [0, 1]
static void HSVtoRGB(float h, float s, float v, float& r, float& g, float& b)
{
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
const float chroma = v * s;
const float h_prime = std::fmod(h / 60.0f, 6.0f);
const float x = chroma * (1.0f - std::abs(std::fmod(h_prime, 2.0f) - 1.0f));
const float m = v - chroma;
if (0.0f <= h_prime && h_prime < 1.0f) {
r = chroma;
g = x;
b = 0.0f;
}
else if (1.0f <= h_prime && h_prime < 2.0f) {
r = x;
g = chroma;
b = 0.0f;
}
else if (2.0f <= h_prime && h_prime < 3.0f) {
r = 0.0f;
g = chroma;
b = x;
}
else if (3.0f <= h_prime && h_prime < 4.0f) {
r = 0.0f;
g = x;
b = chroma;
}
else if (4.0f <= h_prime && h_prime < 5.0f) {
r = x;
g = 0.0f;
b = chroma;
}
else if (5.0f <= h_prime && h_prime < 6.0f) {
r = chroma;
g = 0.0f;
b = x;
}
else {
r = 0.0f;
g = 0.0f;
b = 0.0f;
}
r += m;
g += m;
b += m;
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
}
class Randomizer
{
std::random_device m_rd;
public:
float random_float(float min, float max) {
std::mt19937 rand_generator(m_rd());
std::uniform_real_distribution<float> distrib(min, max);
return distrib(rand_generator);
}
};
ColorRGB::ColorRGB(float r, float g, float b)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) })
{
}
ColorRGB::ColorRGB(unsigned char r, unsigned char g, unsigned char b)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGB::operator < (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
else if (m_data[i] > other.m_data[i])
return false;
}
return false;
}
bool ColorRGB::operator > (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
else if (m_data[i] < other.m_data[i])
return false;
}
return false;
}
ColorRGB ColorRGB::operator + (const ColorRGB& other) const
{
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGB ColorRGB::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA::ColorRGBA(float r, float g, float b, float a)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f), std::clamp(a, 0.0f, 1.0f) })
{
}
ColorRGBA::ColorRGBA(std::array<float, 4> color)
: m_data({std::clamp(color[0], 0.0f, 1.0f), std::clamp(color[1], 0.0f, 1.0f), std::clamp(color[2], 0.0f, 1.0f), std::clamp(color[3], 0.0f, 1.0f)})
{
}
ColorRGBA::ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f), std::clamp(a * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGBA::operator < (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
else if (m_data[i] > other.m_data[i])
return false;
}
return false;
}
bool ColorRGBA::operator > (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
else if (m_data[i] < other.m_data[i])
return false;
}
return false;
}
ColorRGBA ColorRGBA::operator + (const ColorRGBA& other) const
{
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA ColorRGBA::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
ret.m_data[3] = this->m_data[3];
return ret;
}
ColorRGB operator * (float value, const ColorRGB& other) { return other * value; }
ColorRGBA operator * (float value, const ColorRGBA& other) { return other * value; }
ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGB complementary(const ColorRGB& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b() };
}
ColorRGBA complementary(const ColorRGBA& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b(), color.a() };
}
ColorRGB saturate(const ColorRGB& color, float factor)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
s = std::clamp(s * factor, 0.0f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGBA saturate(const ColorRGBA& color, float factor)
{
return to_rgba(saturate(to_rgb(color), factor), color.a());
}
ColorRGB opposite(const ColorRGB& color)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
h += 65.0f; // 65 instead 60 to avoid circle values
if (h > 360.0f)
h -= 360.0f;
Randomizer rnd;
s = rnd.random_float(0.65f, 1.0f);
v = rnd.random_float(0.65f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGB opposite(const ColorRGB& a, const ColorRGB& b)
{
float ha, sa, va;
RGBtoHSV(a.r(), a.g(), a.b(), ha, sa, va);
float hb, sb, vb;
RGBtoHSV(b.r(), b.g(), b.b(), hb, sb, vb);
float delta_h = std::abs(ha - hb);
float start_h = (delta_h > 180.0f) ? std::min(ha, hb) : std::max(ha, hb);
start_h += 5.0f; // to avoid circle change of colors for 120 deg
if (delta_h < 180.0f)
delta_h = 360.0f - delta_h;
Randomizer rnd;
float out_h = start_h + 0.5f * delta_h;
if (out_h > 360.0f)
out_h -= 360.0f;
float out_s = rnd.random_float(0.65f, 1.0f);
float out_v = rnd.random_float(0.65f, 1.0f);
float out_r, out_g, out_b;
HSVtoRGB(out_h, out_s, out_v, out_r, out_g, out_b);
return { out_r, out_g, out_b };
}
bool can_decode_color(const std::string &color)
{
return (color.size() == 7 && color.front() == '#') || (color.size() == 9 && color.front() == '#');
}
bool decode_color(const std::string& color_in, ColorRGB& color_out)
{
ColorRGBA rgba;
if (!decode_color(color_in, rgba))
return false;
color_out = to_rgb(rgba);
return true;
}
bool decode_color(const std::string& color_in, ColorRGBA& color_out)
{
auto hex_digit_to_int = [](const char c) {
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
};
color_out = ColorRGBA::BLACK();
if (can_decode_color(color_in)) {
const char *c = color_in.data() + 1;
if (color_in.size() == 7) {
for (unsigned int i = 0; i < 3; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
} else {
for (unsigned int i = 0; i < 4; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
}
} else
return false;
assert(0.0f <= color_out.r() && color_out.r() <= 1.0f);
assert(0.0f <= color_out.g() && color_out.g() <= 1.0f);
assert(0.0f <= color_out.b() && color_out.b() <= 1.0f);
assert(0.0f <= color_out.a() && color_out.a() <= 1.0f);
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out)
{
colors_out = std::vector<ColorRGB>(colors_in.size(), ColorRGB::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out)
{
colors_out = std::vector<ColorRGBA>(colors_in.size(), ColorRGBA::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
std::string encode_color(const ColorRGB& color)
{
char buffer[64];
::sprintf(buffer, "#%02X%02X%02X", color.r_uchar(), color.g_uchar(), color.b_uchar());
return std::string(buffer);
}
std::string encode_color(const ColorRGBA& color) { return encode_color(to_rgb(color)); }
ColorRGB to_rgb(const ColorRGBA& other_rgba) { return { other_rgba.r(), other_rgba.g(), other_rgba.b() }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), 1.0f }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), alpha }; }
ColorRGBA picking_decode(unsigned int id)
{
return {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff)) * INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b) { return r + (g << 8) + (b << 16); }
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace Slic3r

178
src/libslic3r/Color.hpp Normal file
View File

@ -0,0 +1,178 @@
#ifndef slic3r_Color_hpp_
#define slic3r_Color_hpp_
#include <array>
#include <algorithm>
namespace Slic3r {
class ColorRGB
{
std::array<float, 3> m_data{1.0f, 1.0f, 1.0f};
public:
ColorRGB() = default;
ColorRGB(float r, float g, float b);
ColorRGB(unsigned char r, unsigned char g, unsigned char b);
ColorRGB(const ColorRGB& other) = default;
ColorRGB& operator = (const ColorRGB& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGB& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGB& other) const { return !operator==(other); }
bool operator < (const ColorRGB& other) const;
bool operator > (const ColorRGB& other) const;
ColorRGB operator + (const ColorRGB& other) const;
ColorRGB operator * (float value) const;
const float* const data() const { return m_data.data(); }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 2);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
static const ColorRGB BLACK() { return { 0.0f, 0.0f, 0.0f }; }
static const ColorRGB BLUE() { return { 0.0f, 0.0f, 1.0f }; }
static const ColorRGB BLUEISH() { return { 0.5f, 0.5f, 1.0f }; }
static const ColorRGB CYAN() { return { 0.0f, 1.0f, 1.0f }; }
static const ColorRGB DARK_GRAY() { return { 0.25f, 0.25f, 0.25f }; }
static const ColorRGB DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f }; }
static const ColorRGB GRAY() { return { 0.5f, 0.5f, 0.5f }; }
static const ColorRGB GREEN() { return { 0.0f, 1.0f, 0.0f }; }
static const ColorRGB GREENISH() { return { 0.5f, 1.0f, 0.5f }; }
static const ColorRGB LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f }; }
static const ColorRGB MAGENTA() { return { 1.0f, 0.0f, 1.0f }; }
static const ColorRGB ORANGE() { return { 0.92f, 0.50f, 0.26f }; }
static const ColorRGB RED() { return { 1.0f, 0.0f, 0.0f }; }
static const ColorRGB REDISH() { return { 1.0f, 0.5f, 0.5f }; }
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255}; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }
static const ColorRGB Z() { return { 0.0f, 0.0f, 0.75f }; }
};
class ColorRGBA
{
std::array<float, 4> m_data{ 1.0f, 1.0f, 1.0f, 1.0f };
public:
ColorRGBA() = default;
ColorRGBA(float r, float g, float b, float a);
ColorRGBA(std::array<float, 4> color);
ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
ColorRGBA(const ColorRGBA& other) = default;
ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGBA& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGBA& other) const { return !operator==(other); }
bool operator < (const ColorRGBA& other) const;
bool operator > (const ColorRGBA& other) const;
ColorRGBA operator + (const ColorRGBA& other) const;
ColorRGBA operator * (float value) const;
const float* const data() const { return m_data.data(); }
const std::array<float, 4> &get_data() const { return m_data; }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
float a() const { return m_data[3]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void a(float a) { m_data[3] = std::clamp(a, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 3);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
unsigned char a_uchar() const { return static_cast<unsigned char>(m_data[3] * 255.0f); }
bool is_transparent() const { return m_data[3] < 1.0f; }
static const ColorRGBA BLACK() { return { 0.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA BLUE() { return { 0.0f, 0.0f, 1.0f, 1.0f }; }
static const ColorRGBA BLUEISH() { return { 0.5f, 0.5f, 1.0f, 1.0f }; }
static const ColorRGBA CYAN() { return { 0.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA DARK_GRAY() { return { 0.25f, 0.25f, 0.25f, 1.0f }; }
static const ColorRGBA DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f, 1.0f }; }
static const ColorRGBA GRAY() { return { 0.5f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA GREEN() { return { 0.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA GREENISH() { return { 0.5f, 1.0f, 0.5f, 1.0f }; }
static const ColorRGBA LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f, 1.0f }; }
static const ColorRGBA MAGENTA() { return { 1.0f, 0.0f, 1.0f, 1.0f }; }
static const ColorRGBA ORANGE() { return { 0.923f, 0.504f, 0.264f, 1.0f }; }
static const ColorRGBA RED() { return { 1.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA REDISH() { return { 1.0f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA YELLOW() { return { 1.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA WHITE() { return { 1.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255, 1.0f}; }
static const ColorRGBA X() { return { 0.75f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA Y() { return { 0.0f, 0.75f, 0.0f, 1.0f }; }
static const ColorRGBA Z() { return { 0.0f, 0.0f, 0.75f, 1.0f }; }
};
ColorRGB operator * (float value, const ColorRGB& other);
ColorRGBA operator * (float value, const ColorRGBA& other);
ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t);
ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t);
ColorRGB complementary(const ColorRGB& color);
ColorRGBA complementary(const ColorRGBA& color);
ColorRGB saturate(const ColorRGB& color, float factor);
ColorRGBA saturate(const ColorRGBA& color, float factor);
ColorRGB opposite(const ColorRGB& color);
ColorRGB opposite(const ColorRGB& a, const ColorRGB& b);
bool can_decode_color(const std::string& color);
bool decode_color(const std::string& color_in, ColorRGB& color_out);
bool decode_color(const std::string& color_in, ColorRGBA& color_out);
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out);
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out);
std::string encode_color(const ColorRGB& color);
std::string encode_color(const ColorRGBA& color);
ColorRGB to_rgb(const ColorRGBA& other_rgba);
ColorRGBA to_rgba(const ColorRGB& other_rgb);
ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha);
ColorRGBA picking_decode(unsigned int id);
unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b);
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace Slic3r
#endif /* slic3r_Color_hpp_ */

View File

@ -108,30 +108,32 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
return out;
}
Circled circle_ransac(const Vec2ds& input, size_t iterations)
Circled circle_ransac(const Vec2ds &input, size_t iterations, double *min_error)
{
if (input.size() < 3)
return Circled::make_invalid();
std::mt19937 rng;
std::mt19937 rng;
std::vector<Vec2d> samples;
Circled circle_best = Circled::make_invalid();
double err_min = std::numeric_limits<double>::max();
for (size_t iter = 0; iter < iterations; ++ iter) {
Circled circle_best = Circled::make_invalid();
double err_min = std::numeric_limits<double>::max();
for (size_t iter = 0; iter < iterations; ++iter) {
samples.clear();
std::sample(input.begin(), input.end(), std::back_inserter(samples), 3, rng);
Circled c;
c.center = Geometry::circle_center(samples[0], samples[1], samples[2], EPSILON);
c.radius = std::accumulate(input.begin(), input.end(), 0., [&c](double acc, const Vec2d& pt) { return (pt - c.center).norm() + acc; });
c.radius = std::accumulate(input.begin(), input.end(), 0., [&c](double acc, const Vec2d &pt) { return (pt - c.center).norm() + acc; });
c.radius /= double(input.size());
double err = 0;
for (const Vec2d &pt : input)
err = std::max(err, std::abs((pt - c.center).norm() - c.radius));
if (err < err_min) {
err_min = err;
err_min = err;
circle_best = c;
}
}
if (min_error)
*min_error = err_min;
return circle_best;
}

View File

@ -102,7 +102,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
// Find circle using RANSAC randomized algorithm.
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20);
Circled circle_ransac(const Vec2ds &input, size_t iterations = 20, double *min_error = nullptr);
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
template<typename Vector, typename Points>

1247
src/libslic3r/Measure.cpp Normal file

File diff suppressed because it is too large Load Diff

195
src/libslic3r/Measure.hpp Normal file
View File

@ -0,0 +1,195 @@
///|/ Copyright (c) Prusa Research 2022 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_Measure_hpp_
#define Slic3r_Measure_hpp_
#include <optional>
#include <memory>
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
Edge = 1 << 1,
Circle = 1 << 2,
Plane = 1 << 3
};
class SurfaceFeature {
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
void translate(const Vec3d& displacement);
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
// For points, return the point.
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
// For edges, return start and end.
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
// For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
std::tuple<int, Vec3d, Vec3d> get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); }
// For anything, return an extra point that should also be considered a part of this.
std::optional<Vec3d> get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; }
bool operator == (const SurfaceFeature& other) const {
if (this->m_type != other.m_type) return false;
switch (this->m_type)
{
case SurfaceFeatureType::Undef: { break; }
case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); }
case SurfaceFeatureType::Edge: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) ||
(this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1));
}
case SurfaceFeatureType::Plane:
case SurfaceFeatureType::Circle: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON);
}
}
return false;
}
bool operator != (const SurfaceFeature& other) const {
return !operator == (other);
}
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
Vec3d m_pt2{ Vec3d::Zero() };
std::optional<Vec3d> m_pt3;
double m_value{ 0.0 };
};
class MeasuringImpl;
class Measuring {
public:
// Construct the measurement object on a given its.
explicit Measuring(const indexed_triangle_set& its);
~Measuring();
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
// Return total number of planes.
int get_num_of_planes() const;
// Returns a list of triangle indices for given plane.
const std::vector<int>& get_plane_triangle_indices(int idx) const;
// Returns the surface features of the plane with the given index
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
std::unique_ptr<MeasuringImpl> priv;
};
struct DistAndPoints {
DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {}
double dist;
Vec3d from;
Vec3d to;
};
struct AngleAndEdges {
AngleAndEdges(double angle_, const Vec3d& center_, const std::pair<Vec3d, Vec3d>& e1_, const std::pair<Vec3d, Vec3d>& e2_, double radius_, bool coplanar_)
: angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {}
double angle;
Vec3d center;
std::pair<Vec3d, Vec3d> e1;
std::pair<Vec3d, Vec3d> e2;
double radius;
bool coplanar;
static const AngleAndEdges Dummy;
};
struct MeasurementResult {
std::optional<AngleAndEdges> angle;
std::optional<DistAndPoints> distance_infinite;
std::optional<DistAndPoints> distance_strict;
std::optional<Vec3d> distance_xyz;
bool has_distance_data() const {
return distance_infinite.has_value() || distance_strict.has_value();
}
bool has_any_data() const {
return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value();
}
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
inline Vec3d edge_direction(const SurfaceFeature& edge) {
assert(edge.get_type() == SurfaceFeatureType::Edge);
return edge_direction(edge.get_edge());
}
inline Vec3d plane_normal(const SurfaceFeature& plane) {
assert(plane.get_type() == SurfaceFeatureType::Plane);
return std::get<1>(plane.get_plane());
}
inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; }
inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; }
inline bool are_parallel(const std::pair<Vec3d, Vec3d>& e1, const std::pair<Vec3d, Vec3d>& e2) {
return are_parallel(e1.second - e1.first, e2.second - e2.first);
}
inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_parallel(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_perpendicular(edge_direction(f1), plane_normal(f2));
else
return false;
}
inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_perpendicular(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_parallel(edge_direction(f1), plane_normal(f2));
else
return false;
}
} // namespace Measure
} // namespace Slic3r
#endif // Slic3r_Measure_hpp_

View File

@ -0,0 +1,390 @@
///|/ Copyright (c) Prusa Research 2022 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_MeasureUtils_hpp_
#define Slic3r_MeasureUtils_hpp_
#include <initializer_list>
namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
{
public:
Polynomial1(std::initializer_list<double> values)
{
// C++ 11 will call the default constructor for
// Polynomial1<Real> p{}, so it is guaranteed that
// values.size() > 0.
m_coefficient.resize(values.size());
std::copy(values.begin(), values.end(), m_coefficient.begin());
EliminateLeadingZeros();
}
// Construction and destruction. The first constructor creates a
// polynomial of the specified degree but sets all coefficients to
// zero (to ensure initialization). You are responsible for setting
// the coefficients, presumably with the degree-term set to a nonzero
// number. In the second constructor, the degree is the number of
// initializers plus 1, but then adjusted so that coefficient[degree]
// is not zero (unless all initializer values are zero).
explicit Polynomial1(uint32_t degree)
: m_coefficient(static_cast<size_t>(degree) + 1, 0.0)
{}
// Eliminate any leading zeros in the polynomial, except in the case
// the degree is 0 and the coefficient is 0. The elimination is
// necessary when arithmetic operations cause a decrease in the degree
// of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) =
// (2 + 3*x). The inputs both have degree 2, so the result is created
// with degree 2. After the addition we find that the degree is in
// fact 1 and resize the array of coefficients. This function is
// called internally by the arithmetic operators, but it is exposed in
// the public interface in case you need it for your own purposes.
void EliminateLeadingZeros()
{
const size_t size = m_coefficient.size();
if (size > 1) {
const double zero = 0.0;
int32_t leading;
for (leading = static_cast<int32_t>(size) - 1; leading > 0; --leading) {
if (m_coefficient[leading] != zero)
break;
}
m_coefficient.resize(++leading);
}
}
// Set all coefficients to the specified value.
void SetCoefficients(double value)
{
std::fill(m_coefficient.begin(), m_coefficient.end(), value);
}
inline uint32_t GetDegree() const
{
// By design, m_coefficient.size() > 0.
return static_cast<uint32_t>(m_coefficient.size() - 1);
}
inline const double& operator[](uint32_t i) const { return m_coefficient[i]; }
inline double& operator[](uint32_t i) { return m_coefficient[i]; }
// Evaluate the polynomial. If the polynomial is invalid, the
// function returns zero.
double operator()(double t) const
{
int32_t i = static_cast<int32_t>(m_coefficient.size());
double result = m_coefficient[--i];
for (--i; i >= 0; --i) {
result *= t;
result += m_coefficient[i];
}
return result;
}
protected:
// The class is designed so that m_coefficient.size() >= 1.
std::vector<double> m_coefficient;
};
inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
Polynomial1 result(p0Degree + p1Degree);
result.SetCoefficients(0.0);
for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) {
for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) {
result[i0 + i1] += p0[i0] * p1[i1];
}
}
return result;
}
inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = -p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator * (double scalar, const Polynomial1& p)
{
const uint32_t degree = p.GetDegree();
Polynomial1 result(degree);
for (uint32_t i = 0; i <= degree; ++i) {
result[i] = scalar * p[i];
}
return result;
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
{
public:
// General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c'
// must have at least d+1 elements and the output array 'root' must
// have at least d elements.
// Find the roots on (-infinity,+infinity).
static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots)
{
if (degree >= 0 && c != nullptr) {
const double zero = 0.0;
while (degree >= 0 && c[degree] == zero) {
--degree;
}
if (degree > 0) {
// Compute the Cauchy bound.
const double one = 1.0;
const double invLeading = one / c[degree];
double maxValue = zero;
for (int32_t i = 0; i < degree; ++i) {
const double value = std::fabs(c[i] * invLeading);
if (value > maxValue)
maxValue = value;
}
const double bound = one + maxValue;
return FindRecursive(degree, c, -bound, bound, maxIterations, roots);
}
else if (degree == 0)
// The polynomial is a nonzero constant.
return 0;
else {
// The polynomial is identically zero.
roots[0] = zero;
return 1;
}
}
else
// Invalid degree or c.
return 0;
}
// If you know that p(tmin) * p(tmax) <= 0, then there must be at
// least one root in [tmin, tmax]. Compute it using bisection.
static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root)
{
const double zero = 0.0;
double pmin = Evaluate(degree, c, tmin);
if (pmin == zero) {
root = tmin;
return true;
}
double pmax = Evaluate(degree, c, tmax);
if (pmax == zero) {
root = tmax;
return true;
}
if (pmin * pmax > zero)
// It is not known whether the interval bounds a root.
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
root = 0.5 * (tmin + tmax);
// This test is designed for 'float' or 'double' when tmin
// and tmax are consecutive floating-point numbers.
if (root == tmin || root == tmax)
break;
const double p = Evaluate(degree, c, root);
const double product = p * pmin;
if (product < zero) {
tmax = root;
pmax = p;
}
else if (product > zero) {
tmin = root;
pmin = p;
}
else
break;
}
return true;
}
// Support for the Find functions.
static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots)
{
// The base of the recursion.
const double zero = 0.0;
double root = zero;
if (degree == 1) {
int32_t numRoots;
if (c[1] != zero) {
root = -c[0] / c[1];
numRoots = 1;
}
else if (c[0] == zero) {
root = zero;
numRoots = 1;
}
else
numRoots = 0;
if (numRoots > 0 && tmin <= root && root <= tmax) {
roots[0] = root;
return 1;
}
return 0;
}
// Find the roots of the derivative polynomial scaled by 1/degree.
// The scaling avoids the factorial growth in the coefficients;
// for example, without the scaling, the high-order term x^d
// becomes (d!)*x through multiple differentiations. With the
// scaling we instead get x. This leads to better numerical
// behavior of the root finder.
const int32_t derivDegree = degree - 1;
std::vector<double> derivCoeff(static_cast<size_t>(derivDegree) + 1);
std::vector<double> derivRoots(derivDegree);
for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) {
derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree;
}
const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]);
int32_t numRoots = 0;
if (numDerivRoots > 0) {
// Find root on [tmin,derivRoots[0]].
if (Find(degree, c, tmin, derivRoots[0], maxIterations, root))
roots[numRoots++] = root;
// Find root on [derivRoots[i],derivRoots[i+1]].
for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) {
if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root))
roots[numRoots++] = root;
}
// Find root on [derivRoots[numDerivRoots-1],tmax].
if (Find(degree, c, derivRoots[static_cast<size_t>(numDerivRoots) - 1], tmax, maxIterations, root))
roots[numRoots++] = root;
}
else {
// The polynomial is monotone on [tmin,tmax], so has at most one root.
if (Find(degree, c, tmin, tmax, maxIterations, root))
roots[numRoots++] = root;
}
return numRoots;
}
static double Evaluate(int32_t degree, const double* c, double t)
{
int32_t i = degree;
double result = c[i];
while (--i >= 0) {
result = t * result + c[i];
}
return result;
}
};
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If
// the maximum absolute component occurs at index i, then the orthogonal
// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components
// zero. The index addition i+1 is computed modulo N.
inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength)
{
double cmax = std::fabs(v[0]);
int32_t imax = 0;
for (int32_t i = 1; i < 3; ++i) {
double c = std::fabs(v[i]);
if (c > cmax) {
cmax = c;
imax = i;
}
}
Vec3d result = Vec3d::Zero();
int32_t inext = imax + 1;
if (inext == 3)
inext = 0;
result[imax] = v[inext];
result[inext] = -v[imax];
if (unitLength) {
const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext];
const double invLength = 1.0 / std::sqrt(sqrDistance);
result[imax] *= invLength;
result[inext] *= invLength;
}
return result;
}
} // namespace Slic3r
} // namespace Measure
#endif // Slic3r_MeasureUtils_hpp_

View File

@ -45,6 +45,7 @@ using Vec3f = Eigen::Matrix<float, 3, 1, Eigen::DontAlign>;
using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
// BBS
using Vec4f = Eigen::Matrix<float, 4, 1, Eigen::DontAlign>;
using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
using Points = std::vector<Point>;
@ -70,7 +71,6 @@ using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAli
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
using ColorRGBA = std::array<float, 4>;
// I don't know why Eigen::Transform::Identity() return a const object...
template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::Identity(); }
inline const auto &identity3f = identity<3, float>;

View File

@ -0,0 +1,167 @@
///|/ Copyright (c) Prusa Research 2022 Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_SurfaceMesh_hpp_
#define slic3r_SurfaceMesh_hpp_
#include <admesh/stl.h>
#include <libslic3r/TriangleMesh.hpp>
#include "boost/container/small_vector.hpp"
namespace Slic3r {
class TriangleMesh;
enum Face_index : int;
class Halfedge_index {
friend class SurfaceMesh;
public:
Halfedge_index() : m_face(Face_index(-1)), m_side(0) {}
Face_index face() const { return m_face; }
unsigned char side() const { return m_side; }
bool is_invalid() const { return int(m_face) < 0; }
bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); }
bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; }
private:
Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {}
Face_index m_face;
unsigned char m_side;
};
class Vertex_index {
friend class SurfaceMesh;
public:
Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {}
bool is_invalid() const { return int(m_face) < 0; }
bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex.
private:
Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {}
Face_index m_face;
unsigned char m_vertex_idx;
};
class SurfaceMesh {
public:
explicit SurfaceMesh(const indexed_triangle_set& its)
: m_its(its),
m_face_neighbors(its_face_neighbors_par(its))
{}
SurfaceMesh(const SurfaceMesh&) = delete;
SurfaceMesh& operator=(const SurfaceMesh&) = delete;
Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); }
Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); }
Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; }
Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; }
Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; }
Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); }
Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); }
Halfedge_index opposite(Halfedge_index h) const {
if (h.is_invalid())
return h;
int face_idx = m_face_neighbors[h.m_face][h.m_side];
Halfedge_index h_candidate = halfedge(Face_index(face_idx));
if (h_candidate.is_invalid())
return Halfedge_index(); // invalid
for (int i=0; i<3; ++i) {
if (is_same_vertex(source(h_candidate), target(h))) {
// Meshes in PrusaSlicer should be fixed enough for the following not to happen.
assert(is_same_vertex(target(h_candidate), source(h)));
return h_candidate;
}
h_candidate = next(h_candidate);
}
return Halfedge_index(); // invalid
}
Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); }
Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); }
Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); }
Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); }
Halfedge_index halfedge(Vertex_index source, Vertex_index target) const
{
Halfedge_index hi(source.m_face, source.m_vertex_idx);
assert(! hi.is_invalid());
const Vertex_index orig_target = this->target(hi);
Vertex_index current_target = orig_target;
while (! is_same_vertex(current_target, target)) {
hi = next_around_source(hi);
if (hi.is_invalid())
break;
current_target = this->target(hi);
if (is_same_vertex(current_target, orig_target))
return Halfedge_index(); // invalid
}
return hi;
}
const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; }
size_t degree(Vertex_index v) const
{
// In case the mesh is broken badly, the loop might end up to be infinite,
// never getting back to the first halfedge. Remember list of all half-edges
// and trip if any is encountered for the second time.
Halfedge_index h_first = halfedge(v);
boost::container::small_vector<Halfedge_index, 10> he_visited;
Halfedge_index h = next_around_target(h_first);
size_t degree = 2;
while (! h.is_invalid() && h != h_first) {
he_visited.emplace_back(h);
h = next_around_target(h);
if (std::find(he_visited.begin(), he_visited.end(), h) == he_visited.end())
return 0;
++degree;
}
return h.is_invalid() ? 0 : degree - 1;
}
size_t degree(Face_index f) const {
size_t total = 0;
for (unsigned char i=0; i<3; ++i) {
size_t d = degree(Vertex_index(f, i));
if (d == 0)
return 0;
total += d;
}
assert(total - 6 >= 0);
return total - 6; // we counted 3 halfedges from f, and one more for each neighbor
}
bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; }
bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; }
Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; }
private:
const std::vector<Vec3i> m_face_neighbors;
const indexed_triangle_set& m_its;
};
} //namespace Slic3r
#endif // slic3r_SurfaceMesh_hpp_

View File

@ -2,6 +2,7 @@
#define slic3r_GLModel_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Color.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <vector>
#include <string>

View File

@ -488,6 +488,15 @@ public:
~TaskTimer();
};
class KeyAutoRepeatFilter
{
size_t m_count{0};
public:
void increase_count() { ++m_count; }
void reset_count() { m_count = 0; }
bool is_first() const { return m_count == 0; }
};
/* Image Generator */
#define _3MF_COVER_SIZE wxSize(240, 240)

View File

@ -32,29 +32,12 @@ const int c_connectors_start_id = c_connectors_group_id - c_cube_z_move_id;
const float UndefFloat = -999.f;
// connector colors
static const ColorRGBA BLACK() { return {0.0f, 0.0f, 0.0f, 1.0f}; }
static const ColorRGBA BLUE() { return {0.0f, 0.0f, 1.0f, 1.0f}; }
static const ColorRGBA BLUEISH() { return {0.5f, 0.5f, 1.0f, 1.0f}; }
static const ColorRGBA CYAN() { return {0.0f, 1.0f, 1.0f, 1.0f}; }
static const ColorRGBA DARK_GRAY() { return {0.25f, 0.25f, 0.25f, 1.0f}; }
static const ColorRGBA DARK_YELLOW() { return {0.5f, 0.5f, 0.0f, 1.0f}; }
static const ColorRGBA GRAY() { return {0.5f, 0.5f, 0.5f, 1.0f}; }
static const ColorRGBA GREEN() { return {0.0f, 1.0f, 0.0f, 1.0f}; }
static const ColorRGBA GREENISH() { return {0.5f, 1.0f, 0.5f, 1.0f}; }
static const ColorRGBA LIGHT_GRAY() { return {0.75f, 0.75f, 0.75f, 1.0f}; }
static const ColorRGBA MAGENTA() { return {1.0f, 0.0f, 1.0f, 1.0f}; }
static const ColorRGBA ORANGE() { return {0.923f, 0.504f, 0.264f, 1.0f}; }
static const ColorRGBA RED() { return {1.0f, 0.0f, 0.0f, 1.0f}; }
static const ColorRGBA REDISH() { return {1.0f, 0.5f, 0.5f, 1.0f}; }
static const ColorRGBA YELLOW() { return {1.0f, 1.0f, 0.0f, 1.0f}; }
static const ColorRGBA WHITE() { return {1.0f, 1.0f, 1.0f, 1.0f}; }
static const ColorRGBA PLAG_COLOR = YELLOW();
static const ColorRGBA DOWEL_COLOR = DARK_YELLOW();
static const ColorRGBA HOVERED_PLAG_COLOR = CYAN();
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW();
static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN();
static const ColorRGBA HOVERED_DOWEL_COLOR = {0.0f, 0.5f, 0.5f, 1.0f};
static const ColorRGBA SELECTED_PLAG_COLOR = GRAY();
static const ColorRGBA SELECTED_DOWEL_COLOR = GRAY(); // DARK_GRAY();
static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY();
static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::GRAY(); // DARK_GRAY();
static const ColorRGBA CONNECTOR_DEF_COLOR = {1.0f, 1.0f, 1.0f, 0.5f};
static const ColorRGBA CONNECTOR_ERR_COLOR = {1.0f, 0.3f, 0.3f, 0.5f};
static const ColorRGBA HOVERED_ERR_COLOR = {1.0f, 0.3f, 0.3f, 1.0f};
@ -62,8 +45,8 @@ static const ColorRGBA HOVERED_ERR_COLOR = {1.0f, 0.3f, 0.3f, 1.0f};
static const ColorRGBA CUT_PLANE_DEF_COLOR = {0.9f, 0.9f, 0.9f, 0.5f};
static const ColorRGBA CUT_PLANE_ERR_COLOR = {1.0f, 0.8f, 0.8f, 0.5f};
static const ColorRGBA UPPER_PART_COLOR = CYAN();
static const ColorRGBA LOWER_PART_COLOR = MAGENTA();
static const ColorRGBA UPPER_PART_COLOR = ColorRGBA::CYAN();
static const ColorRGBA LOWER_PART_COLOR = ColorRGBA::MAGENTA();
static const ColorRGBA MODIFIER_COLOR = {0.75f, 0.75f, 0.75f, 0.5f};
static Vec3d rotate_vec3d_around_vec3d_with_rotate_matrix(
@ -351,27 +334,6 @@ bool GLGizmoAdvancedCut::unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &p
return true;
}
void GLGizmoAdvancedCut::render_glmodel(GLModel &model, const std::array<float, 4> &color, Transform3d view_model_matrix, bool for_picking)
{
glPushMatrix();
GLShaderProgram *shader = nullptr;
if (for_picking)
shader = wxGetApp().get_shader("cali");
else
shader = wxGetApp().get_shader("gouraud_light");
if (shader) {
shader->start_using();
glsafe(::glMultMatrixd(view_model_matrix.data()));
model.set_color(-1, color);
model.render();
shader->stop_using();
}
glPopMatrix();
}
void GLGizmoAdvancedCut::reset_cut_plane()
{
m_transformed_bounding_box = transformed_bounding_box(m_bb_center);
@ -1143,9 +1105,9 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers()
bool is_valid = can_perform_cut() && has_valid_groove();
ColorRGBA cp_clr = is_valid ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR;
if (m_cut_mode == CutMode::cutTongueAndGroove) {
cp_clr[3] = cp_clr[3] - 0.1f; // cp_clr.a(cp_clr.a() - 0.1f);
cp_clr.a(cp_clr.a() - 0.1f);
}
render_glmodel(m_plane, cp_clr, Geometry::translation_transform(m_plane_center) * m_rotate_matrix);
render_glmodel(m_plane, cp_clr.get_data(), Geometry::translation_transform(m_plane_center) * m_rotate_matrix);
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_CULL_FACE));
@ -1277,7 +1239,7 @@ void GLGizmoAdvancedCut::render_connectors()
const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf;
render_glmodel(m_shapes[connector.attribs], render_color, view_model_matrix);
render_glmodel(m_shapes[connector.attribs], render_color.get_data(), view_model_matrix);
}
}
@ -1878,13 +1840,12 @@ bool GLGizmoAdvancedCut::render_cut_mode_combo(double label_width, float item_wi
void GLGizmoAdvancedCut::render_color_marker(float size, const ColorRGBA &color)
{
auto to_ImU32 = [](const ColorRGBA &color) -> ImU32 { return ImGui::GetColorU32({color[0], color[1], color[2], color[3]}); };
ImGui::SameLine();
const float radius = 0.5f * size;
ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
pos.x += 3 * m_imgui->scaled(1 / 15.0f);
pos.y += 1.25f * radius;
ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, to_ImU32(color), 6);
ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, ImGuiWrapper::to_ImU32(color), 6);
}
void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float bottom_limit)
@ -2699,7 +2660,7 @@ void PartSelection::part_render(const Vec3d *normal)
for (size_t id = 0; id < m_cut_parts.size(); ++id) { // m_parts.size() test
if (normal && ((is_looking_forward && m_cut_parts[id].is_up_part) || (!is_looking_forward && !m_cut_parts[id].is_up_part)))
continue;
GLGizmoAdvancedCut::render_glmodel(m_cut_parts[id].glmodel, m_cut_parts[id].is_up_part ? UPPER_PART_COLOR : LOWER_PART_COLOR, m_cut_parts[id].trans);
GLGizmoBase::render_glmodel(m_cut_parts[id].glmodel, m_cut_parts[id].is_up_part ? UPPER_PART_COLOR.get_data() : LOWER_PART_COLOR.get_data(), m_cut_parts[id].trans);
}
}

View File

@ -218,7 +218,7 @@ public:
bool unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &pos, Vec3d &pos_world, bool respect_contours = true);
virtual bool apply_clipping_plane() { return m_connectors_editing; }
static void render_glmodel(GLModel &model, const std::array<float, 4> &color, Transform3d view_model_matrix, bool for_picking = false);
protected:
virtual bool on_init();
virtual void on_load(cereal::BinaryInputArchive &ar) override;

View File

@ -424,7 +424,26 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
}
}
void GLGizmoBase::render_glmodel(GLModel &model, const std::array<float, 4> &color, Transform3d view_model_matrix, bool for_picking, float emission_factor)
{
glPushMatrix();
GLShaderProgram *shader = nullptr;
if (for_picking)
shader = wxGetApp().get_shader("cali");
else
shader = wxGetApp().get_shader("gouraud_light");
if (shader) {
shader->start_using();
shader->set_uniform("emission_factor", emission_factor);
glsafe(::glMultMatrixd(view_model_matrix.data()));
model.set_color(-1, color);
model.render();
shader->stop_using();
}
glPopMatrix();
}
std::string GLGizmoBase::get_name(bool include_shortcut) const
{

View File

@ -2,6 +2,7 @@
#define slic3r_GLGizmoBase_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Color.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GLModel.hpp"
@ -215,7 +216,7 @@ public:
/// </summary>
virtual void data_changed(bool is_serializing){};
int get_count() { return ++count; }
static void render_glmodel(GLModel &model, const std::array<float, 4> &color, Transform3d view_model_matrix, bool for_picking = false, float emission_factor = 0.0f);
protected:
float last_input_window_width = 0;
virtual bool on_init() = 0;

View File

@ -80,6 +80,8 @@ static const std::map<const wchar_t, std::string> font_icons = {
{ImGui::AddFilamentDarkIcon , "add_filament_dark"},
{ImGui::DeleteFilamentIcon , "delete_filament"},
{ImGui::DeleteFilamentDarkIcon , "delete_filament_dark"},
{ImGui::ClipboardBtnIcon , "copy_menu"},
{ImGui::ClipboardBtnDarkIcon , "copy_menu_dark"},
{ImGui::CircleButtonDarkIcon , "circle_paint_dark" },
{ImGui::TriangleButtonDarkIcon , "triangle_paint_dark" },
@ -148,7 +150,6 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED;
//BBS
const ImVec4 ImGuiWrapper::COL_BLUE_LIGHT = ImVec4(0.122f, 0.557f, 0.918f, 1.0f);
const ImVec4 ImGuiWrapper::COL_GREEN_LIGHT = ImVec4(0.86f, 0.99f, 0.91f, 1.0f);
const ImVec4 ImGuiWrapper::COL_HOVER = { 0.933f, 0.933f, 0.933f, 1.0f };
@ -158,6 +159,7 @@ const ImVec4 ImGuiWrapper::COL_SEPARATOR_DARK = { 0.24f, 0.24f, 0.27f, 1.0f }
const ImVec4 ImGuiWrapper::COL_TITLE_BG = { 0.745f, 0.745f, 0.745f, 1.0f };
const ImVec4 ImGuiWrapper::COL_WINDOW_BG = { 1.000f, 1.000f, 1.000f, 1.0f };
const ImVec4 ImGuiWrapper::COL_WINDOW_BG_DARK = { 45 / 255.f, 45 / 255.f, 49 / 255.f, 1.f };
const ImVec4 ImGuiWrapper::COL_BAMBU = {0.0f, 150.f / 255.0f, 136.0f / 255, 1.0f};
int ImGuiWrapper::TOOLBAR_WINDOW_FLAGS = ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_NoMove
@ -800,11 +802,102 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
return ImGui::RadioButton(label_utf8.c_str(), active);
}
bool ImGuiWrapper::image_button()
ImFontAtlasCustomRect *ImGuiWrapper::GetTextureCustomRect(const wchar_t &tex_id)
{
return false;
auto item = m_custom_glyph_rects_ids.find(tex_id);
return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr;
}
ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA &color)
{
return ImGui::GetColorU32({color.r(), color.g(), color.b(), color.a()});
}
ImVec4 ImGuiWrapper::to_ImVec4(const ColorRGBA &color) {
return {color.r(), color.g(), color.b(), color.a()};
}
ColorRGBA ImGuiWrapper::from_ImU32(const ImU32 &color)
{
return from_ImVec4(ImGui::ColorConvertU32ToFloat4(color));
}
ColorRGBA ImGuiWrapper::from_ImVec4(const ImVec4 &color)
{
return {color.x, color.y, color.z, color.w};
}
static bool image_button_ex(ImGuiID id,
ImTextureID texture_id,
const ImVec2 & size,
const ImVec2 & uv0,
const ImVec2 & uv1,
const ImVec2 & padding,
const ImVec4 & bg_col,
const ImVec4 & tint_col,
ImGuiButtonFlags flags)
{
ImGuiContext &g = *GImGui;
ImGuiWindow * window = ImGui::GetCurrentWindow();
if (window->SkipItems) return false;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
ImGui::ItemSize(bb);
if (!ImGui::ItemAdd(bb, id)) return false;
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
// Render
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImGui::RenderNavHighlight(bb, id);
ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float) ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui::GetColorU32(bg_col));
window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui::GetColorU32(tint_col));
return pressed;
}
bool ImGuiWrapper::image_button(
ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col, ImGuiButtonFlags flags)
{
ImGuiContext &g = *GImGui;
ImGuiWindow * window = g.CurrentWindow;
if (window->SkipItems)
return false;
// Default to using texture ID as ID. User can still push string/integer prefixes.
ImGui::PushID((void *) (intptr_t) user_texture_id);
const ImGuiID id = window->GetID("#image");
ImGui::PopID();
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float) frame_padding, (float) frame_padding) : g.Style.FramePadding;
return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags);
}
bool ImGuiWrapper::image_button(const wchar_t icon, const wxString &tooltip)
{
const ImGuiIO & io = ImGui::GetIO();
const ImTextureID tex_id = io.Fonts->TexID;
assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0);
const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth);
const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight);
const ImFontAtlasCustomRect *const rect = GetTextureCustomRect(icon);
const ImVec2 size = {float(rect->Width), float(rect->Height)};
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
ImGui::PushStyleColor(ImGuiCol_Button, {0.25f, 0.25f, 0.25f, 0.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, {0.4f, 0.4f, 0.4f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0.25f, 0.25f, 0.25f, 1.0f});
const bool res = image_button(tex_id, size, uv0, uv1);
ImGui::PopStyleColor(3);
if (!tooltip.empty() && ImGui::IsItemHovered()) this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f);
return res;
}
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
{
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
@ -1791,6 +1884,11 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
}
void ImGuiWrapper::disable_background_fadeout_animation()
{
GImGui->DimBgRatio = 1.0f;
}
#ifdef __APPLE__
static const ImWchar ranges_keyboard_shortcuts[] =
{
@ -2150,11 +2248,11 @@ void ImGuiWrapper::init_font(bool compress)
int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next
// add rectangles for the icons to the font atlas
for (auto& icon : font_icons)
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
for (auto& icon : font_icons_large)
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
for (auto& icon : font_icons_extra_large)
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
// Build texture atlas
unsigned char* pixels;

View File

@ -9,6 +9,7 @@
#include <wx/string.h>
#include "libslic3r/Point.hpp"
#include "libslic3r/Color.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp"
namespace Slic3r {namespace Search {
@ -59,6 +60,7 @@ class ImGuiWrapper
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
bool m_requires_extra_frame{ false };
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
std::map<wchar_t, int> m_custom_glyph_rects_ids;
std::string m_clipboard_text;
public:
@ -115,7 +117,21 @@ public:
bool bbl_button(const wxString &label);
bool button(const wxString& label, float width, float height);
bool radio_button(const wxString &label, bool active);
bool image_button();
static ImU32 to_ImU32(const ColorRGBA &color);
static ImVec4 to_ImVec4(const ColorRGBA &color);
static ColorRGBA from_ImU32(const ImU32 &color);
static ColorRGBA from_ImVec4(const ImVec4 &color);
ImFontAtlasCustomRect *GetTextureCustomRect(const wchar_t &tex_id);
bool image_button(ImTextureID user_texture_id,
const ImVec2 & size,
const ImVec2 & uv0 = ImVec2(0.0, 0.0),
const ImVec2 & uv1 = ImVec2(1.0, 1.0),
int frame_padding = -1,
const ImVec4 & bg_col = ImVec4(0.0, 0.0, 0.0, 0.0),
const ImVec4 & tint_col = ImVec4(1.0, 1.0, 1.0, 1.0),
ImGuiButtonFlags flags = 0);
bool image_button(const wchar_t icon, const wxString &tooltip = L"");
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
@ -181,6 +197,7 @@ public:
void set_requires_extra_frame() { m_requires_extra_frame = true; }
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
void disable_background_fadeout_animation();
static const ImVec4 COL_GREY_DARK;
static const ImVec4 COL_GREY_LIGHT;
@ -201,7 +218,7 @@ public:
static const ImVec4 COL_WINDOW_BG_DARK;
static const ImVec4 COL_SEPARATOR;
static const ImVec4 COL_SEPARATOR_DARK;
static const ImVec4 COL_BAMBU;
//BBS
static void on_change_color_mode(bool is_dark);
static void push_toolbar_style(const float scale);

View File

@ -88,7 +88,7 @@ void MeshClipper::render_cut(const ColorRGBA &color, const std::vector<size_t> *
if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) continue;
auto isl = m_result->cut_islands[i];
ColorRGBA gray{0.5f, 0.5f, 0.5f, 1.f};
isl->model.set_color(-1, isl->disabled ? gray : color);
isl->model.set_color(-1, isl->disabled ? gray.get_data() : color.get_data());
isl->model.render();
}
shader->stop_using();
@ -114,7 +114,7 @@ void MeshClipper::render_contour(const ColorRGBA &color, const std::vector<size_
if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) continue;
auto isl = m_result->cut_islands[i];
ColorRGBA red{1.0f, 0.f, 0.f, 1.f};
isl->model_expanded.set_color(-1, isl->disabled ? red : color);
isl->model_expanded.set_color(-1, isl->disabled ? red.get_data() : color.get_data());
isl->model_expanded.render();
}
shader->stop_using();
@ -420,6 +420,13 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
return m_normals[facet_idx];
}
MeshRaycaster::MeshRaycaster(const TriangleMesh &mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(mesh.its))
{
;
}
void MeshRaycaster::line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3d &point, Vec3d &direction)
{
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
@ -447,7 +454,7 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3
pt2 = inv * pt2;
point = pt1;
direction = pt2-pt1;
direction = (pt2-pt1).normalized();
}
@ -555,6 +562,37 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
return out;
}
bool MeshRaycaster::closest_hit(
const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3f &position, Vec3f &normal, const ClippingPlane *clipping_plane, size_t *facet_idx) const
{
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
auto hits = m_emesh.query_ray_hits(point, direction.normalized());
if (hits.empty()) return false; // no intersection found
size_t hit_id = 0;
if (clipping_plane != nullptr) {
while (hit_id < hits.size() && clipping_plane->is_point_clipped(trafo * hits[hit_id].position())) {
++hit_id;
}
}
if (hit_id == hits.size())
return false; // all points are obscured or cut by the clipping plane.
auto &hit = hits[hit_id];
position = hit.position().cast<float>();
normal = hit.normal().cast<float>();
if (facet_idx != nullptr)
*facet_idx = hit.face();
return true;
}
Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
{

View File

@ -2,6 +2,7 @@
#define slic3r_MeshUtils_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Color.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/SLA/IndexedMesh.hpp"
#include "admesh/stl.h"
@ -150,11 +151,7 @@ class MeshRaycaster {
public:
// The class references extern TriangleMesh, which must stay alive
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(mesh.its))
{
}
MeshRaycaster(const TriangleMesh &mesh);
static void line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo,
const Camera &camera, Vec3d &point, Vec3d &direction);
@ -188,6 +185,17 @@ public:
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
) const;
// Returns true if the ray, built from mouse position and camera direction, intersects the mesh.
// In this case, position and normal contain the position and normal, in model coordinates, of the intersection closest to the camera,
// depending on the position/orientation of the clipping_plane, if specified
bool closest_hit(const Vec2d & mouse_pos,
const Transform3d & trafo, // how to get the mesh into world coords
const Camera & camera, // current camera position
Vec3f & position, // where to save the positibon of the hit (mesh coords)
Vec3f & normal, // normal of the triangle that was hit
const ClippingPlane *clipping_plane = nullptr, // clipping plane (if active)
size_t * facet_idx = nullptr // index of the facet hit
) const;
// Given a point in world coords, the method returns closest point on the mesh.
// The output is in mesh coords.
// normal* can be used to also get normal of the respective triangle.
@ -204,6 +212,40 @@ private:
std::vector<stl_normal> m_normals;
};
class PickRaycaster
{
public:
//PickRaycaster(TriangleMesh *mesh) {
// mesh_raycaster = std::make_shared<MeshRaycaster>(*mesh);
//}
/*PickRaycaster(TriangleMesh *mesh, const Transform3d &tran) : PickRaycaster(mesh) {
set_transform(tran);
}*/
PickRaycaster(TriangleMesh *mesh, int _id)
{
mesh_raycaster = std::make_shared<MeshRaycaster>(*mesh);
m_id = _id;
}
PickRaycaster(TriangleMesh *mesh, int _id, const Transform3d &tran)
{
mesh_raycaster = std::make_shared<MeshRaycaster>(*mesh);
set_transform(tran);
m_id = _id;
}
void set_transform(const Transform3d &tran) {
world_tran.set_from_transform(tran);
}
std::shared_ptr<MeshRaycaster> mesh_raycaster{nullptr};
Geometry::Transformation world_tran;
bool is_active() const { return m_active; }
void set_active(bool active) { m_active = active; }
int get_id() { return m_id; }
private:
bool m_active{true};
int m_id{-1};
};
} // namespace GUI
} // namespace Slic3r

View File

@ -907,7 +907,14 @@ void Selection::move_to_center(const Vec3d& displacement, bool local)
this->set_bounding_boxes_dirty();
}
void Selection::translate(const Vec3d& displacement, bool local)
void Selection::setup_cache()
{
if (!m_valid)
return;
set_caches();
}
void Selection::translate(const Vec3d &displacement, bool local)
{
if (!m_valid)
return;

View File

@ -334,6 +334,7 @@ public:
void stop_dragging() { m_dragging = false; }
bool is_dragging() const { return m_dragging; }
void setup_cache();
void translate(const Vec3d& displacement, bool local = false);
void move_to_center(const Vec3d& displacement, bool local = false);
void rotate(const Vec3d& rotation, TransformationType transformation_type);