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:
parent
89f40dd6fc
commit
06b17c4fdc
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
|
@ -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>;
|
||||
|
|
|
@ -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_
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_GLModel_hpp_
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Color.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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,7 +212,41 @@ 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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue