Merge remote-tracking branch 'remotes/origin/fs_emboss'
This commit is contained in:
commit
9a682a10cb
124 changed files with 27968 additions and 7958 deletions
resources
data
icons
src
admesh
imgui
libslic3r
AABBTreeLines.hppAppConfig.cppAppConfig.hppBoundingBox.hppCMakeLists.txtCutSurface.cppCutSurface.hppEmboss.cppEmboss.hppExPolygon.hppExPolygonsIndex.cppExPolygonsIndex.hppExtrusionEntityCollection.hpp
Format
IntersectionPoints.cppIntersectionPoints.hppLine.hppModel.hppModelVolumeType.hppNSVGUtils.cppNSVGUtils.hppPoint.cppPoint.hppPolygon.hppQuadricEdgeCollapse.cppQuadricEdgeCollapse.hppSVG.cppSVG.hppTechnologies.hppTextConfiguration.hppTimer.cppTimer.hppTriangleMesh.cppTriangleMesh.hppTriangulation.cppTriangulation.hppUtils.hpplibslic3r.hutils.cppslic3r
CMakeLists.txt
GUI
3DScene.cppCameraUtils.cppCameraUtils.hppGLCanvas3D.cppGLCanvas3D.hppGLSelectionRectangle.cppGUI_App.cppGUI_App.hppGUI_Factories.cppGUI_Factories.hppGUI_ObjectList.cppGUI_ObjectList.hppGUI_ObjectManipulation.cpp
Gizmos
GLGizmoBase.cppGLGizmoBase.hppGLGizmoEmboss.cppGLGizmoEmboss.hppGLGizmoMmuSegmentation.cppGLGizmoMove.cppGLGizmoMove.hppGLGizmoRotate.cppGLGizmoRotate.hppGLGizmoScale.cppGLGizmoScale.hppGLGizmoSimplify.cppGLGizmoSimplify.hppGLGizmosManager.cppGLGizmosManager.hpp
ImGuiWrapper.cppImGuiWrapper.hppJobs
CreateFontNameImageJob.cppCreateFontNameImageJob.hppCreateFontStyleImagesJob.cppCreateFontStyleImagesJob.hppEmbossJob.cppEmbossJob.hppPlaterWorker.hpp
NotificationManager.cppNotificationManager.hppObjectDataViewModel.cppObjectDataViewModel.hppPlater.cppPlater.hppSelection.cpp
2555
resources/data/embossed_text.obj
Normal file
2555
resources/data/embossed_text.obj
Normal file
File diff suppressed because it is too large
Load diff
4
resources/icons/add_text_modifier.svg
Normal file
4
resources/icons/add_text_modifier.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="m 2,1 v 4 h 4 v 10 h 4 v -10 h 4 v -4 z m 1.121094,1 h 1.414062 l 2,2 H 5.115235 Z m 2.121094,0 h 1.414062 l 2.34375,2.34375 v 1.4140625 z m 2.121093,0 H 8.771485 L 10.769532,3.998 H 9.355469 Z m 2.121094,0 h 1.414063 L 12.890625,4 h -1.414062 z m 2.121094,0 H 13 L 13,3.3964375 Z M 3,2.59175 4.408203,4 H 3 Z M 7,4.4706562 9,6.4647969 V 7.8788594 L 7,5.8847188 Z M 7,6.59175 9,8.5858906 V 10 L 7,8 Z m 0,2.1210938 2,2 v 1.414063 L 7,10.126906 Z m 0,2.1210942 2,2 V 14 L 8.7611513,14 7,12.248 Z M 7,13 8,14 H 7 Z" />
|
||||||
|
<path fill="#ed6b21" d="M 3.5 10 A 2.5 2.5 0 0 0 1 12.5 A 2.5 2.5 0 0 0 3.5 15 A 2.5 2.5 0 0 0 6 12.5 A 2.5 2.5 0 0 0 3.5 10 z M 3.6445312 11 A 1.5 1.5 0 0 1 5 12.355469 L 3.6445312 11 z M 3.0117188 11 L 4.9179688 13 A 1.5 1.5 0 0 1 4.2890625 13.773438 L 2.2246094 11.708984 A 1.5 1.5 0 0 1 3 11 z M 2 12.21875 L 3.78125 14 A 1.5 1.5 0 0 1 3.5 14 A 1.5 1.5 0 0 1 2 12.5 A 1.5 1.5 0 0 1 2 12.21875 z " />
|
||||||
|
</svg>
|
After (image error) Size: 1 KiB |
4
resources/icons/add_text_negative.svg
Normal file
4
resources/icons/add_text_negative.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="M 0,0 V 6 H 5 V 16 H 10.293 11 V 15.293 6 h 5 V 0 Z m 1,1 h 14 v 3.293 l -1,-1 V 1.5 H 13 V 3 H 8 V 13 H 6.5 v 1 l 1.793,0 1,1 H 6 V 5 L 4,3 H 1.5 V 4 H 3.583984 L 4.587891,5 H 1 Z M 9.707,4 H 13.293 l 1,1 H 10.707 Z M 9,4.707 l 1,1 V 14.293 l -1,-1 z" />
|
||||||
|
<path fill="#ed6b21" d="M 1,12 C 0.5,12 0,12 0,12.5 0,13 0.5,13 1,13 h 3 c 0.5,0 1,0 1,-0.5 0,-0.5 -0.5,-0.5 -1,-0.5 z" />
|
||||||
|
</svg>
|
After (image error) Size: 498 B |
4
resources/icons/add_text_part.svg
Normal file
4
resources/icons/add_text_part.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="M 1,0 V 4.7128906 L 3.419922,7 H 7 v 7.257812 l -1,-1 V 7.5 H 5 v 6 L 7.53125,16 H 12 V 7 h 4 V 2.5 L 13.625,0 Z m 1.695312,1 h 10.580079 l 1,1 H 3.695312 Z M 2,1.703125 l 1,1 V 5.40625 l -1,-1 z M 4,3 h 11 v 3 h -4 v 9 H 8 V 6 H 4 Z" />
|
||||||
|
<path fill="#ed6b21" d="M 2.5,10 C 2,10 2,10.5 2,11 v 1 H 1 C 0.5,12 0,12 0,12.5 0,13 0.5,13 1,13 h 1 v 1 c 0,0.5 0,1 0.5,1 C 3,15 3,14.5 3,14 V 13 H 4 C 4.5,13 5,13 5,12.5 5,12 4.5,12 4,12 H 3 V 11 C 3,10.5 3,10 2.5,10 Z" />
|
||||||
|
</svg>
|
After (image error) Size: 583 B |
4
resources/icons/make_bold.svg
Normal file
4
resources/icons/make_bold.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="m 4.25,1.25 v 13.5 H 6.4238281 C 7.9550132,14.7449 9.3017782,14.70695 10.224609,14.236328 11.146154,13.766363 11.714577,12.89426 11.748047,11.001953 11.715611,9.759759 11.205071,8.9432111 10.546875,8.4101562 9.1029625,7.398789 8.344894,7.5382005 7.2507941,7.4731562 8.4769831,7.2602927 9.6564272,6.8632046 10.164063,6.1347656 10.274222,5.9766906 10.75,5.2822758 10.75,4.203125 10.75,3.2734782 10.376638,2.5663256 9.6445312,2.0566406 8.9124241,1.5469557 7.8014876,1.25 6.359375,1.25 Z M 6.3964844,1.734375 C 7.2161251,1.749043 8.1487931,1.833985 8.9101562,2.2109375 9.6715196,2.5878899 10.247584,3.3058125 10.248047,4.4316406 10.248331,5.1225624 10.039341,5.6730907 9.6875,6.0800781 9.3356593,6.4870656 8.8548801,6.7482198 8.3261719,6.9179688 7.2687554,7.25747 5.999968,7.25 5,7.25 H 4.75 V 1.7519531 L 4.9980469,1.75 C 5.3500853,1.7484474 5.8499795,1.7245951 6.3964844,1.734375 Z M 4.75,7.75 H 5 c 0.9307414,0 1.1815757,-0.01401 2.0039062,0 1.5407673,7.868e-4 2.5967243,0.4032699 3.2636718,1.0253906 0.6676,0.6227287 0.926463,1.4487864 0.929688,2.2265624 0.0033,0.784756 -0.253554,1.602823 -0.863282,2.222656 C 9.7242569,13.844443 8.7711925,14.25 7.4414062,14.25 c -0.5191067,0.0053 -1.7497727,0.0034 -2.4433593,0 L 4.75,14.248047 V 8 Z" />
|
||||||
|
<path fill="#ED6B21" d="M 3 0 L 3 16 L 6.3613281 16 C 10.584392 16.000011 13.000613 14.99955 13.066406 11.099609 C 13.000613 8.4746378 11.421875 7.5967 10.5625 7.296875 C 11 7.0001348 12 6.4851964 12 4 C 11.998007 1.2820714 9.9990238 2.4680048e-07 6.359375 0 L 3 0 z M 4 1 L 6.359375 1 C 9.3115238 1 11 2.203125 11 4.203125 C 11 6.4383214 9.96875 6.8438848 8.484375 7.375 C 9.78125 7.4092 11.934207 8.3750284 12 11 C 11.93421 14.899941 9.4906414 14.989816 6.4238281 15 L 4 15 L 4 1 z M 6.3925781 1.984375 C 5.8561577 1.9747756 5.3619154 1.9984038 5 2 L 5 7 C 7 7 9.9991037 7.0007673 9.9980469 4.4316406 C 9.997186 2.3388824 8.0018393 2.0131733 6.3925781 1.984375 z M 6 3.0058594 C 7.4476617 2.9994748 8.9991037 3.0007673 8.9824219 4.4472656 C 8.9653933 5.9238329 7.5802442 6.000795 6 6.0097656 L 6 3.0058594 z M 5 8 L 5 14 C 5.6931417 14.0034 6.9277744 14.0053 7.4414062 14 C 10.00096 14 10.95332 12.463982 10.947266 11.003906 C 10.941263 9.5560542 10 8 7 8 C 6.1813678 7.9860533 5.9332322 8 5 8 z M 6 9 C 6.9332322 9 6.5973834 8.9977721 7.4160156 9.0117188 C 8.9999302 9.0009732 9.9881376 9.5560542 9.9941406 11.003906 C 10.000194 12.463982 9.0009597 13 7.3632812 13 C 6.8496494 13.0053 6.6931417 13.0034 6 13 L 6 9 z " />
|
||||||
|
</svg>
|
After (image error) Size: 2.5 KiB |
4
resources/icons/make_italic.svg
Normal file
4
resources/icons/make_italic.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="M 3 0 L 3 2 L 5 2 L 5 13.5 L 5.1523438 13.5 L 7 6.6035156 L 7 2.5 L 6 2.5 L 6.7695312 0 L 3 0 z M 3 14 L 3 14 L 3 14 L 3 14 z " />
|
||||||
|
<path fill="#ED6B21" d="M 9,16 3,16 3.5358984,14 H 5.5358985 L 8.7512887,2 H 6.7512886 L 7.2871873,0 13.287187,0 12.751289,2 h -2 L 7.5358984,14 h 2 z" />
|
||||||
|
</svg>
|
After (image error) Size: 405 B |
4
resources/icons/make_unbold.svg
Normal file
4
resources/icons/make_unbold.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="M 3 0 L 3 16 L 6.3613281 16 C 10.584392 16.000011 13.000613 14.99955 13.066406 11.099609 C 13.000613 8.4746378 11.421875 7.5967 10.5625 7.296875 C 11 7.0001348 12 6.4851964 12 4 C 11.998007 1.2820714 9.9990238 2.4680048e-07 6.359375 0 L 3 0 z M 3.5 0.5 L 6.359375 0.5 C 7.9033731 0.5 9.1632916 0.80839367 10.072266 1.4414062 C 10.981239 2.0744189 11.5 3.0625 11.5 4.203125 C 11.5 5.397618 11.202666 6.2262446 10.654297 6.7792969 C 10.380321 7.0556129 9.9925213 7.1486579 9.6542969 7.3125 C 10.122334 7.4649494 10.58062 7.4742236 11.017578 7.828125 C 11.825156 8.4821989 12.463719 9.5408321 12.5 10.988281 L 12.5 10.998047 L 12.5 11.007812 C 12.465345 13.062131 11.72635 14.311861 10.564453 14.904297 C 9.4025567 15.4967 7.9616779 15.4949 6.4238281 15.5 L 3.5 15.5 L 3.5 0.5 z M 6.6914062 2.5117188 C 6.2771596 2.5022917 5.8703114 2.5182618 5.5 2.5253906 L 5.5 6.4511719 C 6.3843057 6.4419785 7.3560744 6.4413087 8.0976562 6.203125 C 8.5398322 6.0611055 8.8878766 5.8595959 9.1210938 5.5898438 C 9.3543108 5.3200916 9.498271 4.976613 9.4980469 4.4316406 C 9.4978035 3.8400832 9.33166 3.4925368 9.0664062 3.2246094 C 8.8011525 2.956682 8.4018312 2.7685186 7.9238281 2.6523438 C 7.5272979 2.5559702 7.1056529 2.5211458 6.6914062 2.5117188 z M 6 3.0058594 C 7.4476617 2.9994748 8.9991037 3.0007673 8.9824219 4.4472656 C 8.9653933 5.9238329 7.5802442 6.000795 6 6.0097656 L 6 3.0058594 z M 5.5 8.5 L 5.5 13.5 C 6.184944 13.502 7.0345824 13.5041 7.4355469 13.5 L 7.4375 13.5 L 7.4414062 13.5 C 8.6211924 13.5 9.3462736 13.159029 9.7988281 12.699219 C 10.251383 12.239408 10.449866 11.626375 10.447266 11.005859 C 10.44471 10.389517 10.253823 9.7888382 9.7558594 9.3242188 C 9.2578953 8.8595992 8.4135287 8.5 7 8.5 L 6.9960938 8.5 L 6.9921875 8.5 C 6.3845564 8.489648 6.0006946 8.49674 5.5 8.5 z M 6 9 C 6.9332322 9 6.5973834 8.9977721 7.4160156 9.0117188 C 8.9999302 9.0009732 9.9881376 9.5560542 9.9941406 11.003906 C 10.000194 12.463982 9.0009597 13 7.3632812 13 C 6.8496494 13.0053 6.6931417 13.0034 6 13 L 6 9 z M 4.5 14.498047 L 4.5 14.5 L 4.9980469 14.5 L 4.5 14.498047 z " />
|
||||||
|
<path fill="#ED6B21" d="m 12,11 c -0.06579,3.899941 -2.5100604,3.989816 -5.5768737,4 H 4 V 1 H 6.3603512 C 9.3125,1 11,2.203125 11,4.203125 11,6.4383214 9.96875,6.84375 8.484375,7.3748652 9.78125,7.4090652 11.934207,8.3750284 12,11 Z M 9.9989432,4.4308733 C 9.9977954,1.640529 6.4476617,1.9936154 5,2 V 7 C 7,7 10,7 9.9989432,4.4308733 Z M 10.947071,11.004336 C 10.941068,9.556484 10,8 7,8 6.1813678,7.9860533 5.9332322,8 5,8 v 6 c 0.6931417,0.0034 1.9268147,0.0053 2.4404466,0 C 10,14 10.953125,12.464412 10.947071,11.004336 Z" />
|
||||||
|
</svg>
|
After (image error) Size: 2.7 KiB |
4
resources/icons/make_unitalic.svg
Normal file
4
resources/icons/make_unitalic.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||||
|
<path fill="#808080" d="M 9.5,0 V 2.5 H 8.6171875 L 7.5,6.6777344 V 13.5 h 0.171875 L 10.751953,2 h 2 l 0.535156,-2 z" />
|
||||||
|
<path fill="#ED6B21" d="m 9,16 -6,0 L 3,14 H 5 V 2 H 3 L 3,0 9,0 V 2 H 7 V 14 h 2 z" />
|
||||||
|
</svg>
|
After (image error) Size: 305 B |
|
@ -1,4 +1,4 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
|
||||||
<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||||
<path fill="#ED6B21" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
<path fill="#ED6B21" d="M 8.5,0 C 3.804688,0 0,3.804688 0,8.5 0,13.195313 3.804688,17 8.5,17 13.195313,17 17,13.195313 17,8.5 17,3.804688 13.195313,0 8.5,0 Z m 0,15 C 4.910156,15 2,12.089844 2,8.5 2,4.910156 4.910156,2 8.5,2 12.089844,2 15,4.910156 15,8.5 15,12.089844 12.089844,15 8.5,15 Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before (image error) Size: 1.1 KiB After (image error) Size: 1 KiB |
|
@ -207,6 +207,30 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file)
|
||||||
|
{
|
||||||
|
Slic3r::CNumericLocalesSetter locales_setter;
|
||||||
|
FILE* fp = fopen(file, "w");
|
||||||
|
if (fp == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < its.vertices.size(); ++i)
|
||||||
|
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||||
|
its.vertices[i](0),
|
||||||
|
its.vertices[i](1),
|
||||||
|
its.vertices[i](2),
|
||||||
|
color[i](0),
|
||||||
|
color[i](1),
|
||||||
|
color[i](2));
|
||||||
|
for (size_t i = 0; i < its.indices.size(); ++i)
|
||||||
|
fprintf(fp, "f %d %d %d\n",
|
||||||
|
its.indices[i][0] + 1,
|
||||||
|
its.indices[i][1] + 1,
|
||||||
|
its.indices[i][2] + 1);
|
||||||
|
fclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check validity of the mesh, assert on error.
|
// Check validity of the mesh, assert on error.
|
||||||
bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
|
bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
|
||||||
|
|
|
@ -303,6 +303,17 @@ extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
|
||||||
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
|
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
|
||||||
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
|
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
|
||||||
|
|
||||||
|
|
||||||
|
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> obj_color; // Vec3f
|
||||||
|
/// <summary>
|
||||||
|
/// write idexed triangle set into obj file with color
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="its">input model</param>
|
||||||
|
/// <param name="color">color of stored model</param>
|
||||||
|
/// <param name="file">define place to store</param>
|
||||||
|
/// <returns>True on success otherwise FALSE</returns>
|
||||||
|
extern bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file);
|
||||||
|
|
||||||
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
|
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
|
||||||
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
|
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
|
||||||
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
|
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
|
||||||
|
|
|
@ -3,14 +3,17 @@ project(imgui)
|
||||||
|
|
||||||
add_library(imgui STATIC
|
add_library(imgui STATIC
|
||||||
imconfig.h
|
imconfig.h
|
||||||
|
imgui.cpp
|
||||||
imgui.h
|
imgui.h
|
||||||
|
imgui_demo.cpp
|
||||||
|
imgui_draw.cpp
|
||||||
imgui_internal.h
|
imgui_internal.h
|
||||||
|
imgui_stdlib.cpp
|
||||||
|
imgui_stdlib.h
|
||||||
|
imgui_tables.cpp
|
||||||
|
imgui_widgets.cpp
|
||||||
|
# imgui STB
|
||||||
imstb_rectpack.h
|
imstb_rectpack.h
|
||||||
imstb_textedit.h
|
imstb_textedit.h
|
||||||
imstb_truetype.h
|
imstb_truetype.h
|
||||||
imgui_tables.cpp
|
|
||||||
imgui.cpp
|
|
||||||
imgui_demo.cpp
|
|
||||||
imgui_draw.cpp
|
|
||||||
imgui_widgets.cpp
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,19 @@ For more information go to https://github.com/ocornut/imgui
|
||||||
|
|
||||||
THIS DIRECTORY CONTAINS THE imgui-1.83 ad5d1a8 SOURCE DISTRIBUTION.
|
THIS DIRECTORY CONTAINS THE imgui-1.83 ad5d1a8 SOURCE DISTRIBUTION.
|
||||||
|
|
||||||
|
|
||||||
Customized with the following commits:
|
Customized with the following commits:
|
||||||
f93d0001baa5443da2c6510d11b03c675e652418
|
f93d0001baa5443da2c6510d11b03c675e652418
|
||||||
b71d787f695c779e571865d5214d4da8d50aa7c5
|
b71d787f695c779e571865d5214d4da8d50aa7c5
|
||||||
|
|
||||||
|
imgui_stdlib.h + imgui_stdlib.cpp are move from directory /imgui/misc/cpp/
|
||||||
|
InputText() wrappers for C++ standard library (STL) type: std::string.
|
||||||
|
This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
imstb_truetype.h modification:
|
||||||
|
|
||||||
|
Hot fix for open symbolic fonts on windows
|
||||||
|
62bdfe6f8d04b88e8bd511cd613be80c0baa7f55
|
||||||
|
|
||||||
|
Hot fix for open curved fonts mainly on MAC
|
||||||
|
2148e49f75d82cb19dc6ec409fb7825296ed005c
|
||||||
|
|
76
src/imgui/imgui_stdlib.cpp
Normal file
76
src/imgui/imgui_stdlib.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Compatibility:
|
||||||
|
// - std::string support is only guaranteed to work from C++11.
|
||||||
|
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_stdlib.h"
|
||||||
|
|
||||||
|
struct InputTextCallback_UserData
|
||||||
|
{
|
||||||
|
std::string* Str;
|
||||||
|
ImGuiInputTextCallback ChainCallback;
|
||||||
|
void* ChainCallbackUserData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
|
||||||
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||||
|
{
|
||||||
|
// Resize string callback
|
||||||
|
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
|
||||||
|
std::string* str = user_data->Str;
|
||||||
|
IM_ASSERT(data->Buf == str->c_str());
|
||||||
|
str->resize(data->BufTextLen);
|
||||||
|
data->Buf = (char*)str->c_str();
|
||||||
|
}
|
||||||
|
else if (user_data->ChainCallback)
|
||||||
|
{
|
||||||
|
// Forward to user callback, if any
|
||||||
|
data->UserData = user_data->ChainCallbackUserData;
|
||||||
|
return user_data->ChainCallback(data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
22
src/imgui/imgui_stdlib.h
Normal file
22
src/imgui/imgui_stdlib.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Compatibility:
|
||||||
|
// - std::string support is only guaranteed to work from C++11.
|
||||||
|
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
// ImGui::InputText() with std::string
|
||||||
|
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
|
||||||
|
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
}
|
|
@ -1437,6 +1437,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
|
||||||
switch(ttUSHORT(data+encoding_record)) {
|
switch(ttUSHORT(data+encoding_record)) {
|
||||||
case STBTT_PLATFORM_ID_MICROSOFT:
|
case STBTT_PLATFORM_ID_MICROSOFT:
|
||||||
switch (ttUSHORT(data+encoding_record+2)) {
|
switch (ttUSHORT(data+encoding_record+2)) {
|
||||||
|
case STBTT_MS_EID_SYMBOL:
|
||||||
case STBTT_MS_EID_UNICODE_BMP:
|
case STBTT_MS_EID_UNICODE_BMP:
|
||||||
case STBTT_MS_EID_UNICODE_FULL:
|
case STBTT_MS_EID_UNICODE_FULL:
|
||||||
// MS/Unicode
|
// MS/Unicode
|
||||||
|
@ -1734,7 +1735,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
|
||||||
|
|
||||||
// now start the new one
|
// now start the new one
|
||||||
start_off = !(flags & 1);
|
start_off = !(flags & 1);
|
||||||
if (start_off) {
|
if (start_off && (i + 1) < n) {
|
||||||
// if we start off with an off-curve point, then when we need to find a point on the curve
|
// if we start off with an off-curve point, then when we need to find a point on the curve
|
||||||
// where we can start, and we need to save some state for when we wraparound.
|
// where we can start, and we need to save some state for when we wraparound.
|
||||||
scx = x;
|
scx = x;
|
||||||
|
|
|
@ -90,20 +90,21 @@ inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over
|
||||||
|
|
||||||
// Finding a closest line, its closest point and squared distance to the closest point
|
// Finding a closest line, its closest point and squared distance to the closest point
|
||||||
// Returns squared distance to the closest point or -1 if the input is empty.
|
// Returns squared distance to the closest point or -1 if the input is empty.
|
||||||
|
// or no closer point than max_sq_dist
|
||||||
template<typename LineType, typename TreeType, typename VectorType>
|
template<typename LineType, typename TreeType, typename VectorType>
|
||||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(const std::vector<LineType> &lines,
|
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
|
||||||
const TreeType &tree,
|
const std::vector<LineType> &lines,
|
||||||
const VectorType &point,
|
const TreeType &tree,
|
||||||
size_t &hit_idx_out,
|
const VectorType &point,
|
||||||
Eigen::PlainObjectBase<VectorType> &hit_point_out)
|
size_t &hit_idx_out,
|
||||||
|
Eigen::PlainObjectBase<VectorType> &hit_point_out,
|
||||||
|
typename VectorType::Scalar max_sqr_dist = std::numeric_limits<typename VectorType::Scalar>::infinity())
|
||||||
{
|
{
|
||||||
using Scalar = typename VectorType::Scalar;
|
using Scalar = typename VectorType::Scalar;
|
||||||
|
if (tree.empty()) return Scalar(-1);
|
||||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
|
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
|
||||||
return tree.empty() ?
|
return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(
|
||||||
Scalar(-1) :
|
distancer, size_t(0), Scalar(0), max_sqr_dist, hit_idx_out, hit_point_out);
|
||||||
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
|
|
||||||
std::numeric_limits<Scalar>::infinity(),
|
|
||||||
hit_idx_out, hit_point_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all lines within the given radius limit
|
// Returns all lines within the given radius limit
|
||||||
|
|
|
@ -39,6 +39,7 @@ static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-conte
|
||||||
|
|
||||||
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
|
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
|
||||||
const std::string AppConfig::SECTION_MATERIALS = "sla_materials";
|
const std::string AppConfig::SECTION_MATERIALS = "sla_materials";
|
||||||
|
const std::string AppConfig::SECTION_EMBOSS_STYLE = "font";
|
||||||
|
|
||||||
void AppConfig::reset()
|
void AppConfig::reset()
|
||||||
{
|
{
|
||||||
|
|
|
@ -167,6 +167,7 @@ public:
|
||||||
|
|
||||||
static const std::string SECTION_FILAMENTS;
|
static const std::string SECTION_FILAMENTS;
|
||||||
static const std::string SECTION_MATERIALS;
|
static const std::string SECTION_MATERIALS;
|
||||||
|
static const std::string SECTION_EMBOSS_STYLE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -53,7 +53,7 @@ public:
|
||||||
PointClass size() const;
|
PointClass size() const;
|
||||||
double radius() const;
|
double radius() const;
|
||||||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
||||||
void translate(const Vec2d &v) { this->min += v; this->max += v; }
|
void translate(const PointClass &v) { this->min += v; this->max += v; }
|
||||||
void offset(coordf_t delta);
|
void offset(coordf_t delta);
|
||||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||||
PointClass center() const;
|
PointClass center() const;
|
||||||
|
@ -174,6 +174,7 @@ public:
|
||||||
BoundingBox rotated(double angle, const Point ¢er) const;
|
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||||
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
||||||
|
bool intersects(const BoundingBox &other) const { return this->min(0) <= other.max(0) && this->max(0) >= other.min(0) && this->min(1) <= other.max(1) && this->max(1) >= other.min(1); }
|
||||||
// Align the min corner to a grid of cell_size x cell_size cells,
|
// Align the min corner to a grid of cell_size x cell_size cells,
|
||||||
// to encompass the original bounding box.
|
// to encompass the original bounding box.
|
||||||
void align_to_grid(const coord_t cell_size);
|
void align_to_grid(const coord_t cell_size);
|
||||||
|
|
|
@ -19,6 +19,7 @@ set(SLIC3R_SOURCES
|
||||||
pchheader.hpp
|
pchheader.hpp
|
||||||
AStar.hpp
|
AStar.hpp
|
||||||
AABBTreeIndirect.hpp
|
AABBTreeIndirect.hpp
|
||||||
|
AABBTreeLines.hpp
|
||||||
AABBMesh.hpp
|
AABBMesh.hpp
|
||||||
AABBMesh.cpp
|
AABBMesh.cpp
|
||||||
BoundingBox.cpp
|
BoundingBox.cpp
|
||||||
|
@ -42,9 +43,13 @@ set(SLIC3R_SOURCES
|
||||||
EdgeGrid.hpp
|
EdgeGrid.hpp
|
||||||
ElephantFootCompensation.cpp
|
ElephantFootCompensation.cpp
|
||||||
ElephantFootCompensation.hpp
|
ElephantFootCompensation.hpp
|
||||||
|
Emboss.cpp
|
||||||
|
Emboss.hpp
|
||||||
enum_bitmask.hpp
|
enum_bitmask.hpp
|
||||||
ExPolygon.cpp
|
ExPolygon.cpp
|
||||||
ExPolygon.hpp
|
ExPolygon.hpp
|
||||||
|
ExPolygonsIndex.cpp
|
||||||
|
ExPolygonsIndex.hpp
|
||||||
Extruder.cpp
|
Extruder.cpp
|
||||||
Extruder.hpp
|
Extruder.hpp
|
||||||
ExtrusionEntity.cpp
|
ExtrusionEntity.cpp
|
||||||
|
@ -178,6 +183,7 @@ set(SLIC3R_SOURCES
|
||||||
Model.hpp
|
Model.hpp
|
||||||
ModelArrange.hpp
|
ModelArrange.hpp
|
||||||
ModelArrange.cpp
|
ModelArrange.cpp
|
||||||
|
#ModelVolumeType.hpp
|
||||||
MultiMaterialSegmentation.cpp
|
MultiMaterialSegmentation.cpp
|
||||||
MultiMaterialSegmentation.hpp
|
MultiMaterialSegmentation.hpp
|
||||||
MeshNormals.hpp
|
MeshNormals.hpp
|
||||||
|
@ -194,6 +200,8 @@ set(SLIC3R_SOURCES
|
||||||
MutablePriorityQueue.hpp
|
MutablePriorityQueue.hpp
|
||||||
NormalUtils.cpp
|
NormalUtils.cpp
|
||||||
NormalUtils.hpp
|
NormalUtils.hpp
|
||||||
|
NSVGUtils.cpp
|
||||||
|
NSVGUtils.hpp
|
||||||
ObjectID.cpp
|
ObjectID.cpp
|
||||||
ObjectID.hpp
|
ObjectID.hpp
|
||||||
PerimeterGenerator.cpp
|
PerimeterGenerator.cpp
|
||||||
|
@ -266,6 +274,7 @@ set(SLIC3R_SOURCES
|
||||||
Technologies.hpp
|
Technologies.hpp
|
||||||
Tesselate.cpp
|
Tesselate.cpp
|
||||||
Tesselate.hpp
|
Tesselate.hpp
|
||||||
|
TextConfiguration.hpp
|
||||||
TreeSupport.cpp
|
TreeSupport.cpp
|
||||||
TreeSupport.hpp
|
TreeSupport.hpp
|
||||||
TreeModelVolumes.cpp
|
TreeModelVolumes.cpp
|
||||||
|
@ -280,6 +289,8 @@ set(SLIC3R_SOURCES
|
||||||
Utils.hpp
|
Utils.hpp
|
||||||
Time.cpp
|
Time.cpp
|
||||||
Time.hpp
|
Time.hpp
|
||||||
|
Timer.cpp
|
||||||
|
Timer.hpp
|
||||||
Thread.cpp
|
Thread.cpp
|
||||||
Thread.hpp
|
Thread.hpp
|
||||||
TriangleSelector.cpp
|
TriangleSelector.cpp
|
||||||
|
@ -403,8 +414,14 @@ cmake_policy(SET CMP0011 NEW)
|
||||||
find_package(CGAL REQUIRED)
|
find_package(CGAL REQUIRED)
|
||||||
cmake_policy(POP)
|
cmake_policy(POP)
|
||||||
|
|
||||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
add_library(libslic3r_cgal STATIC
|
||||||
TryCatchSignal.cpp Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiUtilsCgal.cpp)
|
CutSurface.hpp CutSurface.cpp
|
||||||
|
Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiUtilsCgal.cpp
|
||||||
|
IntersectionPoints.hpp IntersectionPoints.cpp
|
||||||
|
MeshBoolean.hpp MeshBoolean.cpp
|
||||||
|
TryCatchSignal.hpp TryCatchSignal.cpp
|
||||||
|
Triangulation.hpp Triangulation.cpp
|
||||||
|
)
|
||||||
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
||||||
|
@ -476,6 +493,7 @@ if (APPLE)
|
||||||
# This flag prevents the need for minimum SDK version 10.14
|
# This flag prevents the need for minimum SDK version 10.14
|
||||||
# currently, PS targets v10.12
|
# currently, PS targets v10.12
|
||||||
target_compile_options(libslic3r PUBLIC "-fno-aligned-allocation")
|
target_compile_options(libslic3r PUBLIC "-fno-aligned-allocation")
|
||||||
|
target_compile_options(libslic3r_cgal PUBLIC "-fno-aligned-allocation")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||||
|
|
3990
src/libslic3r/CutSurface.cpp
Normal file
3990
src/libslic3r/CutSurface.cpp
Normal file
File diff suppressed because it is too large
Load diff
74
src/libslic3r/CutSurface.hpp
Normal file
74
src/libslic3r/CutSurface.hpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef slic3r_CutSurface_hpp_
|
||||||
|
#define slic3r_CutSurface_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <admesh/stl.h> // indexed_triangle_set
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "Emboss.hpp" // IProjection
|
||||||
|
|
||||||
|
namespace Slic3r{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents cutted surface from object
|
||||||
|
/// Extend index triangle set by outlines
|
||||||
|
/// </summary>
|
||||||
|
struct SurfaceCut : public indexed_triangle_set
|
||||||
|
{
|
||||||
|
// vertex indices(index to mesh vertices)
|
||||||
|
using Index = unsigned int;
|
||||||
|
using Contour = std::vector<Index>;
|
||||||
|
using Contours = std::vector<Contour>;
|
||||||
|
// list of circulated open surface
|
||||||
|
Contours contours;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cut surface shape from models.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="shapes">Multiple shape to cut from model</param>
|
||||||
|
/// <param name="models">Multi mesh to cut, need to be in same coordinate system</param>
|
||||||
|
/// <param name="projection">Define transformation 2d shape into 3d</param>
|
||||||
|
/// <param name="projection_ratio">Define ideal ratio between front and back projection to cut
|
||||||
|
/// 0 .. means use closest to front projection
|
||||||
|
/// 1 .. means use closest to back projection
|
||||||
|
/// value from <0, 1>
|
||||||
|
/// </param>
|
||||||
|
/// <returns>Cutted surface from model</returns>
|
||||||
|
SurfaceCut cut_surface(const ExPolygons &shapes,
|
||||||
|
const std::vector<indexed_triangle_set> &models,
|
||||||
|
const Emboss::IProjection &projection,
|
||||||
|
float projection_ratio);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create model from surface cuts by projection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cut">Surface from model with outlines</param>
|
||||||
|
/// <param name="projection">Way of emboss</param>
|
||||||
|
/// <returns>Mesh</returns>
|
||||||
|
indexed_triangle_set cut2model(const SurfaceCut &cut,
|
||||||
|
const Emboss::IProject3d &projection);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Separate (A)rea (o)f (I)nterest .. AoI from model
|
||||||
|
/// NOTE: Only 2d filtration, do not filtrate by Z coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="its">Input model</param>
|
||||||
|
/// <param name="bb">Bounding box to project into space</param>
|
||||||
|
/// <param name="projection">Define tranformation of BB into space</param>
|
||||||
|
/// <returns>Triangles lay at least partialy inside of projected Bounding box</returns>
|
||||||
|
indexed_triangle_set its_cut_AoI(const indexed_triangle_set &its,
|
||||||
|
const BoundingBox &bb,
|
||||||
|
const Emboss::IProjection &projection);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Separate triangles by mask
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="its">Input model</param>
|
||||||
|
/// <param name="mask">Mask - same size as its::indices</param>
|
||||||
|
/// <returns>Copy of indices by mask(with their vertices)</returns>
|
||||||
|
indexed_triangle_set its_mask(const indexed_triangle_set &its, const std::vector<bool> &mask);
|
||||||
|
|
||||||
|
bool corefine_test(const std::string &model_path, const std::string &shape_path);
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_CutSurface_hpp_
|
1294
src/libslic3r/Emboss.cpp
Normal file
1294
src/libslic3r/Emboss.cpp
Normal file
File diff suppressed because it is too large
Load diff
349
src/libslic3r/Emboss.hpp
Normal file
349
src/libslic3r/Emboss.hpp
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
#ifndef slic3r_Emboss_hpp_
|
||||||
|
#define slic3r_Emboss_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
#include <admesh/stl.h> // indexed_triangle_set
|
||||||
|
#include "Polygon.hpp"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "TextConfiguration.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// class with only static function add ability to engraved OR raised
|
||||||
|
/// text OR polygons onto model surface
|
||||||
|
/// </summary>
|
||||||
|
namespace Emboss
|
||||||
|
{
|
||||||
|
// every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value
|
||||||
|
// stored in fonts (to be able represents curve by sequence of lines)
|
||||||
|
static constexpr double SHAPE_SCALE = 0.001; // SCALING_FACTOR promile is fine enough
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collect fonts registred inside OS
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>OS registred TTF font files(full path) with names</returns>
|
||||||
|
EmbossStyles get_font_list();
|
||||||
|
#ifdef _WIN32
|
||||||
|
EmbossStyles get_font_list_by_register();
|
||||||
|
EmbossStyles get_font_list_by_enumeration();
|
||||||
|
EmbossStyles get_font_list_by_folder();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OS dependent function to get location of font by its name descriptor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font_face_name">Unique identificator for font</param>
|
||||||
|
/// <returns>File path to font when found</returns>
|
||||||
|
std::optional<std::wstring> get_font_path(const std::wstring &font_face_name);
|
||||||
|
|
||||||
|
// description of one letter
|
||||||
|
struct Glyph
|
||||||
|
{
|
||||||
|
// NOTE: shape is scaled by SHAPE_SCALE
|
||||||
|
// to be able store points without floating points
|
||||||
|
ExPolygons shape;
|
||||||
|
|
||||||
|
// values are in font points
|
||||||
|
int advance_width=0, left_side_bearing=0;
|
||||||
|
};
|
||||||
|
// cache for glyph by unicode
|
||||||
|
using Glyphs = std::map<int, Glyph>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// keep information from file about font
|
||||||
|
/// (store file data itself)
|
||||||
|
/// + cache data readed from buffer
|
||||||
|
/// </summary>
|
||||||
|
struct FontFile
|
||||||
|
{
|
||||||
|
// loaded data from font file
|
||||||
|
// must store data size for imgui rasterization
|
||||||
|
// To not store data on heap and To prevent unneccesary copy
|
||||||
|
// data are stored inside unique_ptr
|
||||||
|
std::unique_ptr<std::vector<unsigned char>> data;
|
||||||
|
|
||||||
|
struct Info
|
||||||
|
{
|
||||||
|
// vertical position is "scale*(ascent - descent + lineGap)"
|
||||||
|
int ascent, descent, linegap;
|
||||||
|
|
||||||
|
// for convert font units to pixel
|
||||||
|
int unit_per_em;
|
||||||
|
};
|
||||||
|
// info for each font in data
|
||||||
|
std::vector<Info> infos;
|
||||||
|
|
||||||
|
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
|
||||||
|
std::vector<Info> &&infos)
|
||||||
|
: data(std::move(data)), infos(std::move(infos))
|
||||||
|
{
|
||||||
|
assert(this->data != nullptr);
|
||||||
|
assert(!this->data->empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FontFile &other) const {
|
||||||
|
if (data->size() != other.data->size())
|
||||||
|
return false;
|
||||||
|
//if(*data != *other.data) return false;
|
||||||
|
for (size_t i = 0; i < infos.size(); i++)
|
||||||
|
if (infos[i].ascent != other.infos[i].ascent ||
|
||||||
|
infos[i].descent == other.infos[i].descent ||
|
||||||
|
infos[i].linegap == other.infos[i].linegap)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add caching for shape of glyphs
|
||||||
|
/// </summary>
|
||||||
|
struct FontFileWithCache
|
||||||
|
{
|
||||||
|
// Pointer on data of the font file
|
||||||
|
std::shared_ptr<const FontFile> font_file;
|
||||||
|
|
||||||
|
// Cache for glyph shape
|
||||||
|
// IMPORTANT: accessible only in plater job thread !!!
|
||||||
|
// main thread only clear cache by set to another shared_ptr
|
||||||
|
std::shared_ptr<Emboss::Glyphs> cache;
|
||||||
|
|
||||||
|
FontFileWithCache() : font_file(nullptr), cache(nullptr) {}
|
||||||
|
FontFileWithCache(std::unique_ptr<FontFile> font_file)
|
||||||
|
: font_file(std::move(font_file))
|
||||||
|
, cache(std::make_shared<Emboss::Glyphs>())
|
||||||
|
{}
|
||||||
|
bool has_value() const { return font_file != nullptr && cache != nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load font file into buffer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file_path">Location of .ttf or .ttc font file</param>
|
||||||
|
/// <returns>Font object when loaded.</returns>
|
||||||
|
std::unique_ptr<FontFile> create_font_file(const char *file_path);
|
||||||
|
// data = raw file data
|
||||||
|
std::unique_ptr<FontFile> create_font_file(std::unique_ptr<std::vector<unsigned char>> data);
|
||||||
|
#ifdef _WIN32
|
||||||
|
// fix for unknown pointer HFONT
|
||||||
|
using HFONT = void*;
|
||||||
|
void * can_load(HFONT hfont);
|
||||||
|
std::unique_ptr<FontFile> create_font_file(HFONT hfont);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// convert letter into polygons
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">Define fonts</param>
|
||||||
|
/// <param name="font_index">Index of font in collection</param>
|
||||||
|
/// <param name="letter">One character defined by unicode codepoint</param>
|
||||||
|
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
||||||
|
/// <returns>inner polygon cw(outer ccw)</returns>
|
||||||
|
std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert text into polygons
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">Define fonts + cache, which could extend</param>
|
||||||
|
/// <param name="text">Characters to convert</param>
|
||||||
|
/// <param name="font_prop">User defined property of the font</param>
|
||||||
|
/// <param name="was_canceled">Way to interupt processing</param>
|
||||||
|
/// <returns>Inner polygon cw(outer ccw)</returns>
|
||||||
|
ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop, std::function<bool()> was_canceled = nullptr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fix intersections and self intersections in polygons glyph shape
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="shape">Input shape to heal</param>
|
||||||
|
/// <returns>Healed shapes</returns>
|
||||||
|
ExPolygons heal_shape(const Polygons &shape);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NOTE: call Slic3r::union_ex before this call
|
||||||
|
///
|
||||||
|
/// Heal (read: Fix) issues in expolygons:
|
||||||
|
/// - self intersections
|
||||||
|
/// - duplicit points
|
||||||
|
/// - points close to line segments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="shape">In/Out shape to heal</param>
|
||||||
|
/// <param name="max_iteration">Heal could create another issue,
|
||||||
|
/// After healing it is checked again until shape is good or maximal count of iteration</param>
|
||||||
|
/// <returns>True when shapes is good otherwise False</returns>
|
||||||
|
bool heal_shape(ExPolygons &shape, unsigned max_iteration = 10);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divide line segments in place near to point
|
||||||
|
/// (which could lead to self intersection due to preccision)
|
||||||
|
/// Remove same neighbors
|
||||||
|
/// Note: Possible part of heal shape
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expolygons">Expolygon to edit</param>
|
||||||
|
/// <param name="distance">(epsilon)Euclidean distance from point to line which divide line</param>
|
||||||
|
/// <returns>True when some division was made otherwise false</returns>
|
||||||
|
bool divide_segments_for_close_point(ExPolygons &expolygons, double distance);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use data from font property to modify transformation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
|
||||||
|
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
||||||
|
/// <param name="transformation">In / Out transformation to modify by property</param>
|
||||||
|
void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read information from naming table of font file
|
||||||
|
/// search for italic (or oblique), bold italic (or bold oblique)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">Selector of font</param>
|
||||||
|
/// <param name="font_index">Index of font in collection</param>
|
||||||
|
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
|
||||||
|
bool is_italic(const FontFile &font, unsigned int font_index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create unique character set from string with filtered from text with only character from font
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Source vector of glyphs</param>
|
||||||
|
/// <param name="font">Font descriptor</param>
|
||||||
|
/// <param name="font_index">Define font in collection</param>
|
||||||
|
/// <param name="exist_unknown">True when text contain glyph unknown in font</param>
|
||||||
|
/// <returns>Unique set of character from text contained in font</returns>
|
||||||
|
std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate scale for glyph shape convert from shape points to mm
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fp">Property of font</param>
|
||||||
|
/// <param name="ff">Font data</param>
|
||||||
|
/// <returns>Conversion to mm</returns>
|
||||||
|
double get_shape_scale(const FontProp &fp, const FontFile &ff);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Project spatial point
|
||||||
|
/// </summary>
|
||||||
|
class IProject3d
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IProject3d() = default;
|
||||||
|
/// <summary>
|
||||||
|
/// Move point with respect to projection direction
|
||||||
|
/// e.g. Orthogonal projection will move with point by direction
|
||||||
|
/// e.g. Spherical projection need to use center of projection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">Spatial point coordinate</param>
|
||||||
|
/// <returns>Projected spatial point</returns>
|
||||||
|
virtual Vec3d project(const Vec3d &point) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Project 2d point into space
|
||||||
|
/// Could be plane, sphere, cylindric, ...
|
||||||
|
/// </summary>
|
||||||
|
class IProjection : public IProject3d
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IProjection() = default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// convert 2d point to 3d points
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p">2d coordinate</param>
|
||||||
|
/// <returns>
|
||||||
|
/// first - front spatial point
|
||||||
|
/// second - back spatial point
|
||||||
|
/// </returns>
|
||||||
|
virtual std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Back projection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p">Point to project</param>
|
||||||
|
/// <param name="depth">[optional] Depth of 2d projected point. Be careful number is in 2d scale</param>
|
||||||
|
/// <returns>Uprojected point when it is possible</returns>
|
||||||
|
virtual std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create triangle model for text
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="shape2d">text or image</param>
|
||||||
|
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
|
||||||
|
/// <returns>Projected shape into space</returns>
|
||||||
|
indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create transformation for emboss text object to lay on surface point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">Position of surface point</param>
|
||||||
|
/// <param name="normal">Normal of surface point</param>
|
||||||
|
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
|
||||||
|
/// <returns>Transformation onto surface point</returns>
|
||||||
|
Transform3d create_transformation_onto_surface(
|
||||||
|
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
|
||||||
|
|
||||||
|
class ProjectZ : public IProjection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProjectZ(double depth) : m_depth(depth) {}
|
||||||
|
// Inherited via IProject
|
||||||
|
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override;
|
||||||
|
Vec3d project(const Vec3d &point) const override;
|
||||||
|
std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const override;
|
||||||
|
double m_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProjectScale : public IProjection
|
||||||
|
{
|
||||||
|
std::unique_ptr<IProjection> core;
|
||||||
|
double m_scale;
|
||||||
|
public:
|
||||||
|
ProjectScale(std::unique_ptr<IProjection> core, double scale)
|
||||||
|
: core(std::move(core)), m_scale(scale)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Inherited via IProject
|
||||||
|
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override
|
||||||
|
{
|
||||||
|
auto res = core->create_front_back(p);
|
||||||
|
return std::make_pair(res.first * m_scale, res.second * m_scale);
|
||||||
|
}
|
||||||
|
Vec3d project(const Vec3d &point) const override{
|
||||||
|
return core->project(point);
|
||||||
|
}
|
||||||
|
std::optional<Vec2d> unproject(const Vec3d &p, double *depth = nullptr) const override {
|
||||||
|
auto res = core->unproject(p / m_scale, depth);
|
||||||
|
if (depth != nullptr) *depth *= m_scale;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrthoProject3d : public Emboss::IProject3d
|
||||||
|
{
|
||||||
|
// size and direction of emboss for ortho projection
|
||||||
|
Vec3d m_direction;
|
||||||
|
public:
|
||||||
|
OrthoProject3d(Vec3d direction) : m_direction(direction) {}
|
||||||
|
Vec3d project(const Vec3d &point) const override{ return point + m_direction;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrthoProject: public Emboss::IProjection {
|
||||||
|
Transform3d m_matrix;
|
||||||
|
// size and direction of emboss for ortho projection
|
||||||
|
Vec3d m_direction;
|
||||||
|
Transform3d m_matrix_inv;
|
||||||
|
public:
|
||||||
|
OrthoProject(Transform3d matrix, Vec3d direction)
|
||||||
|
: m_matrix(matrix), m_direction(direction), m_matrix_inv(matrix.inverse())
|
||||||
|
{}
|
||||||
|
// Inherited via IProject
|
||||||
|
std::pair<Vec3d, Vec3d> create_front_back(const Point &p) const override;
|
||||||
|
Vec3d project(const Vec3d &point) const override;
|
||||||
|
std::optional<Vec2d> unproject(const Vec3d &p, double * depth = nullptr) const override;
|
||||||
|
};
|
||||||
|
} // namespace Emboss
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_Emboss_hpp_
|
|
@ -85,6 +85,25 @@ public:
|
||||||
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
||||||
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
|
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
|
||||||
|
|
||||||
|
inline size_t count_points(const ExPolygons &expolys)
|
||||||
|
{
|
||||||
|
size_t n_points = 0;
|
||||||
|
for (const auto &expoly : expolys) {
|
||||||
|
n_points += expoly.contour.points.size();
|
||||||
|
for (const auto &hole : expoly.holes)
|
||||||
|
n_points += hole.points.size();
|
||||||
|
}
|
||||||
|
return n_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t count_points(const ExPolygon &expoly)
|
||||||
|
{
|
||||||
|
size_t n_points = expoly.contour.points.size();
|
||||||
|
for (const auto &hole : expoly.holes)
|
||||||
|
n_points += hole.points.size();
|
||||||
|
return n_points;
|
||||||
|
}
|
||||||
|
|
||||||
// Count a nuber of polygons stored inside the vector of expolygons.
|
// Count a nuber of polygons stored inside the vector of expolygons.
|
||||||
// Useful for allocating space for polygons when converting expolygons to polygons.
|
// Useful for allocating space for polygons when converting expolygons to polygons.
|
||||||
inline size_t number_polygons(const ExPolygons &expolys)
|
inline size_t number_polygons(const ExPolygons &expolys)
|
||||||
|
@ -97,11 +116,8 @@ inline size_t number_polygons(const ExPolygons &expolys)
|
||||||
|
|
||||||
inline Lines to_lines(const ExPolygon &src)
|
inline Lines to_lines(const ExPolygon &src)
|
||||||
{
|
{
|
||||||
size_t n_lines = src.contour.points.size();
|
|
||||||
for (size_t i = 0; i < src.holes.size(); ++ i)
|
|
||||||
n_lines += src.holes[i].points.size();
|
|
||||||
Lines lines;
|
Lines lines;
|
||||||
lines.reserve(n_lines);
|
lines.reserve(count_points(src));
|
||||||
for (size_t i = 0; i <= src.holes.size(); ++ i) {
|
for (size_t i = 0; i <= src.holes.size(); ++ i) {
|
||||||
const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1];
|
const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1];
|
||||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
||||||
|
@ -113,14 +129,8 @@ inline Lines to_lines(const ExPolygon &src)
|
||||||
|
|
||||||
inline Lines to_lines(const ExPolygons &src)
|
inline Lines to_lines(const ExPolygons &src)
|
||||||
{
|
{
|
||||||
size_t n_lines = 0;
|
|
||||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
|
||||||
n_lines += it_expoly->contour.points.size();
|
|
||||||
for (size_t i = 0; i < it_expoly->holes.size(); ++ i)
|
|
||||||
n_lines += it_expoly->holes[i].points.size();
|
|
||||||
}
|
|
||||||
Lines lines;
|
Lines lines;
|
||||||
lines.reserve(n_lines);
|
lines.reserve(count_points(src));
|
||||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||||
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
||||||
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
||||||
|
@ -132,16 +142,40 @@ inline Lines to_lines(const ExPolygons &src)
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<Linef> to_unscaled_linesf(const ExPolygons &src)
|
// Line is from point index(see to_points) to next point.
|
||||||
|
// Next point of last point in polygon is first polygon point.
|
||||||
|
inline Linesf to_linesf(const ExPolygons &src, uint32_t count_lines = 0)
|
||||||
{
|
{
|
||||||
size_t n_lines = 0;
|
assert(count_lines == 0 || count_lines == count_points(src));
|
||||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
if (count_lines == 0) count_lines = count_points(src);
|
||||||
n_lines += it_expoly->contour.points.size();
|
Linesf lines;
|
||||||
for (size_t i = 0; i < it_expoly->holes.size(); ++ i)
|
lines.reserve(count_lines);
|
||||||
n_lines += it_expoly->holes[i].points.size();
|
Vec2d prev_pd;
|
||||||
|
auto to_lines = [&lines, &prev_pd](const Points &pts) {
|
||||||
|
assert(pts.size() >= 3);
|
||||||
|
if (pts.size() < 2) return;
|
||||||
|
bool is_first = true;
|
||||||
|
for (const Point &p : pts) {
|
||||||
|
Vec2d pd = p.cast<double>();
|
||||||
|
if (is_first) is_first = false;
|
||||||
|
else lines.emplace_back(prev_pd, pd);
|
||||||
|
prev_pd = pd;
|
||||||
|
}
|
||||||
|
lines.emplace_back(prev_pd, pts.front().cast<double>());
|
||||||
|
};
|
||||||
|
for (const ExPolygon& expoly: src) {
|
||||||
|
to_lines(expoly.contour.points);
|
||||||
|
for (const Polygon &hole : expoly.holes)
|
||||||
|
to_lines(hole.points);
|
||||||
}
|
}
|
||||||
std::vector<Linef> lines;
|
assert(lines.size() == count_lines);
|
||||||
lines.reserve(n_lines);
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Linesf to_unscaled_linesf(const ExPolygons &src)
|
||||||
|
{
|
||||||
|
Linesf lines;
|
||||||
|
lines.reserve(count_points(src));
|
||||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
|
||||||
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
|
||||||
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
||||||
|
@ -159,6 +193,19 @@ inline std::vector<Linef> to_unscaled_linesf(const ExPolygons &src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline Points to_points(const ExPolygons &src)
|
||||||
|
{
|
||||||
|
Points points;
|
||||||
|
size_t count = count_points(src);
|
||||||
|
points.reserve(count);
|
||||||
|
for (const ExPolygon &expolygon : src) {
|
||||||
|
append(points, expolygon.contour.points);
|
||||||
|
for (const Polygon &hole : expolygon.holes)
|
||||||
|
append(points, hole.points);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
inline Polylines to_polylines(const ExPolygon &src)
|
inline Polylines to_polylines(const ExPolygon &src)
|
||||||
{
|
{
|
||||||
Polylines polylines;
|
Polylines polylines;
|
||||||
|
@ -278,8 +325,9 @@ inline Polygons to_polygons(ExPolygon &&src)
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
polygons.reserve(src.holes.size() + 1);
|
polygons.reserve(src.holes.size() + 1);
|
||||||
polygons.push_back(std::move(src.contour));
|
polygons.push_back(std::move(src.contour));
|
||||||
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(polygons));
|
polygons.insert(polygons.end(),
|
||||||
src.holes.clear();
|
std::make_move_iterator(src.holes.begin()),
|
||||||
|
std::make_move_iterator(src.holes.end()));
|
||||||
return polygons;
|
return polygons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,10 +335,11 @@ inline Polygons to_polygons(ExPolygons &&src)
|
||||||
{
|
{
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
polygons.reserve(number_polygons(src));
|
polygons.reserve(number_polygons(src));
|
||||||
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++it) {
|
for (ExPolygon& expoly: src) {
|
||||||
polygons.push_back(std::move(it->contour));
|
polygons.push_back(std::move(expoly.contour));
|
||||||
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(polygons));
|
polygons.insert(polygons.end(),
|
||||||
it->holes.clear();
|
std::make_move_iterator(expoly.holes.begin()),
|
||||||
|
std::make_move_iterator(expoly.holes.end()));
|
||||||
}
|
}
|
||||||
return polygons;
|
return polygons;
|
||||||
}
|
}
|
||||||
|
@ -315,11 +364,8 @@ inline ExPolygons to_expolygons(Polygons &&polys)
|
||||||
|
|
||||||
inline Points to_points(const ExPolygon &expoly)
|
inline Points to_points(const ExPolygon &expoly)
|
||||||
{
|
{
|
||||||
size_t cnt = expoly.contour.size();
|
|
||||||
for (const Polygon &hole : expoly.holes)
|
|
||||||
cnt += hole.size();
|
|
||||||
Points out;
|
Points out;
|
||||||
out.reserve(cnt);
|
out.reserve(count_points(expoly));
|
||||||
append(out, expoly.contour.points);
|
append(out, expoly.contour.points);
|
||||||
for (const Polygon &hole : expoly.holes)
|
for (const Polygon &hole : expoly.holes)
|
||||||
append(out, hole.points);
|
append(out, hole.points);
|
||||||
|
@ -345,18 +391,20 @@ inline void polygons_append(Polygons &dst, const ExPolygons &src)
|
||||||
inline void polygons_append(Polygons &dst, ExPolygon &&src)
|
inline void polygons_append(Polygons &dst, ExPolygon &&src)
|
||||||
{
|
{
|
||||||
dst.reserve(dst.size() + src.holes.size() + 1);
|
dst.reserve(dst.size() + src.holes.size() + 1);
|
||||||
dst.push_back(std::move(src.contour));
|
dst.push_back(std::move(src.contour));
|
||||||
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(dst));
|
dst.insert(dst.end(),
|
||||||
src.holes.clear();
|
std::make_move_iterator(src.holes.begin()),
|
||||||
|
std::make_move_iterator(src.holes.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void polygons_append(Polygons &dst, ExPolygons &&src)
|
inline void polygons_append(Polygons &dst, ExPolygons &&src)
|
||||||
{
|
{
|
||||||
dst.reserve(dst.size() + number_polygons(src));
|
dst.reserve(dst.size() + number_polygons(src));
|
||||||
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++ it) {
|
for (ExPolygon& expoly: src) {
|
||||||
dst.push_back(std::move(it->contour));
|
dst.push_back(std::move(expoly.contour));
|
||||||
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(dst));
|
dst.insert(dst.end(),
|
||||||
it->holes.clear();
|
std::make_move_iterator(expoly.holes.begin()),
|
||||||
|
std::make_move_iterator(expoly.holes.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,8 +418,9 @@ inline void expolygons_append(ExPolygons &dst, ExPolygons &&src)
|
||||||
if (dst.empty()) {
|
if (dst.empty()) {
|
||||||
dst = std::move(src);
|
dst = std::move(src);
|
||||||
} else {
|
} else {
|
||||||
std::move(std::begin(src), std::end(src), std::back_inserter(dst));
|
dst.insert(dst.end(),
|
||||||
src.clear();
|
std::make_move_iterator(src.begin()),
|
||||||
|
std::make_move_iterator(src.end()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
82
src/libslic3r/ExPolygonsIndex.cpp
Normal file
82
src/libslic3r/ExPolygonsIndex.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#include "ExPolygonsIndex.hpp"
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
// IMPROVE: use one dimensional vector for polygons offset with searching by std::lower_bound
|
||||||
|
ExPolygonsIndices::ExPolygonsIndices(const ExPolygons &shapes)
|
||||||
|
{
|
||||||
|
// prepare offsets
|
||||||
|
m_offsets.reserve(shapes.size());
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (const ExPolygon &shape : shapes) {
|
||||||
|
assert(!shape.contour.points.empty());
|
||||||
|
std::vector<uint32_t> shape_offsets;
|
||||||
|
shape_offsets.reserve(shape.holes.size() + 1);
|
||||||
|
shape_offsets.push_back(offset);
|
||||||
|
offset += shape.contour.points.size();
|
||||||
|
for (const Polygon &hole: shape.holes) {
|
||||||
|
shape_offsets.push_back(offset);
|
||||||
|
offset += hole.points.size();
|
||||||
|
}
|
||||||
|
m_offsets.push_back(std::move(shape_offsets));
|
||||||
|
}
|
||||||
|
m_count = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ExPolygonsIndices::cvt(const ExPolygonsIndex &id) const
|
||||||
|
{
|
||||||
|
assert(id.expolygons_index < m_offsets.size());
|
||||||
|
const std::vector<uint32_t> &shape_offset = m_offsets[id.expolygons_index];
|
||||||
|
assert(id.polygon_index < shape_offset.size());
|
||||||
|
uint32_t res = shape_offset[id.polygon_index] + id.point_index;
|
||||||
|
assert(res < m_count);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExPolygonsIndex ExPolygonsIndices::cvt(uint32_t index) const
|
||||||
|
{
|
||||||
|
assert(index < m_count);
|
||||||
|
ExPolygonsIndex result{0, 0, 0};
|
||||||
|
// find expolygon index
|
||||||
|
auto fn = [](const std::vector<uint32_t> &offsets, uint32_t index) { return offsets[0] < index; };
|
||||||
|
auto it = std::lower_bound(m_offsets.begin() + 1, m_offsets.end(), index, fn);
|
||||||
|
result.expolygons_index = it - m_offsets.begin();
|
||||||
|
if (it == m_offsets.end() || it->at(0) != index) --result.expolygons_index;
|
||||||
|
|
||||||
|
// find polygon index
|
||||||
|
const std::vector<uint32_t> &shape_offset = m_offsets[result.expolygons_index];
|
||||||
|
auto it2 = std::lower_bound(shape_offset.begin() + 1, shape_offset.end(), index);
|
||||||
|
result.polygon_index = it2 - shape_offset.begin();
|
||||||
|
if (it2 == shape_offset.end() || *it2 != index) --result.polygon_index;
|
||||||
|
|
||||||
|
// calculate point index
|
||||||
|
uint32_t polygon_offset = shape_offset[result.polygon_index];
|
||||||
|
assert(index >= polygon_offset);
|
||||||
|
result.point_index = index - polygon_offset;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExPolygonsIndices::is_last_point(const ExPolygonsIndex &id) const {
|
||||||
|
assert(id.expolygons_index < m_offsets.size());
|
||||||
|
const std::vector<uint32_t> &shape_offset = m_offsets[id.expolygons_index];
|
||||||
|
assert(id.polygon_index < shape_offset.size());
|
||||||
|
uint32_t index = shape_offset[id.polygon_index] + id.point_index;
|
||||||
|
assert(index < m_count);
|
||||||
|
// next index
|
||||||
|
uint32_t next_point_index = index + 1;
|
||||||
|
uint32_t next_poly_index = id.polygon_index + 1;
|
||||||
|
uint32_t next_expoly_index = id.expolygons_index + 1;
|
||||||
|
// is last expoly?
|
||||||
|
if (next_expoly_index == m_offsets.size()) {
|
||||||
|
// is last expoly last poly?
|
||||||
|
if (next_poly_index == shape_offset.size())
|
||||||
|
return next_point_index == m_count;
|
||||||
|
} else {
|
||||||
|
// (not last expoly) is expoly last poly?
|
||||||
|
if (next_poly_index == shape_offset.size())
|
||||||
|
return next_point_index == m_offsets[next_expoly_index][0];
|
||||||
|
}
|
||||||
|
// Not last polygon in expolygon
|
||||||
|
return next_point_index == shape_offset[next_poly_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ExPolygonsIndices::get_count() const { return m_count; }
|
74
src/libslic3r/ExPolygonsIndex.hpp
Normal file
74
src/libslic3r/ExPolygonsIndex.hpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef slic3r_ExPolygonsIndex_hpp_
|
||||||
|
#define slic3r_ExPolygonsIndex_hpp_
|
||||||
|
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Index into ExPolygons
|
||||||
|
/// Identify expolygon, its contour (or hole) and point
|
||||||
|
/// </summary>
|
||||||
|
struct ExPolygonsIndex
|
||||||
|
{
|
||||||
|
// index of ExPolygons
|
||||||
|
uint32_t expolygons_index;
|
||||||
|
|
||||||
|
// index of Polygon
|
||||||
|
// 0 .. contour
|
||||||
|
// N .. hole[N-1]
|
||||||
|
uint32_t polygon_index;
|
||||||
|
|
||||||
|
// index of point in polygon
|
||||||
|
uint32_t point_index;
|
||||||
|
|
||||||
|
bool is_contour() const { return polygon_index == 0; }
|
||||||
|
bool is_hole() const { return polygon_index != 0; }
|
||||||
|
uint32_t hole_index() const { return polygon_index - 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keep conversion from ExPolygonsIndex to Index and vice versa
|
||||||
|
/// ExPolygonsIndex .. contour(or hole) point from ExPolygons
|
||||||
|
/// Index .. continous number
|
||||||
|
///
|
||||||
|
/// index is used to address lines and points as result from function
|
||||||
|
/// Slic3r::to_lines, Slic3r::to_points
|
||||||
|
/// </summary>
|
||||||
|
class ExPolygonsIndices
|
||||||
|
{
|
||||||
|
std::vector<std::vector<uint32_t>> m_offsets;
|
||||||
|
// for check range of index
|
||||||
|
uint32_t m_count; // count of points
|
||||||
|
public:
|
||||||
|
ExPolygonsIndices(const ExPolygons &shapes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert to one index number
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Compose of adress into expolygons</param>
|
||||||
|
/// <returns>Index</returns>
|
||||||
|
uint32_t cvt(const ExPolygonsIndex &id) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Separate to multi index
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">adress into expolygons</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
ExPolygonsIndex cvt(uint32_t index) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether id is last point in polygon
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Identify point in expolygon</param>
|
||||||
|
/// <returns>True when id is last point in polygon otherwise false</returns>
|
||||||
|
bool is_last_point(const ExPolygonsIndex &id) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Count of points in expolygons
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Count of points in expolygons</returns>
|
||||||
|
uint32_t get_count() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_ExPolygonsIndex_hpp_
|
|
@ -76,7 +76,11 @@ public:
|
||||||
if (entities.empty())
|
if (entities.empty())
|
||||||
entities = std::move(src);
|
entities = std::move(src);
|
||||||
else {
|
else {
|
||||||
std::move(std::begin(src), std::end(src), std::back_inserter(entities));
|
entities.insert(entities.end(),
|
||||||
|
std::make_move_iterator(src.begin()),
|
||||||
|
std::make_move_iterator(src.end()));
|
||||||
|
// Removing pointers to polymorphic extrusions from the donor object
|
||||||
|
// so that they will not be deleted twice.
|
||||||
src.clear();
|
src.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
170
src/libslic3r/IntersectionPoints.cpp
Normal file
170
src/libslic3r/IntersectionPoints.cpp
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
#include "IntersectionPoints.hpp"
|
||||||
|
|
||||||
|
//#define USE_CGAL_SWEEP_LINE
|
||||||
|
#ifdef USE_CGAL_SWEEP_LINE
|
||||||
|
|
||||||
|
#include <CGAL/Cartesian.h>
|
||||||
|
#include <CGAL/MP_Float.h>
|
||||||
|
#include <CGAL/Quotient.h>
|
||||||
|
#include <CGAL/Arr_segment_traits_2.h>
|
||||||
|
#include <CGAL/Sweep_line_2_algorithms.h>
|
||||||
|
|
||||||
|
using NT = CGAL::Quotient<CGAL::MP_Float>;
|
||||||
|
using Kernel = CGAL::Cartesian<NT>;
|
||||||
|
using P2 = Kernel::Point_2;
|
||||||
|
using Traits_2 = CGAL::Arr_segment_traits_2<Kernel>;
|
||||||
|
using Segment = Traits_2::Curve_2;
|
||||||
|
using Segments = std::vector<Segment>;
|
||||||
|
|
||||||
|
namespace priv {
|
||||||
|
|
||||||
|
P2 convert(const Slic3r::Point &p) { return P2(p.x(), p.y()); }
|
||||||
|
Slic3r::Vec2d convert(const P2 &p)
|
||||||
|
{
|
||||||
|
return Slic3r::Vec2d(CGAL::to_double(p.x()), CGAL::to_double(p.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs compute_intersections(const Segments &segments)
|
||||||
|
{
|
||||||
|
std::vector<P2> intersections;
|
||||||
|
// Compute all intersection points.
|
||||||
|
CGAL::compute_intersection_points(segments.begin(), segments.end(),
|
||||||
|
std::back_inserter(intersections));
|
||||||
|
if (intersections.empty()) return {};
|
||||||
|
Slic3r::Pointfs pts;
|
||||||
|
pts.reserve(intersections.size());
|
||||||
|
for (const P2 &p : intersections) pts.push_back(convert(p));
|
||||||
|
return pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_polygon(const Slic3r::Polygon &polygon, Segments &segments)
|
||||||
|
{
|
||||||
|
if (polygon.points.size() < 2) return;
|
||||||
|
P2 prev_point = priv::convert(polygon.last_point());
|
||||||
|
for (const Slic3r::Point &p : polygon.points) {
|
||||||
|
P2 act_point = priv::convert(p);
|
||||||
|
if (prev_point == act_point) continue;
|
||||||
|
segments.emplace_back(prev_point, act_point);
|
||||||
|
prev_point = act_point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Lines &lines)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections2(lines);
|
||||||
|
Segments segments;
|
||||||
|
segments.reserve(lines.size());
|
||||||
|
for (Line l : lines)
|
||||||
|
segments.emplace_back(priv::convert(l.a), priv::convert(l.b));
|
||||||
|
return priv::compute_intersections(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Polygon &polygon)
|
||||||
|
{
|
||||||
|
Segments segments;
|
||||||
|
segments.reserve(polygon.points.size());
|
||||||
|
priv::add_polygon(polygon, segments);
|
||||||
|
return priv::compute_intersections(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Polygons &polygons)
|
||||||
|
{
|
||||||
|
Segments segments;
|
||||||
|
segments.reserve(count_points(polygons));
|
||||||
|
for (const Polygon &polygon : polygons)
|
||||||
|
priv::add_polygon(polygon, segments);
|
||||||
|
return priv::compute_intersections(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygon &expolygon)
|
||||||
|
{
|
||||||
|
Segments segments;
|
||||||
|
segments.reserve(count_points(expolygon));
|
||||||
|
priv::add_polygon(expolygon.contour, segments);
|
||||||
|
for (const Polygon &hole : expolygon.holes)
|
||||||
|
priv::add_polygon(hole, segments);
|
||||||
|
return priv::compute_intersections(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
|
||||||
|
{
|
||||||
|
Segments segments;
|
||||||
|
segments.reserve(count_points(expolygons));
|
||||||
|
for (const ExPolygon &expolygon : expolygons) {
|
||||||
|
priv::add_polygon(expolygon.contour, segments);
|
||||||
|
for (const Polygon &hole : expolygon.holes)
|
||||||
|
priv::add_polygon(hole, segments);
|
||||||
|
}
|
||||||
|
return priv::compute_intersections(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace priv
|
||||||
|
|
||||||
|
#else // USE_CGAL_SWEEP_LINE
|
||||||
|
|
||||||
|
// use bounding boxes
|
||||||
|
#include <libslic3r/BoundingBox.hpp>
|
||||||
|
|
||||||
|
namespace priv {
|
||||||
|
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
|
||||||
|
{
|
||||||
|
using namespace Slic3r;
|
||||||
|
// IMPROVE0: BoundingBoxes of Polygons
|
||||||
|
// IMPROVE1: Polygon's neighbor lines can't intersect
|
||||||
|
// e.g. use indices to Point to find same points
|
||||||
|
// IMPROVE2: Use BentleyOttmann algorithm
|
||||||
|
// https://doc.cgal.org/latest/Surface_sweep_2/index.html -- CGAL implementation is significantly slower
|
||||||
|
// https://stackoverflow.com/questions/4407493/is-there-a-robust-c-implementation-of-the-bentley-ottmann-algorithm
|
||||||
|
Pointfs pts;
|
||||||
|
Point i;
|
||||||
|
for (size_t li = 0; li < lines.size(); ++li) {
|
||||||
|
const Line &l = lines[li];
|
||||||
|
const Point &a = l.a;
|
||||||
|
const Point &b = l.b;
|
||||||
|
Point min(std::min(a.x(), b.x()), std::min(a.y(), b.y()));
|
||||||
|
Point max(std::max(a.x(), b.x()), std::max(a.y(), b.y()));
|
||||||
|
BoundingBox bb(min, max);
|
||||||
|
for (size_t li_ = li + 1; li_ < lines.size(); ++li_) {
|
||||||
|
const Line &l_ = lines[li_];
|
||||||
|
const Point &a_ = l_.a;
|
||||||
|
const Point &b_ = l_.b;
|
||||||
|
if (a == b_ || b == a_ || a == a_ || b == b_) continue;
|
||||||
|
Point min_(std::min(a_.x(), b_.x()), std::min(a_.y(), b_.y()));
|
||||||
|
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
|
||||||
|
BoundingBox bb_(min_, max_);
|
||||||
|
// intersect of BB compare min max
|
||||||
|
if (bb.intersects(bb_) &&
|
||||||
|
l.intersection(l_, &i))
|
||||||
|
pts.push_back(i.cast<double>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pts;
|
||||||
|
}
|
||||||
|
} // namespace priv
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Lines &lines)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Polygon &polygon)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections(to_lines(polygon));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const Polygons &polygons)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections(to_lines(polygons));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygon &expolygon)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections(to_lines(expolygon));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
|
||||||
|
{
|
||||||
|
return priv::compute_intersections(to_lines(expolygons));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_CGAL_SWEEP_LINE
|
16
src/libslic3r/IntersectionPoints.hpp
Normal file
16
src/libslic3r/IntersectionPoints.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef slic3r_IntersectionPoints_hpp_
|
||||||
|
#define slic3r_IntersectionPoints_hpp_
|
||||||
|
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// collect all intersecting points
|
||||||
|
Pointfs intersection_points(const Lines &lines);
|
||||||
|
Pointfs intersection_points(const Polygon &polygon);
|
||||||
|
Pointfs intersection_points(const Polygons &polygons);
|
||||||
|
Pointfs intersection_points(const ExPolygon &expolygon);
|
||||||
|
Pointfs intersection_points(const ExPolygons &expolygons);
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_IntersectionPoints_hpp_
|
|
@ -210,6 +210,7 @@ public:
|
||||||
static const constexpr int Dim = 2;
|
static const constexpr int Dim = 2;
|
||||||
using Scalar = Vec2d::Scalar;
|
using Scalar = Vec2d::Scalar;
|
||||||
};
|
};
|
||||||
|
using Linesf = std::vector<Linef>;
|
||||||
|
|
||||||
class Linef3
|
class Linef3
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,12 +14,15 @@
|
||||||
#include "Arrange.hpp"
|
#include "Arrange.hpp"
|
||||||
#include "CustomGCode.hpp"
|
#include "CustomGCode.hpp"
|
||||||
#include "enum_bitmask.hpp"
|
#include "enum_bitmask.hpp"
|
||||||
|
//#include "ModelVolumeType.hpp"
|
||||||
|
#include "TextConfiguration.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace cereal {
|
namespace cereal {
|
||||||
class BinaryInputArchive;
|
class BinaryInputArchive;
|
||||||
|
@ -765,6 +768,7 @@ public:
|
||||||
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
|
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
|
||||||
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
||||||
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
|
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
|
||||||
|
const std::shared_ptr<const TriangleMesh>& get_mesh_shared_ptr() const { return m_mesh; }
|
||||||
// Configuration parameters specific to an object model geometry or a modifier volume,
|
// Configuration parameters specific to an object model geometry or a modifier volume,
|
||||||
// overriding the global Slic3r settings and the ModelObject settings.
|
// overriding the global Slic3r settings and the ModelObject settings.
|
||||||
ModelConfigObject config;
|
ModelConfigObject config;
|
||||||
|
@ -778,6 +782,10 @@ public:
|
||||||
// List of mesh facets painted for MMU segmentation.
|
// List of mesh facets painted for MMU segmentation.
|
||||||
FacetsAnnotation mmu_segmentation_facets;
|
FacetsAnnotation mmu_segmentation_facets;
|
||||||
|
|
||||||
|
// Is set only when volume is Embossed Text type
|
||||||
|
// Contain information how to re-create volume
|
||||||
|
std::optional<TextConfiguration> text_configuration;
|
||||||
|
|
||||||
// A parent object owning this modifier volume.
|
// A parent object owning this modifier volume.
|
||||||
ModelObject* get_object() const { return this->object; }
|
ModelObject* get_object() const { return this->object; }
|
||||||
ModelVolumeType type() const { return m_type; }
|
ModelVolumeType type() const { return m_type; }
|
||||||
|
@ -928,23 +936,8 @@ private:
|
||||||
// 1 -> is splittable
|
// 1 -> is splittable
|
||||||
mutable int m_is_splittable{ -1 };
|
mutable int m_is_splittable{ -1 };
|
||||||
|
|
||||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
|
inline bool check() {
|
||||||
{
|
assert(this->id().valid());
|
||||||
assert(this->id().valid());
|
|
||||||
assert(this->config.id().valid());
|
|
||||||
assert(this->supported_facets.id().valid());
|
|
||||||
assert(this->seam_facets.id().valid());
|
|
||||||
assert(this->mmu_segmentation_facets.id().valid());
|
|
||||||
assert(this->id() != this->config.id());
|
|
||||||
assert(this->id() != this->supported_facets.id());
|
|
||||||
assert(this->id() != this->seam_facets.id());
|
|
||||||
assert(this->id() != this->mmu_segmentation_facets.id());
|
|
||||||
if (mesh.facets_count() > 1)
|
|
||||||
calculate_convex_hull();
|
|
||||||
}
|
|
||||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
|
||||||
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
|
|
||||||
assert(this->id().valid());
|
|
||||||
assert(this->config.id().valid());
|
assert(this->config.id().valid());
|
||||||
assert(this->supported_facets.id().valid());
|
assert(this->supported_facets.id().valid());
|
||||||
assert(this->seam_facets.id().valid());
|
assert(this->seam_facets.id().valid());
|
||||||
|
@ -953,6 +946,24 @@ private:
|
||||||
assert(this->id() != this->supported_facets.id());
|
assert(this->id() != this->supported_facets.id());
|
||||||
assert(this->id() != this->seam_facets.id());
|
assert(this->id() != this->seam_facets.id());
|
||||||
assert(this->id() != this->mmu_segmentation_facets.id());
|
assert(this->id() != this->mmu_segmentation_facets.id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelVolume(ModelObject *object, const TriangleMesh &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||||
|
m_mesh(new TriangleMesh(mesh)), m_type(type), object(object)
|
||||||
|
{
|
||||||
|
assert(check());
|
||||||
|
if (m_mesh->facets_count() > 1) calculate_convex_hull();
|
||||||
|
}
|
||||||
|
ModelVolume(ModelObject *object, TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART)
|
||||||
|
: m_mesh(new TriangleMesh(std::move(mesh))), m_type(type), object(object)
|
||||||
|
{
|
||||||
|
assert(check());
|
||||||
|
if (m_mesh->facets_count() > 1) calculate_convex_hull();
|
||||||
|
}
|
||||||
|
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||||
|
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
|
||||||
|
assert(check());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
||||||
|
@ -961,7 +972,8 @@ private:
|
||||||
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
||||||
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
||||||
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
|
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
|
||||||
cut_info(other.cut_info)
|
cut_info(other.cut_info),
|
||||||
|
text_configuration(other.text_configuration)
|
||||||
{
|
{
|
||||||
assert(this->id().valid());
|
assert(this->id().valid());
|
||||||
assert(this->config.id().valid());
|
assert(this->config.id().valid());
|
||||||
|
@ -982,7 +994,8 @@ private:
|
||||||
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) :
|
ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) :
|
||||||
name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation),
|
name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation),
|
||||||
cut_info(other.cut_info)
|
cut_info(other.cut_info),
|
||||||
|
text_configuration(other.text_configuration)
|
||||||
{
|
{
|
||||||
assert(this->id().valid());
|
assert(this->id().valid());
|
||||||
assert(this->config.id().valid());
|
assert(this->config.id().valid());
|
||||||
|
@ -1029,6 +1042,7 @@ private:
|
||||||
cereal::load_by_value(ar, seam_facets);
|
cereal::load_by_value(ar, seam_facets);
|
||||||
cereal::load_by_value(ar, mmu_segmentation_facets);
|
cereal::load_by_value(ar, mmu_segmentation_facets);
|
||||||
cereal::load_by_value(ar, config);
|
cereal::load_by_value(ar, config);
|
||||||
|
cereal::load(ar, text_configuration);
|
||||||
assert(m_mesh);
|
assert(m_mesh);
|
||||||
if (has_convex_hull) {
|
if (has_convex_hull) {
|
||||||
cereal::load_optional(ar, m_convex_hull);
|
cereal::load_optional(ar, m_convex_hull);
|
||||||
|
@ -1045,6 +1059,7 @@ private:
|
||||||
cereal::save_by_value(ar, seam_facets);
|
cereal::save_by_value(ar, seam_facets);
|
||||||
cereal::save_by_value(ar, mmu_segmentation_facets);
|
cereal::save_by_value(ar, mmu_segmentation_facets);
|
||||||
cereal::save_by_value(ar, config);
|
cereal::save_by_value(ar, config);
|
||||||
|
cereal::save(ar, text_configuration);
|
||||||
if (has_convex_hull)
|
if (has_convex_hull)
|
||||||
cereal::save_optional(ar, m_convex_hull);
|
cereal::save_optional(ar, m_convex_hull);
|
||||||
}
|
}
|
||||||
|
|
16
src/libslic3r/ModelVolumeType.hpp
Normal file
16
src/libslic3r/ModelVolumeType.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef slic3r_ModelVolumeType_hpp_
|
||||||
|
#define slic3r_ModelVolumeType_hpp_
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
enum class ModelVolumeType : int {
|
||||||
|
INVALID = -1,
|
||||||
|
MODEL_PART = 0,
|
||||||
|
NEGATIVE_VOLUME,
|
||||||
|
PARAMETER_MODIFIER,
|
||||||
|
SUPPORT_BLOCKER,
|
||||||
|
SUPPORT_ENFORCER,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif /* slic3r_ModelVolumeType_hpp_ */
|
83
src/libslic3r/NSVGUtils.cpp
Normal file
83
src/libslic3r/NSVGUtils.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include "NSVGUtils.hpp"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
|
||||||
|
float tessTol,
|
||||||
|
Vec2f p1,
|
||||||
|
Vec2f p2,
|
||||||
|
Vec2f p3,
|
||||||
|
Vec2f p4,
|
||||||
|
int level)
|
||||||
|
{
|
||||||
|
Vec2f p12 = (p1 + p2) * 0.5f;
|
||||||
|
Vec2f p23 = (p2 + p3) * 0.5f;
|
||||||
|
Vec2f p34 = (p3 + p4) * 0.5f;
|
||||||
|
Vec2f p123 = (p12 + p23) * 0.5f;
|
||||||
|
|
||||||
|
Vec2f pd = p4 - p1;
|
||||||
|
Vec2f pd2 = p2 - p4;
|
||||||
|
float d2 = std::abs(pd2.x() * pd.y() - pd2.y() * pd.x());
|
||||||
|
Vec2f pd3 = p3 - p4;
|
||||||
|
float d3 = std::abs(pd3.x() * pd.y() - pd3.y() * pd.x());
|
||||||
|
float d23 = d2 + d3;
|
||||||
|
|
||||||
|
if ((d23 * d23) < tessTol * (pd.x() * pd.x() + pd.y() * pd.y())) {
|
||||||
|
polygon.points.emplace_back(p4.x(), p4.y());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
--level;
|
||||||
|
if (level == 0) return;
|
||||||
|
Vec2f p234 = (p23 + p34) * 0.5f;
|
||||||
|
Vec2f p1234 = (p123 + p234) * 0.5f;
|
||||||
|
flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level);
|
||||||
|
flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygons NSVGUtils::to_polygons(NSVGimage *image, float tessTol, int max_level)
|
||||||
|
{
|
||||||
|
Polygons polygons;
|
||||||
|
for (NSVGshape *shape = image->shapes; shape != NULL;
|
||||||
|
shape = shape->next) {
|
||||||
|
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue;
|
||||||
|
Slic3r::Polygon polygon;
|
||||||
|
if (shape->fill.type != NSVG_PAINT_NONE) {
|
||||||
|
for (NSVGpath *path = shape->paths; path != NULL;
|
||||||
|
path = path->next) {
|
||||||
|
// Flatten path
|
||||||
|
polygon.points.emplace_back(path->pts[0], path->pts[1]);
|
||||||
|
size_t path_size = (path->npts > 1) ?
|
||||||
|
static_cast<size_t>(path->npts - 1) : 0;
|
||||||
|
for (size_t i = 0; i < path_size; i += 3) {
|
||||||
|
float *p = &path->pts[i * 2];
|
||||||
|
Vec2f p1(p[0], p[1]), p2(p[2], p[3]), p3(p[4], p[5]),
|
||||||
|
p4(p[6], p[7]);
|
||||||
|
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4,
|
||||||
|
max_level);
|
||||||
|
}
|
||||||
|
if (path->closed && !polygon.empty()) {
|
||||||
|
polygons.push_back(polygon);
|
||||||
|
polygon = Slic3r::Polygon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!polygon.empty())
|
||||||
|
polygons.push_back(polygon);
|
||||||
|
}
|
||||||
|
return polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image,
|
||||||
|
float tessTol,
|
||||||
|
int max_level)
|
||||||
|
{
|
||||||
|
Polygons polygons = to_polygons(image, tessTol, max_level);
|
||||||
|
|
||||||
|
// Fix Y axis
|
||||||
|
for (Polygon &polygon : polygons)
|
||||||
|
for (Point &p : polygon.points) p.y() *= -1;
|
||||||
|
|
||||||
|
return Slic3r::union_ex(polygons);
|
||||||
|
}
|
34
src/libslic3r/NSVGUtils.hpp
Normal file
34
src/libslic3r/NSVGUtils.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef slic3r_NSVGUtils_hpp_
|
||||||
|
#define slic3r_NSVGUtils_hpp_
|
||||||
|
|
||||||
|
#include "Polygon.hpp"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "nanosvg/nanosvg.h" // load SVG file
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Helper function to work with nano svg
|
||||||
|
class NSVGUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NSVGUtils() = delete;
|
||||||
|
|
||||||
|
// inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
|
||||||
|
static void flatten_cubic_bez(Polygon &polygon,
|
||||||
|
float tessTol,
|
||||||
|
Vec2f p1,
|
||||||
|
Vec2f p2,
|
||||||
|
Vec2f p3,
|
||||||
|
Vec2f p4,
|
||||||
|
int level);
|
||||||
|
// convert svg image to ExPolygons
|
||||||
|
static ExPolygons to_ExPolygons(NSVGimage *image,
|
||||||
|
float tessTol = 10.,
|
||||||
|
int max_level = 10);
|
||||||
|
// convert svg paths to Polygons
|
||||||
|
static Polygons to_polygons(NSVGimage *image,
|
||||||
|
float tessTol = 10.,
|
||||||
|
int max_level = 10);
|
||||||
|
};
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_NSVGUtils_hpp_
|
|
@ -66,6 +66,24 @@ bool has_duplicate_points(std::vector<Point> &&pts)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Points collect_duplications(Points pts /* Copy */)
|
||||||
|
{
|
||||||
|
std::stable_sort(pts.begin(), pts.end());
|
||||||
|
Points duplicits;
|
||||||
|
const Point *prev = &pts.front();
|
||||||
|
for (size_t i = 1; i < pts.size(); ++i) {
|
||||||
|
const Point *act = &pts[i];
|
||||||
|
if (*prev == *act) {
|
||||||
|
// duplicit point
|
||||||
|
if (!duplicits.empty() && duplicits.back() == *act)
|
||||||
|
continue; // only unique duplicits
|
||||||
|
duplicits.push_back(*act);
|
||||||
|
}
|
||||||
|
prev = act;
|
||||||
|
}
|
||||||
|
return duplicits;
|
||||||
|
}
|
||||||
|
|
||||||
BoundingBox get_extents(const Points &pts)
|
BoundingBox get_extents(const Points &pts)
|
||||||
{
|
{
|
||||||
return BoundingBox(pts);
|
return BoundingBox(pts);
|
||||||
|
|
|
@ -267,6 +267,9 @@ inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts
|
||||||
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect adjecent(duplicit points)
|
||||||
|
Points collect_duplications(Points pts /* Copy */);
|
||||||
|
|
||||||
inline bool shorter_then(const Point& p0, const coord_t len)
|
inline bool shorter_then(const Point& p0, const coord_t len)
|
||||||
{
|
{
|
||||||
if (p0.x() > len || p0.x() < -len)
|
if (p0.x() > len || p0.x() < -len)
|
||||||
|
@ -547,6 +550,7 @@ namespace boost { namespace polygon {
|
||||||
} }
|
} }
|
||||||
// end Boost
|
// end Boost
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
// Serialization through the Cereal library
|
// Serialization through the Cereal library
|
||||||
namespace cereal {
|
namespace cereal {
|
||||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
|
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
|
||||||
|
@ -560,10 +564,11 @@ namespace cereal {
|
||||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
|
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
|
||||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
|
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
|
||||||
|
|
||||||
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
|
template<class Archive> void serialize(Archive& archive, Slic3r::Matrix4d &m){ archive(binary_data(m.data(), 4*4*sizeof(double))); }
|
||||||
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
|
template<class Archive> void serialize(Archive& archive, Slic3r::Matrix2f &m){ archive(binary_data(m.data(), 2*2*sizeof(float))); }
|
||||||
template<class Archive> void load(Archive& archive, Slic3r::Transform3d& m) { archive.loadBinary((char*)m.data(), sizeof(double) * 16); }
|
|
||||||
template<class Archive> void save(Archive& archive, const Slic3r::Transform3d& m) { archive.saveBinary((char*)m.data(), sizeof(double) * 16); }
|
// Eigen Transformation serialization
|
||||||
|
template<class Archive, class T, int N> inline void serialize(Archive& archive, Eigen::Transform<T, N, Eigen::Affine, Eigen::DontAlign>& t){ archive(t.matrix()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be able to use Vec<> and Mat<> in range based for loops:
|
// To be able to use Vec<> and Mat<> in range based for loops:
|
||||||
|
|
|
@ -169,13 +169,16 @@ inline Points to_points(const Polygon &poly)
|
||||||
return poly.points;
|
return poly.points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t count_points(const Polygons &polys) {
|
||||||
|
size_t n_points = 0;
|
||||||
|
for (const auto &poly: polys) n_points += poly.points.size();
|
||||||
|
return n_points;
|
||||||
|
}
|
||||||
|
|
||||||
inline Points to_points(const Polygons &polys)
|
inline Points to_points(const Polygons &polys)
|
||||||
{
|
{
|
||||||
size_t n_points = 0;
|
|
||||||
for (size_t i = 0; i < polys.size(); ++ i)
|
|
||||||
n_points += polys[i].points.size();
|
|
||||||
Points points;
|
Points points;
|
||||||
points.reserve(n_points);
|
points.reserve(count_points(polys));
|
||||||
for (const Polygon &poly : polys)
|
for (const Polygon &poly : polys)
|
||||||
append(points, poly.points);
|
append(points, poly.points);
|
||||||
return points;
|
return points;
|
||||||
|
@ -195,11 +198,8 @@ inline Lines to_lines(const Polygon &poly)
|
||||||
|
|
||||||
inline Lines to_lines(const Polygons &polys)
|
inline Lines to_lines(const Polygons &polys)
|
||||||
{
|
{
|
||||||
size_t n_lines = 0;
|
|
||||||
for (size_t i = 0; i < polys.size(); ++ i)
|
|
||||||
n_lines += polys[i].points.size();
|
|
||||||
Lines lines;
|
Lines lines;
|
||||||
lines.reserve(n_lines);
|
lines.reserve(count_points(polys));
|
||||||
for (size_t i = 0; i < polys.size(); ++ i) {
|
for (size_t i = 0; i < polys.size(); ++ i) {
|
||||||
const Polygon &poly = polys[i];
|
const Polygon &poly = polys[i];
|
||||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
||||||
|
|
|
@ -184,7 +184,7 @@ void Slic3r::its_quadric_edge_collapse(
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
status_fn(status_init_size);
|
status_fn(status_init_size);
|
||||||
|
|
||||||
//its_store_triangle(its, "triangle.obj", 1182);
|
//its_store_triangle_to_obj(its, "triangle.obj", 1182);
|
||||||
//store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos);
|
//store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos);
|
||||||
|
|
||||||
// convert from triangle index to mutable priority queue index
|
// convert from triangle index to mutable priority queue index
|
||||||
|
@ -904,7 +904,7 @@ void QuadricEdgeCollapse::store_surround(const char *obj_filename,
|
||||||
std::vector<size_t> trs;
|
std::vector<size_t> trs;
|
||||||
trs.reserve(triangles.size());
|
trs.reserve(triangles.size());
|
||||||
for (size_t ti : triangles) trs.push_back(ti);
|
for (size_t ti : triangles) trs.push_back(ti);
|
||||||
its_store_triangles(its, obj_filename, trs);
|
its_store_triangles_to_obj(its, obj_filename, trs);
|
||||||
// its_write_obj(its,"original.obj");
|
// its_write_obj(its,"original.obj");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#ifndef slic3r_quadric_edge_collapse_hpp_
|
||||||
|
#define slic3r_quadric_edge_collapse_hpp_
|
||||||
|
|
||||||
// paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf
|
// paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf
|
||||||
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
|
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
|
||||||
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
|
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
|
||||||
|
@ -26,3 +29,4 @@ void its_quadric_edge_collapse(
|
||||||
std::function<void(int)> statusfn = nullptr);
|
std::function<void(int)> statusfn = nullptr);
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_quadric_edge_collapse_hpp_
|
||||||
|
|
|
@ -378,4 +378,10 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
|
||||||
svg.Close();
|
svg.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float SVG::to_svg_coord(float x) throw()
|
||||||
|
{
|
||||||
|
// return x;
|
||||||
|
return unscale<float>(x) * 10.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
|
@ -167,9 +167,9 @@ public:
|
||||||
{ export_expolygons(path.c_str(), expolygons_with_attributes); }
|
{ export_expolygons(path.c_str(), expolygons_with_attributes); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static float to_svg_coord(float x) throw() { return unscale<float>(x) * 10.f; }
|
static float to_svg_coord(float x) throw();
|
||||||
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
||||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +1,69 @@
|
||||||
#ifndef _prusaslicer_technologies_h_
|
#ifndef _prusaslicer_technologies_h_
|
||||||
#define _prusaslicer_technologies_h_
|
#define _prusaslicer_technologies_h_
|
||||||
|
|
||||||
//=============
|
//=============
|
||||||
// debug techs
|
// debug techs
|
||||||
//=============
|
//=============
|
||||||
// Shows camera target in the 3D scene
|
// Shows camera target in the 3D scene
|
||||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||||
// Log debug messages to console when changing selection
|
// Log debug messages to console when changing selection
|
||||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||||
// Shows an imgui dialog with camera related data
|
// Shows an imgui dialog with camera related data
|
||||||
#define ENABLE_CAMERA_STATISTICS 0
|
#define ENABLE_CAMERA_STATISTICS 0
|
||||||
// Enable extracting thumbnails from selected gcode and save them as png files
|
// Enable extracting thumbnails from selected gcode and save them as png files
|
||||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
|
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
|
||||||
// Disable synchronization of unselected instances
|
// Disable synchronization of unselected instances
|
||||||
#define DISABLE_INSTANCES_SYNCH 0
|
#define DISABLE_INSTANCES_SYNCH 0
|
||||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
||||||
// Enable G-Code viewer statistics imgui dialog
|
// Enable G-Code viewer statistics imgui dialog
|
||||||
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||||
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||||
// Enable project dirty state manager debug window
|
// Enable project dirty state manager debug window
|
||||||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
||||||
// Disable using instanced models to render options in gcode preview
|
// Disable using instanced models to render options in gcode preview
|
||||||
#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1
|
#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1
|
||||||
// Enable Measure Gizmo debug window
|
// Enable Measure Gizmo debug window
|
||||||
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
||||||
|
|
||||||
|
|
||||||
// Enable rendering of objects using environment map
|
// Enable rendering of objects using environment map
|
||||||
#define ENABLE_ENVIRONMENT_MAP 0
|
#define ENABLE_ENVIRONMENT_MAP 0
|
||||||
// Enable smoothing of objects normals
|
// Enable smoothing of objects normals
|
||||||
#define ENABLE_SMOOTH_NORMALS 0
|
#define ENABLE_SMOOTH_NORMALS 0
|
||||||
|
|
||||||
|
|
||||||
//====================
|
//====================
|
||||||
// 2.5.0.alpha1 techs
|
// 2.5.0.alpha1 techs
|
||||||
//====================
|
//====================
|
||||||
#define ENABLE_2_5_0_ALPHA1 1
|
#define ENABLE_2_5_0_ALPHA1 1
|
||||||
|
|
||||||
// Enable removal of legacy OpenGL calls
|
// Enable removal of legacy OpenGL calls
|
||||||
#define ENABLE_LEGACY_OPENGL_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_LEGACY_OPENGL_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable OpenGL ES
|
// Enable OpenGL ES
|
||||||
#define ENABLE_OPENGL_ES (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
#define ENABLE_OPENGL_ES (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||||
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
|
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
|
||||||
#define ENABLE_GL_CORE_PROFILE (1 && ENABLE_LEGACY_OPENGL_REMOVAL && !ENABLE_OPENGL_ES)
|
#define ENABLE_GL_CORE_PROFILE (1 && ENABLE_LEGACY_OPENGL_REMOVAL && !ENABLE_OPENGL_ES)
|
||||||
// Enable OpenGL debug messages using debug context
|
// Enable OpenGL debug messages using debug context
|
||||||
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
|
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
|
||||||
// Shows an imgui dialog with GLModel statistics data
|
// Shows an imgui dialog with GLModel statistics data
|
||||||
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||||
// Enable rework of Reload from disk command
|
// Enable rework of Reload from disk command
|
||||||
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable alternative version of file_wildcards()
|
// Enable alternative version of file_wildcards()
|
||||||
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable processing of gcode G2 and G3 lines
|
// Enable processing of gcode G2 and G3 lines
|
||||||
#define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_PROCESS_G2_G3_LINES (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable fix of used filament data exported to gcode file
|
// Enable fix of used filament data exported to gcode file
|
||||||
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1)
|
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1)
|
||||||
// Enable picking using raytracing
|
// Enable picking using raytracing
|
||||||
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
#define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||||
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
|
#define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING)
|
||||||
|
|
||||||
|
|
||||||
#endif // _prusaslicer_technologies_h_
|
#endif // _prusaslicer_technologies_h_
|
||||||
|
|
239
src/libslic3r/TextConfiguration.hpp
Normal file
239
src/libslic3r/TextConfiguration.hpp
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
#ifndef slic3r_TextConfiguration_hpp_
|
||||||
|
#define slic3r_TextConfiguration_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <cereal/types/optional.hpp>
|
||||||
|
#include <cereal/types/string.hpp>
|
||||||
|
#include <cereal/archives/binary.hpp>
|
||||||
|
#include "Point.hpp" // Transform3d
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User modifiable property of text style
|
||||||
|
/// NOTE: OnEdit fix serializations: EmbossStylesSerializable, TextConfigurationSerialization
|
||||||
|
/// </summary>
|
||||||
|
struct FontProp
|
||||||
|
{
|
||||||
|
// define extra space between letters, negative mean closer letter
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<int> char_gap; // [in font point]
|
||||||
|
|
||||||
|
// define extra space between lines, negative mean closer lines
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<int> line_gap; // [in font point]
|
||||||
|
|
||||||
|
// Z depth of text
|
||||||
|
float emboss; // [in mm]
|
||||||
|
|
||||||
|
// Flag that text should use surface cutted from object
|
||||||
|
// FontProp::distance should without value
|
||||||
|
// FontProp::emboss should be positive number
|
||||||
|
// Note: default value is false
|
||||||
|
bool use_surface;
|
||||||
|
|
||||||
|
// positive value mean wider character shape
|
||||||
|
// negative value mean tiner character shape
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<float> boldness; // [in mm]
|
||||||
|
|
||||||
|
// positive value mean italic of character (CW)
|
||||||
|
// negative value mean CCW skew (unItalic)
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<float> skew; // [ration x:y]
|
||||||
|
|
||||||
|
// distance from surface point
|
||||||
|
// used for move over model surface
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<float> distance; // [in mm]
|
||||||
|
|
||||||
|
// change up vector direction of font
|
||||||
|
// When not set value is zero and is not stored
|
||||||
|
std::optional<float> angle; // [in radians]
|
||||||
|
|
||||||
|
// Parameter for True Type Font collections
|
||||||
|
// Select index of font in collection
|
||||||
|
std::optional<unsigned int> collection_number;
|
||||||
|
|
||||||
|
//enum class Align {
|
||||||
|
// left,
|
||||||
|
// right,
|
||||||
|
// center,
|
||||||
|
// top_left,
|
||||||
|
// top_right,
|
||||||
|
// top_center,
|
||||||
|
// bottom_left,
|
||||||
|
// bottom_right,
|
||||||
|
// bottom_center
|
||||||
|
//};
|
||||||
|
//// change pivot of text
|
||||||
|
//// When not set, center is used and is not stored
|
||||||
|
//std::optional<Align> align;
|
||||||
|
|
||||||
|
//////
|
||||||
|
// Duplicit data to wxFontDescriptor
|
||||||
|
// used for store/load .3mf file
|
||||||
|
//////
|
||||||
|
|
||||||
|
// Height of text line (letters)
|
||||||
|
// duplicit to wxFont::PointSize
|
||||||
|
float size_in_mm; // [in mm]
|
||||||
|
|
||||||
|
// Additional data about font to be able to find substitution,
|
||||||
|
// when same font is not installed
|
||||||
|
std::optional<std::string> family;
|
||||||
|
std::optional<std::string> face_name;
|
||||||
|
std::optional<std::string> style;
|
||||||
|
std::optional<std::string> weight;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only constructor with restricted values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line_height">Y size of text [in mm]</param>
|
||||||
|
/// <param name="depth">Z size of text [in mm]</param>
|
||||||
|
FontProp(float line_height = 10.f, float depth = 2.f)
|
||||||
|
: emboss(depth), size_in_mm(line_height), use_surface(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const FontProp& other) const {
|
||||||
|
return
|
||||||
|
char_gap == other.char_gap &&
|
||||||
|
line_gap == other.line_gap &&
|
||||||
|
use_surface == other.use_surface &&
|
||||||
|
is_approx(emboss, other.emboss) &&
|
||||||
|
is_approx(size_in_mm, other.size_in_mm) &&
|
||||||
|
is_approx(boldness, other.boldness) &&
|
||||||
|
is_approx(skew, other.skew) &&
|
||||||
|
is_approx(distance, other.distance) &&
|
||||||
|
is_approx(angle, other.angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// undo / redo stack recovery
|
||||||
|
template<class Archive> void save(Archive &ar) const
|
||||||
|
{
|
||||||
|
ar(emboss, use_surface, size_in_mm);
|
||||||
|
cereal::save(ar, char_gap);
|
||||||
|
cereal::save(ar, line_gap);
|
||||||
|
cereal::save(ar, boldness);
|
||||||
|
cereal::save(ar, skew);
|
||||||
|
cereal::save(ar, distance);
|
||||||
|
cereal::save(ar, angle);
|
||||||
|
cereal::save(ar, collection_number);
|
||||||
|
cereal::save(ar, family);
|
||||||
|
cereal::save(ar, face_name);
|
||||||
|
cereal::save(ar, style);
|
||||||
|
cereal::save(ar, weight);
|
||||||
|
}
|
||||||
|
template<class Archive> void load(Archive &ar)
|
||||||
|
{
|
||||||
|
ar(emboss, use_surface, size_in_mm);
|
||||||
|
cereal::load(ar, char_gap);
|
||||||
|
cereal::load(ar, line_gap);
|
||||||
|
cereal::load(ar, boldness);
|
||||||
|
cereal::load(ar, skew);
|
||||||
|
cereal::load(ar, distance);
|
||||||
|
cereal::load(ar, angle);
|
||||||
|
cereal::load(ar, collection_number);
|
||||||
|
cereal::load(ar, family);
|
||||||
|
cereal::load(ar, face_name);
|
||||||
|
cereal::load(ar, style);
|
||||||
|
cereal::load(ar, weight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Style of embossed text
|
||||||
|
/// (Path + Type) must define how to open font for using on different OS
|
||||||
|
/// NOTE: OnEdit fix serializations: EmbossStylesSerializable, TextConfigurationSerialization
|
||||||
|
/// </summary>
|
||||||
|
struct EmbossStyle
|
||||||
|
{
|
||||||
|
// Human readable name of style it is shown in GUI
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
// Define how to open font
|
||||||
|
// Meaning depend on type
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
enum class Type;
|
||||||
|
// Define what is stored in path
|
||||||
|
Type type { Type::undefined };
|
||||||
|
|
||||||
|
// User modification of font style
|
||||||
|
FontProp prop;
|
||||||
|
|
||||||
|
// when name is empty than Font item was loaded from .3mf file
|
||||||
|
// and potentionaly it is not reproducable
|
||||||
|
// define data stored in path
|
||||||
|
// when wx change way of storing add new descriptor Type
|
||||||
|
enum class Type {
|
||||||
|
undefined = 0,
|
||||||
|
|
||||||
|
// wx font descriptors are platform dependent
|
||||||
|
// path is font descriptor generated by wxWidgets
|
||||||
|
wx_win_font_descr, // on Windows
|
||||||
|
wx_lin_font_descr, // on Linux
|
||||||
|
wx_mac_font_descr, // on Max OS
|
||||||
|
|
||||||
|
// TrueTypeFont file loacation on computer
|
||||||
|
// for privacy: only filename is stored into .3mf
|
||||||
|
file_path
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const EmbossStyle &other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
type == other.type &&
|
||||||
|
prop == other.prop &&
|
||||||
|
name == other.name &&
|
||||||
|
path == other.path
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// undo / redo stack recovery
|
||||||
|
template<class Archive> void serialize(Archive &ar){
|
||||||
|
ar(name, path, type, prop);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emboss style name inside vector is unique
|
||||||
|
// It is not map beacuse items has own order (view inside of slect)
|
||||||
|
// It is stored into AppConfig by EmbossStylesSerializable
|
||||||
|
using EmbossStyles = std::vector<EmbossStyle>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define how to create 'Text volume'
|
||||||
|
/// It is stored into .3mf by TextConfigurationSerialization
|
||||||
|
/// It is part of ModelVolume optional data
|
||||||
|
/// </summary>
|
||||||
|
struct TextConfiguration
|
||||||
|
{
|
||||||
|
// Style of embossed text
|
||||||
|
EmbossStyle style;
|
||||||
|
|
||||||
|
// Embossed text value
|
||||||
|
std::string text = "None";
|
||||||
|
|
||||||
|
// !!! Volume stored in .3mf has transformed vertices.
|
||||||
|
// (baked transformation into vertices position)
|
||||||
|
// Only place for fill this is when load from .3mf
|
||||||
|
// This is correct volume transformation
|
||||||
|
std::optional<Transform3d> fix_3mf_tr;
|
||||||
|
|
||||||
|
// undo / redo stack recovery
|
||||||
|
template<class Archive> void save(Archive &ar) const{
|
||||||
|
ar(text, style);
|
||||||
|
cereal::save(ar, fix_3mf_tr);
|
||||||
|
}
|
||||||
|
template<class Archive> void load(Archive &ar){
|
||||||
|
ar(text, style);
|
||||||
|
cereal::load(ar, fix_3mf_tr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_TextConfiguration_hpp_
|
12
src/libslic3r/Timer.cpp
Normal file
12
src/libslic3r/Timer.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "Timer.hpp"
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
Slic3r::Timer::Timer(const std::string &name) : m_name(name), m_start(steady_clock::now()) {}
|
||||||
|
|
||||||
|
Slic3r::Timer::~Timer()
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
|
||||||
|
duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms";
|
||||||
|
}
|
31
src/libslic3r/Timer.hpp
Normal file
31
src/libslic3r/Timer.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef libslic3r_Timer_hpp_
|
||||||
|
#define libslic3r_Timer_hpp_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instance of this class is used for measure time consumtion
|
||||||
|
/// of block code until instance is alive and write result to debug output
|
||||||
|
/// </summary>
|
||||||
|
class Timer
|
||||||
|
{
|
||||||
|
std::string m_name;
|
||||||
|
std::chrono::steady_clock::time_point m_start;
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// name describe timer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Describe timer in consol log</param>
|
||||||
|
Timer(const std::string& name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// name describe timer
|
||||||
|
/// </summary>
|
||||||
|
~Timer();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // libslic3r_Timer_hpp_
|
|
@ -783,9 +783,9 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit)
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool its_store_triangle(const indexed_triangle_set &its,
|
bool its_store_triangle_to_obj(const indexed_triangle_set &its,
|
||||||
const char * obj_filename,
|
const char *obj_filename,
|
||||||
size_t triangle_index)
|
size_t triangle_index)
|
||||||
{
|
{
|
||||||
if (its.indices.size() <= triangle_index) return false;
|
if (its.indices.size() <= triangle_index) return false;
|
||||||
Vec3i t = its.indices[triangle_index];
|
Vec3i t = its.indices[triangle_index];
|
||||||
|
@ -796,9 +796,9 @@ bool its_store_triangle(const indexed_triangle_set &its,
|
||||||
return its_write_obj(its2, obj_filename);
|
return its_write_obj(its2, obj_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool its_store_triangles(const indexed_triangle_set &its,
|
bool its_store_triangles_to_obj(const indexed_triangle_set &its,
|
||||||
const char * obj_filename,
|
const char *obj_filename,
|
||||||
const std::vector<size_t> & triangles)
|
const std::vector<size_t> &triangles)
|
||||||
{
|
{
|
||||||
indexed_triangle_set its2;
|
indexed_triangle_set its2;
|
||||||
its2.vertices.reserve(triangles.size() * 3);
|
its2.vertices.reserve(triangles.size() * 3);
|
||||||
|
@ -1206,6 +1206,23 @@ void its_reverse_all_facets(indexed_triangle_set &its)
|
||||||
std::swap(face[0], face[1]);
|
std::swap(face[0], face[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add)
|
||||||
|
{
|
||||||
|
if (its.empty()) {
|
||||||
|
its = std::move(its_add);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &verts = its.vertices;
|
||||||
|
size_t verts_size = verts.size();
|
||||||
|
Slic3r::append(verts, std::move(its_add.vertices));
|
||||||
|
|
||||||
|
// increase face indices
|
||||||
|
int offset = static_cast<int>(verts_size);
|
||||||
|
for (auto &face : its_add.indices)
|
||||||
|
for (int i = 0; i < 3; ++i) face[i] += offset;
|
||||||
|
Slic3r::append(its.indices, std::move(its_add.indices));
|
||||||
|
}
|
||||||
|
|
||||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
|
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
|
||||||
{
|
{
|
||||||
auto N = int(A.vertices.size());
|
auto N = int(A.vertices.size());
|
||||||
|
|
|
@ -211,8 +211,8 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit =
|
||||||
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
|
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
|
||||||
|
|
||||||
// store part of index triangle set
|
// store part of index triangle set
|
||||||
bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
|
bool its_store_triangle_to_obj(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
|
||||||
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
|
bool its_store_triangles_to_obj(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
|
||||||
|
|
||||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
|
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
|
||||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
|
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
|
||||||
|
@ -285,6 +285,14 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its,
|
||||||
float its_volume(const indexed_triangle_set &its);
|
float its_volume(const indexed_triangle_set &its);
|
||||||
float its_average_edge_length(const indexed_triangle_set &its);
|
float its_average_edge_length(const indexed_triangle_set &its);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merge one triangle mesh to another
|
||||||
|
/// Added triangle set will be consumed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="its">IN/OUT triangle mesh</param>
|
||||||
|
/// <param name="its_add">Triangle mesh (will be consumed)</param>
|
||||||
|
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add);
|
||||||
|
|
||||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
|
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
|
||||||
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
|
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
|
||||||
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
|
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
|
||||||
|
|
328
src/libslic3r/Triangulation.cpp
Normal file
328
src/libslic3r/Triangulation.cpp
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
#include "Triangulation.hpp"
|
||||||
|
#include "IntersectionPoints.hpp"
|
||||||
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
||||||
|
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||||
|
#include <CGAL/spatial_sort.h>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
namespace priv{
|
||||||
|
inline void insert_edges(Triangulation::HalfEdges &edges, uint32_t &offset, const Polygon &polygon, const Triangulation::Changes& changes) {
|
||||||
|
const Points &pts = polygon.points;
|
||||||
|
uint32_t size = static_cast<uint32_t>(pts.size());
|
||||||
|
uint32_t last_index = offset + size - 1;
|
||||||
|
uint32_t prev_index = changes[last_index];
|
||||||
|
for (uint32_t i = 0; i < size; ++i) {
|
||||||
|
uint32_t index = changes[offset + i];
|
||||||
|
// when duplicit points are neighbor
|
||||||
|
if (prev_index == index) continue;
|
||||||
|
edges.push_back({prev_index, index});
|
||||||
|
prev_index = index;
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void insert_edges(Triangulation::HalfEdges &edges, uint32_t &offset, const Polygon &polygon) {
|
||||||
|
const Points &pts = polygon.points;
|
||||||
|
uint32_t size = static_cast<uint32_t>(pts.size());
|
||||||
|
uint32_t prev_index = offset + size - 1;
|
||||||
|
for (uint32_t i = 0; i < size; ++i) {
|
||||||
|
uint32_t index = offset + i;
|
||||||
|
edges.push_back({prev_index, index});
|
||||||
|
prev_index = index;
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool has_bidirectional_constrained(
|
||||||
|
const Triangulation::HalfEdges &constrained)
|
||||||
|
{
|
||||||
|
for (const auto &c : constrained) {
|
||||||
|
auto key = std::make_pair(c.second, c.first);
|
||||||
|
auto it = std::lower_bound(constrained.begin(), constrained.end(),
|
||||||
|
key);
|
||||||
|
if (it != constrained.end() && *it == key) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_unique(const Points &points) {
|
||||||
|
Points pts = points; // copy
|
||||||
|
std::sort(pts.begin(), pts.end());
|
||||||
|
auto it = std::adjacent_find(pts.begin(), pts.end());
|
||||||
|
return it == pts.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool has_self_intersection(
|
||||||
|
const Points &points,
|
||||||
|
const Triangulation::HalfEdges &constrained_half_edges)
|
||||||
|
{
|
||||||
|
Lines lines;
|
||||||
|
lines.reserve(constrained_half_edges.size());
|
||||||
|
for (const auto &he : constrained_half_edges)
|
||||||
|
lines.emplace_back(points[he.first], points[he.second]);
|
||||||
|
return !intersection_points(lines).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace priv
|
||||||
|
|
||||||
|
//#define VISUALIZE_TRIANGULATION
|
||||||
|
#ifdef VISUALIZE_TRIANGULATION
|
||||||
|
#include "admesh/stl.h" // indexed triangle set
|
||||||
|
static void visualize(const Points &points,
|
||||||
|
const Triangulation::Indices &indices,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
// visualize
|
||||||
|
indexed_triangle_set its;
|
||||||
|
its.vertices.reserve(points.size());
|
||||||
|
for (const Point &p : points) its.vertices.emplace_back(p.x(), p.y(), 0.);
|
||||||
|
its.indices = indices;
|
||||||
|
its_write_obj(its, filename);
|
||||||
|
}
|
||||||
|
#endif // VISUALIZE_TRIANGULATION
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const Points &points,
|
||||||
|
const HalfEdges &constrained_half_edges)
|
||||||
|
{
|
||||||
|
assert(!points.empty());
|
||||||
|
assert(!constrained_half_edges.empty());
|
||||||
|
// constrained must be sorted
|
||||||
|
assert(std::is_sorted(constrained_half_edges.begin(),
|
||||||
|
constrained_half_edges.end()));
|
||||||
|
// check that there is no duplicit constrained edge
|
||||||
|
assert(std::adjacent_find(constrained_half_edges.begin(), constrained_half_edges.end()) == constrained_half_edges.end());
|
||||||
|
// edges can NOT contain bidirectional constrained
|
||||||
|
assert(!priv::has_bidirectional_constrained(constrained_half_edges));
|
||||||
|
// check that there is only unique poistion of points
|
||||||
|
assert(priv::is_unique(points));
|
||||||
|
assert(!priv::has_self_intersection(points, constrained_half_edges));
|
||||||
|
// use cgal triangulation
|
||||||
|
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||||
|
using Vb = CGAL::Triangulation_vertex_base_with_info_2<uint32_t, K>;
|
||||||
|
using Fb = CGAL::Constrained_triangulation_face_base_2<K>;
|
||||||
|
using Tds = CGAL::Triangulation_data_structure_2<Vb, Fb>;
|
||||||
|
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, Tds, CGAL::Exact_predicates_tag>;
|
||||||
|
|
||||||
|
// construct a constrained triangulation
|
||||||
|
CDT cdt;
|
||||||
|
{
|
||||||
|
std::vector<CDT::Vertex_handle> vertices_handle(points.size()); // for constriants
|
||||||
|
using Point_with_ord = std::pair<CDT::Point, size_t>;
|
||||||
|
using SearchTrait = CGAL::Spatial_sort_traits_adapter_2
|
||||||
|
<K, CGAL::First_of_pair_property_map<Point_with_ord> >;
|
||||||
|
|
||||||
|
std::vector<Point_with_ord> cdt_points;
|
||||||
|
cdt_points.reserve(points.size());
|
||||||
|
size_t ord = 0;
|
||||||
|
for (const auto &p : points)
|
||||||
|
cdt_points.emplace_back(std::make_pair(CDT::Point{p.x(), p.y()}, ord++));
|
||||||
|
|
||||||
|
SearchTrait st;
|
||||||
|
CGAL::spatial_sort(cdt_points.begin(), cdt_points.end(), st);
|
||||||
|
CDT::Face_handle f;
|
||||||
|
for (const auto& p : cdt_points) {
|
||||||
|
auto handle = cdt.insert(p.first, f);
|
||||||
|
handle->info() = p.second;
|
||||||
|
vertices_handle[p.second] = handle;
|
||||||
|
f = handle->face();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain the triangulation.
|
||||||
|
for (const HalfEdge &edge : constrained_half_edges)
|
||||||
|
cdt.insert_constraint(vertices_handle[edge.first], vertices_handle[edge.second]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto faces = cdt.finite_face_handles();
|
||||||
|
|
||||||
|
// Unmark constrained edges of outside faces.
|
||||||
|
size_t num_faces = 0;
|
||||||
|
for (CDT::Face_handle fh : faces) {
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
if (!fh->is_constrained(i)) continue;
|
||||||
|
auto key = std::make_pair(fh->vertex((i + 2) % 3)->info(), fh->vertex((i + 1) % 3)->info());
|
||||||
|
auto it = std::lower_bound(constrained_half_edges.begin(), constrained_half_edges.end(), key);
|
||||||
|
if (it == constrained_half_edges.end() || *it != key) continue;
|
||||||
|
// This face contains a constrained edge and it is outside.
|
||||||
|
for (int j = 0; j < 3; ++ j)
|
||||||
|
fh->set_constraint(j, false);
|
||||||
|
--num_faces;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++num_faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inside = [](CDT::Face_handle &fh) {
|
||||||
|
return fh->neighbor(0) != fh &&
|
||||||
|
(fh->is_constrained(0) ||
|
||||||
|
fh->is_constrained(1) ||
|
||||||
|
fh->is_constrained(2));
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef VISUALIZE_TRIANGULATION
|
||||||
|
std::vector<Vec3i> indices2;
|
||||||
|
indices2.reserve(num_faces);
|
||||||
|
for (CDT::Face_handle fh : faces)
|
||||||
|
if (inside(fh)) indices2.emplace_back(fh->vertex(0)->info(), fh->vertex(1)->info(), fh->vertex(2)->info());
|
||||||
|
visualize(points, indices2, "C:/data/temp/triangulation_without_floodfill.obj");
|
||||||
|
#endif // VISUALIZE_TRIANGULATION
|
||||||
|
|
||||||
|
// Propagate inside the constrained regions.
|
||||||
|
std::vector<CDT::Face_handle> queue;
|
||||||
|
queue.reserve(num_faces);
|
||||||
|
for (CDT::Face_handle seed : faces){
|
||||||
|
if (!inside(seed)) continue;
|
||||||
|
// Seed fill to neighbor faces.
|
||||||
|
queue.emplace_back(seed);
|
||||||
|
while (! queue.empty()) {
|
||||||
|
CDT::Face_handle fh = queue.back();
|
||||||
|
queue.pop_back();
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
if (fh->is_constrained(i)) continue;
|
||||||
|
// Propagate along this edge.
|
||||||
|
fh->set_constraint(i, true);
|
||||||
|
CDT::Face_handle nh = fh->neighbor(i);
|
||||||
|
bool was_inside = inside(nh);
|
||||||
|
// Mark the other side of this edge.
|
||||||
|
nh->set_constraint(nh->index(fh), true);
|
||||||
|
if (! was_inside)
|
||||||
|
queue.push_back(nh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vec3i> indices;
|
||||||
|
indices.reserve(num_faces);
|
||||||
|
for (CDT::Face_handle fh : faces)
|
||||||
|
if (inside(fh))
|
||||||
|
indices.emplace_back(fh->vertex(0)->info(), fh->vertex(1)->info(), fh->vertex(2)->info());
|
||||||
|
|
||||||
|
#ifdef VISUALIZE_TRIANGULATION
|
||||||
|
visualize(points, indices, "C:/data/temp/triangulation.obj");
|
||||||
|
#endif // VISUALIZE_TRIANGULATION
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const Polygon &polygon)
|
||||||
|
{
|
||||||
|
const Points &pts = polygon.points;
|
||||||
|
HalfEdges edges;
|
||||||
|
edges.reserve(pts.size());
|
||||||
|
uint32_t offset = 0;
|
||||||
|
priv::insert_edges(edges, offset, polygon);
|
||||||
|
std::sort(edges.begin(), edges.end());
|
||||||
|
return triangulate(pts, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const Polygons &polygons)
|
||||||
|
{
|
||||||
|
size_t count = count_points(polygons);
|
||||||
|
Points points;
|
||||||
|
points.reserve(count);
|
||||||
|
|
||||||
|
HalfEdges edges;
|
||||||
|
edges.reserve(count);
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
for (const Polygon &polygon : polygons) {
|
||||||
|
Slic3r::append(points, polygon.points);
|
||||||
|
priv::insert_edges(edges, offset, polygon);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(edges.begin(), edges.end());
|
||||||
|
return triangulate(points, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const ExPolygon &expolygon){
|
||||||
|
ExPolygons expolys({expolygon});
|
||||||
|
return triangulate(expolys);
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons){
|
||||||
|
Points pts = to_points(expolygons);
|
||||||
|
Points d_pts = collect_duplications(pts);
|
||||||
|
if (d_pts.empty()) return triangulate(expolygons, pts);
|
||||||
|
|
||||||
|
Changes changes = create_changes(pts, d_pts);
|
||||||
|
Indices indices = triangulate(expolygons, pts, changes);
|
||||||
|
// reverse map for changes
|
||||||
|
Changes changes2(changes.size(), std::numeric_limits<uint32_t>::max());
|
||||||
|
for (size_t i = 0; i < changes.size(); ++i)
|
||||||
|
changes2[changes[i]] = i;
|
||||||
|
|
||||||
|
// convert indices into expolygons indicies
|
||||||
|
for (Vec3i &t : indices)
|
||||||
|
for (size_t ti = 0; ti < 3; ti++) t[ti] = changes2[t[ti]];
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons, const Points &points)
|
||||||
|
{
|
||||||
|
assert(count_points(expolygons) == points.size());
|
||||||
|
// when contain duplicit coordinate in points will not work properly
|
||||||
|
assert(collect_duplications(points).empty());
|
||||||
|
|
||||||
|
HalfEdges edges;
|
||||||
|
edges.reserve(points.size());
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (const ExPolygon &expolygon : expolygons) {
|
||||||
|
priv::insert_edges(edges, offset, expolygon.contour);
|
||||||
|
for (const Polygon &hole : expolygon.holes)
|
||||||
|
priv::insert_edges(edges, offset, hole);
|
||||||
|
}
|
||||||
|
std::sort(edges.begin(), edges.end());
|
||||||
|
return triangulate(points, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Indices Triangulation::triangulate(const ExPolygons &expolygons, const Points& points, const Changes& changes)
|
||||||
|
{
|
||||||
|
assert(!points.empty());
|
||||||
|
assert(count_points(expolygons) == points.size());
|
||||||
|
assert(changes.size() == points.size());
|
||||||
|
// IMPROVE: search from end and somehow distiquish that value is not a change
|
||||||
|
uint32_t count_points = *std::max_element(changes.begin(), changes.end())+1;
|
||||||
|
Points pts(count_points);
|
||||||
|
for (size_t i = 0; i < changes.size(); i++)
|
||||||
|
pts[changes[i]] = points[i];
|
||||||
|
|
||||||
|
HalfEdges edges;
|
||||||
|
edges.reserve(points.size());
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (const ExPolygon &expolygon : expolygons) {
|
||||||
|
priv::insert_edges(edges, offset, expolygon.contour, changes);
|
||||||
|
for (const Polygon &hole : expolygon.holes)
|
||||||
|
priv::insert_edges(edges, offset, hole, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(edges.begin(), edges.end());
|
||||||
|
return triangulate(pts, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangulation::Changes Triangulation::create_changes(const Points &points, const Points &duplicits)
|
||||||
|
{
|
||||||
|
assert(!duplicits.empty());
|
||||||
|
assert(duplicits.size() < points.size()/2);
|
||||||
|
std::vector<uint32_t> duplicit_indices(duplicits.size(), std::numeric_limits<uint32_t>::max());
|
||||||
|
Changes changes;
|
||||||
|
changes.reserve(points.size());
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (const Point &p: points) {
|
||||||
|
auto it = std::lower_bound(duplicits.begin(), duplicits.end(), p);
|
||||||
|
if (it == duplicits.end() || *it != p) {
|
||||||
|
changes.push_back(index);
|
||||||
|
++index;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint32_t &d_index = duplicit_indices[it - duplicits.begin()];
|
||||||
|
if (d_index == std::numeric_limits<uint32_t>::max()) {
|
||||||
|
d_index = index;
|
||||||
|
changes.push_back(index);
|
||||||
|
++index;
|
||||||
|
} else {
|
||||||
|
changes.push_back(d_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
72
src/libslic3r/Triangulation.hpp
Normal file
72
src/libslic3r/Triangulation.hpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef libslic3r_Triangulation_hpp_
|
||||||
|
#define libslic3r_Triangulation_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <libslic3r/Point.hpp>
|
||||||
|
#include <libslic3r/Polygon.hpp>
|
||||||
|
#include <libslic3r/ExPolygon.hpp>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class Triangulation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Triangulation() = delete;
|
||||||
|
|
||||||
|
// define oriented connection of 2 vertices(defined by its index)
|
||||||
|
using HalfEdge = std::pair<uint32_t, uint32_t>;
|
||||||
|
using HalfEdges = std::vector<HalfEdge>;
|
||||||
|
using Indices = std::vector<Vec3i>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connect points by triangulation to create filled surface by triangles
|
||||||
|
/// Input points have to be unique
|
||||||
|
/// Inspiration for make unique points is Emboss::dilate_to_unique_points
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">Points to connect</param>
|
||||||
|
/// <param name="edges">Constraint for edges, pair is from point(first) to
|
||||||
|
/// point(second), sorted lexicographically</param>
|
||||||
|
/// <returns>Triangles</returns>
|
||||||
|
static Indices triangulate(const Points &points,
|
||||||
|
const HalfEdges &half_edges);
|
||||||
|
static Indices triangulate(const Polygon &polygon);
|
||||||
|
static Indices triangulate(const Polygons &polygons);
|
||||||
|
static Indices triangulate(const ExPolygon &expolygon);
|
||||||
|
static Indices triangulate(const ExPolygons &expolygons);
|
||||||
|
|
||||||
|
// Map for convert original index to set without duplication
|
||||||
|
// from_index<to_index>
|
||||||
|
using Changes = std::vector<uint32_t>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create conversion map from original index into new
|
||||||
|
/// with respect of duplicit point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">input set of points</param>
|
||||||
|
/// <param name="duplicits">duplicit points collected from points</param>
|
||||||
|
/// <returns>Conversion map for point index</returns>
|
||||||
|
static Changes create_changes(const Points &points, const Points &duplicits);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triangulation for expolygons, speed up when points are already collected
|
||||||
|
/// NOTE: Not working properly for ExPolygons with multiple point on same coordinate
|
||||||
|
/// You should check it by "collect_changes"
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expolygons">Input shape to triangulation - define edges</param>
|
||||||
|
/// <param name="points">Points from expolygons</param>
|
||||||
|
/// <returns>Triangle indices</returns>
|
||||||
|
static Indices triangulate(const ExPolygons &expolygons, const Points& points);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triangulation for expolygons containing multiple points with same coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expolygons">Input shape to triangulation - define edge</param>
|
||||||
|
/// <param name="points">Points from expolygons</param>
|
||||||
|
/// <param name="changes">Changes swap for indicies into points</param>
|
||||||
|
/// <returns>Triangle indices</returns>
|
||||||
|
static Indices triangulate(const ExPolygons &expolygons, const Points& points, const Changes& changes);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // libslic3r_Triangulation_hpp_
|
|
@ -237,6 +237,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER
|
||||||
}
|
}
|
||||||
|
|
||||||
extern std::string xml_escape(std::string text, bool is_marked = false);
|
extern std::string xml_escape(std::string text, bool is_marked = false);
|
||||||
|
extern std::string xml_escape_double_quotes_attribute_value(std::string text);
|
||||||
|
|
||||||
|
|
||||||
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
|
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
|
||||||
|
@ -109,9 +110,10 @@ template <typename T>
|
||||||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||||
{
|
{
|
||||||
if (dest.empty())
|
if (dest.empty())
|
||||||
dest = src;
|
dest = src; // copy
|
||||||
else
|
else
|
||||||
dest.insert(dest.end(), src.begin(), src.end());
|
dest.insert(dest.end(), src.begin(), src.end());
|
||||||
|
// NOTE: insert reserve space when needed
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -120,11 +122,14 @@ inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
||||||
if (dest.empty())
|
if (dest.empty())
|
||||||
dest = std::move(src);
|
dest = std::move(src);
|
||||||
else {
|
else {
|
||||||
dest.reserve(dest.size() + src.size());
|
dest.insert(dest.end(),
|
||||||
std::move(std::begin(src), std::end(src), std::back_inserter(dest));
|
std::make_move_iterator(src.begin()),
|
||||||
|
std::make_move_iterator(src.end()));
|
||||||
|
|
||||||
|
// Vojta wants back compatibility
|
||||||
|
src.clear();
|
||||||
|
src.shrink_to_fit();
|
||||||
}
|
}
|
||||||
src.clear();
|
|
||||||
src.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T, class... Args> // Arbitrary allocator can be used
|
template<class T, class... Args> // Arbitrary allocator can be used
|
||||||
|
@ -140,8 +145,8 @@ void clear_and_shrink(std::vector<T, Args...>& vec)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void append_reversed(std::vector<T>& dest, const std::vector<T>& src)
|
inline void append_reversed(std::vector<T>& dest, const std::vector<T>& src)
|
||||||
{
|
{
|
||||||
if (dest.empty())
|
if (dest.empty())
|
||||||
dest = src;
|
dest = {src.rbegin(), src.rend()};
|
||||||
else
|
else
|
||||||
dest.insert(dest.end(), src.rbegin(), src.rend());
|
dest.insert(dest.end(), src.rbegin(), src.rend());
|
||||||
}
|
}
|
||||||
|
@ -151,11 +156,14 @@ template <typename T>
|
||||||
inline void append_reversed(std::vector<T>& dest, std::vector<T>&& src)
|
inline void append_reversed(std::vector<T>& dest, std::vector<T>&& src)
|
||||||
{
|
{
|
||||||
if (dest.empty())
|
if (dest.empty())
|
||||||
dest = std::move(src);
|
dest = {std::make_move_iterator(src.rbegin),
|
||||||
else {
|
std::make_move_iterator(src.rend)};
|
||||||
dest.reserve(dest.size() + src.size());
|
else
|
||||||
std::move(std::rbegin(src), std::rend(src), std::back_inserter(dest));
|
dest.insert(dest.end(),
|
||||||
}
|
std::make_move_iterator(src.rbegin()),
|
||||||
|
std::make_move_iterator(src.rend()));
|
||||||
|
|
||||||
|
// Vojta wants back compatibility
|
||||||
src.clear();
|
src.clear();
|
||||||
src.shrink_to_fit();
|
src.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
@ -268,6 +276,14 @@ constexpr inline bool is_approx(Number value, Number test_value, Number precisio
|
||||||
return std::fabs(double(value) - double(test_value)) < double(precision);
|
return std::fabs(double(value) - double(test_value)) < double(precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Number>
|
||||||
|
constexpr inline bool is_approx(const std::optional<Number> &value,
|
||||||
|
const std::optional<Number> &test_value)
|
||||||
|
{
|
||||||
|
return (!value.has_value() && !test_value.has_value()) ||
|
||||||
|
(value.has_value() && test_value.has_value() && is_approx<Number>(*value, *test_value));
|
||||||
|
}
|
||||||
|
|
||||||
// A meta-predicate which is true for integers wider than or equal to coord_t
|
// A meta-predicate which is true for integers wider than or equal to coord_t
|
||||||
template<class I> struct is_scaled_coord
|
template<class I> struct is_scaled_coord
|
||||||
{
|
{
|
||||||
|
|
|
@ -944,7 +944,34 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
|
||||||
case '\'': replacement = "'"; break;
|
case '\'': replacement = "'"; break;
|
||||||
case '&': replacement = "&"; break;
|
case '&': replacement = "&"; break;
|
||||||
case '<': replacement = is_marked ? "<" :"<"; break;
|
case '<': replacement = is_marked ? "<" :"<"; break;
|
||||||
case '>': replacement = is_marked ? ">" :">"; break;
|
case '>': replacement = is_marked ? ">" : ">"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
text.replace(pos, 1, replacement);
|
||||||
|
pos += replacement.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Definition of escape symbols https://www.w3.org/TR/REC-xml/#AVNormalize
|
||||||
|
|
||||||
|
std::string xml_escape_double_quotes_attribute_value(std::string text)
|
||||||
|
{
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
for (;;) {
|
||||||
|
pos = text.find_first_of("\"&<\r\n\t", pos);
|
||||||
|
if (pos == std::string::npos) break;
|
||||||
|
|
||||||
|
std::string replacement;
|
||||||
|
switch (text[pos]) {
|
||||||
|
case '\"': replacement = """; break;
|
||||||
|
case '&': replacement = "&"; break;
|
||||||
|
case '<': replacement = "<"; break;
|
||||||
|
case '\r': replacement = "
"; break;
|
||||||
|
case '\n': replacement = "
"; break;
|
||||||
|
case '\t': replacement = "	"; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/Gizmos/GLGizmosCommon.hpp
|
GUI/Gizmos/GLGizmosCommon.hpp
|
||||||
GUI/Gizmos/GLGizmoBase.cpp
|
GUI/Gizmos/GLGizmoBase.cpp
|
||||||
GUI/Gizmos/GLGizmoBase.hpp
|
GUI/Gizmos/GLGizmoBase.hpp
|
||||||
|
GUI/Gizmos/GLGizmoEmboss.cpp
|
||||||
|
GUI/Gizmos/GLGizmoEmboss.hpp
|
||||||
GUI/Gizmos/GLGizmoMove.cpp
|
GUI/Gizmos/GLGizmoMove.cpp
|
||||||
GUI/Gizmos/GLGizmoMove.hpp
|
GUI/Gizmos/GLGizmoMove.hpp
|
||||||
GUI/Gizmos/GLGizmoRotate.cpp
|
GUI/Gizmos/GLGizmoRotate.cpp
|
||||||
|
@ -139,6 +141,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/CoordAxes.hpp
|
GUI/CoordAxes.hpp
|
||||||
GUI/Camera.cpp
|
GUI/Camera.cpp
|
||||||
GUI/Camera.hpp
|
GUI/Camera.hpp
|
||||||
|
GUI/CameraUtils.cpp
|
||||||
|
GUI/CameraUtils.hpp
|
||||||
GUI/wxExtensions.cpp
|
GUI/wxExtensions.cpp
|
||||||
GUI/wxExtensions.hpp
|
GUI/wxExtensions.hpp
|
||||||
GUI/ExtruderSequenceDialog.cpp
|
GUI/ExtruderSequenceDialog.cpp
|
||||||
|
@ -185,6 +189,12 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/Jobs/PlaterWorker.hpp
|
GUI/Jobs/PlaterWorker.hpp
|
||||||
GUI/Jobs/ArrangeJob.hpp
|
GUI/Jobs/ArrangeJob.hpp
|
||||||
GUI/Jobs/ArrangeJob.cpp
|
GUI/Jobs/ArrangeJob.cpp
|
||||||
|
GUI/Jobs/CreateFontNameImageJob.cpp
|
||||||
|
GUI/Jobs/CreateFontNameImageJob.hpp
|
||||||
|
GUI/Jobs/CreateFontStyleImagesJob.cpp
|
||||||
|
GUI/Jobs/CreateFontStyleImagesJob.hpp
|
||||||
|
GUI/Jobs/EmbossJob.cpp
|
||||||
|
GUI/Jobs/EmbossJob.hpp
|
||||||
GUI/Jobs/RotoptimizeJob.hpp
|
GUI/Jobs/RotoptimizeJob.hpp
|
||||||
GUI/Jobs/RotoptimizeJob.cpp
|
GUI/Jobs/RotoptimizeJob.cpp
|
||||||
GUI/Jobs/FillBedJob.hpp
|
GUI/Jobs/FillBedJob.hpp
|
||||||
|
@ -232,8 +242,14 @@ set(SLIC3R_GUI_SOURCES
|
||||||
Utils/OctoPrint.hpp
|
Utils/OctoPrint.hpp
|
||||||
Utils/Duet.cpp
|
Utils/Duet.cpp
|
||||||
Utils/Duet.hpp
|
Utils/Duet.hpp
|
||||||
|
Utils/EmbossStyleManager.cpp
|
||||||
|
Utils/EmbossStyleManager.hpp
|
||||||
|
Utils/EmbossStylesSerializable.cpp
|
||||||
|
Utils/EmbossStylesSerializable.hpp
|
||||||
Utils/FlashAir.cpp
|
Utils/FlashAir.cpp
|
||||||
Utils/FlashAir.hpp
|
Utils/FlashAir.hpp
|
||||||
|
Utils/FontConfigHelp.cpp
|
||||||
|
Utils/FontConfigHelp.hpp
|
||||||
Utils/AstroBox.cpp
|
Utils/AstroBox.cpp
|
||||||
Utils/AstroBox.hpp
|
Utils/AstroBox.hpp
|
||||||
Utils/Repetier.cpp
|
Utils/Repetier.cpp
|
||||||
|
@ -246,6 +262,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
Utils/PresetUpdater.hpp
|
Utils/PresetUpdater.hpp
|
||||||
Utils/Process.cpp
|
Utils/Process.cpp
|
||||||
Utils/Process.hpp
|
Utils/Process.hpp
|
||||||
|
Utils/RaycastManager.cpp
|
||||||
|
Utils/RaycastManager.hpp
|
||||||
Utils/UndoRedo.cpp
|
Utils/UndoRedo.cpp
|
||||||
Utils/UndoRedo.hpp
|
Utils/UndoRedo.hpp
|
||||||
Utils/HexFile.cpp
|
Utils/HexFile.cpp
|
||||||
|
@ -256,6 +274,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
Utils/MKS.hpp
|
Utils/MKS.hpp
|
||||||
Utils/WinRegistry.cpp
|
Utils/WinRegistry.cpp
|
||||||
Utils/WinRegistry.hpp
|
Utils/WinRegistry.hpp
|
||||||
|
Utils/WxFontUtils.cpp
|
||||||
|
Utils/WxFontUtils.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(NanoSVG REQUIRED)
|
find_package(NanoSVG REQUIRED)
|
||||||
|
@ -310,5 +330,5 @@ endif ()
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
||||||
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
|
target_include_directories(libslic3r_gui PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
|
||||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES})
|
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -324,64 +324,73 @@ void GLVolume::SinkingContours::update()
|
||||||
const int object_idx = m_parent.object_idx();
|
const int object_idx = m_parent.object_idx();
|
||||||
const Model& model = GUI::wxGetApp().plater()->model();
|
const Model& model = GUI::wxGetApp().plater()->model();
|
||||||
|
|
||||||
if (0 <= object_idx && object_idx < int(model.objects.size()) && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
|
if (object_idx < 0 ||
|
||||||
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
|
object_idx >= int(model.objects.size()) ||
|
||||||
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
|
!m_parent.is_sinking() ||
|
||||||
m_old_box = box;
|
m_parent.is_below_printbed()){
|
||||||
m_shift = Vec3d::Zero();
|
|
||||||
|
|
||||||
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
|
|
||||||
|
|
||||||
m_model.reset();
|
|
||||||
GUI::GLModel::Geometry init_data;
|
|
||||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
|
||||||
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 };
|
|
||||||
init_data.color = ColorRGBA::WHITE();
|
|
||||||
unsigned int vertices_counter = 0;
|
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
|
||||||
MeshSlicingParams slicing_params;
|
|
||||||
slicing_params.trafo = m_parent.world_matrix();
|
|
||||||
const Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
|
|
||||||
for (const ExPolygon& expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
|
|
||||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
|
||||||
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
|
|
||||||
init_data.reserve_vertices(init_data.vertices_count() + triangulation.size());
|
|
||||||
init_data.reserve_indices(init_data.indices_count() + triangulation.size());
|
|
||||||
for (const Vec3d& v : triangulation) {
|
|
||||||
init_data.add_vertex((Vec3f)(v.cast<float>() + 0.015f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting
|
|
||||||
++vertices_counter;
|
|
||||||
if (vertices_counter % 3 == 0)
|
|
||||||
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_model.init_from(std::move(init_data));
|
|
||||||
#else
|
|
||||||
GUI::GLModel::Geometry::Entity entity;
|
|
||||||
entity.type = GUI::GLModel::EPrimitiveType::Triangles;
|
|
||||||
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
|
|
||||||
entity.positions.reserve(entity.positions.size() + triangulation.size());
|
|
||||||
entity.normals.reserve(entity.normals.size() + triangulation.size());
|
|
||||||
entity.indices.reserve(entity.indices.size() + triangulation.size() / 3);
|
|
||||||
for (const Vec3d& v : triangulation) {
|
|
||||||
entity.positions.emplace_back(v.cast<float>() + 0.015f * Vec3f::UnitZ()); // add a small positive z to avoid z-fighting
|
|
||||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
|
||||||
const size_t positions_count = entity.positions.size();
|
|
||||||
if (positions_count % 3 == 0) {
|
|
||||||
entity.indices.emplace_back(positions_count - 3);
|
|
||||||
entity.indices.emplace_back(positions_count - 2);
|
|
||||||
entity.indices.emplace_back(positions_count - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init_data.entities.emplace_back(entity);
|
|
||||||
}
|
|
||||||
m_model.init_from(init_data);
|
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_shift = box.center() - m_old_box.center();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_model.reset();
|
m_model.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
|
||||||
|
if (m_old_box.size().isApprox(box.size()) &&
|
||||||
|
m_old_box.min.z() == box.min.z()){
|
||||||
|
// Fix it !!! It is not working all the time
|
||||||
|
m_shift = box.center() - m_old_box.center();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_old_box = box;
|
||||||
|
m_shift = Vec3d::Zero();
|
||||||
|
|
||||||
|
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
|
||||||
|
|
||||||
|
m_model.reset();
|
||||||
|
GUI::GLModel::Geometry init_data;
|
||||||
|
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 };
|
||||||
|
init_data.color = ColorRGBA::WHITE();
|
||||||
|
unsigned int vertices_counter = 0;
|
||||||
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
MeshSlicingParams slicing_params;
|
||||||
|
slicing_params.trafo = m_parent.world_matrix();
|
||||||
|
const Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
|
||||||
|
if (polygons.empty()) return;
|
||||||
|
|
||||||
|
for (const ExPolygon& expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
|
||||||
|
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
|
||||||
|
init_data.reserve_vertices(init_data.vertices_count() + triangulation.size());
|
||||||
|
init_data.reserve_indices(init_data.indices_count() + triangulation.size());
|
||||||
|
for (const Vec3d& v : triangulation) {
|
||||||
|
init_data.add_vertex((Vec3f)(v.cast<float>() + 0.015f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting
|
||||||
|
++vertices_counter;
|
||||||
|
if (vertices_counter % 3 == 0)
|
||||||
|
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_model.init_from(std::move(init_data));
|
||||||
|
#else
|
||||||
|
GUI::GLModel::Geometry::Entity entity;
|
||||||
|
entity.type = GUI::GLModel::EPrimitiveType::Triangles;
|
||||||
|
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
|
||||||
|
entity.positions.reserve(entity.positions.size() + triangulation.size());
|
||||||
|
entity.normals.reserve(entity.normals.size() + triangulation.size());
|
||||||
|
entity.indices.reserve(entity.indices.size() + triangulation.size() / 3);
|
||||||
|
for (const Vec3d& v : triangulation) {
|
||||||
|
entity.positions.emplace_back(v.cast<float>() + 0.015f * Vec3f::UnitZ()); // add a small positive z to avoid z-fighting
|
||||||
|
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||||
|
const size_t positions_count = entity.positions.size();
|
||||||
|
if (positions_count % 3 == 0) {
|
||||||
|
entity.indices.emplace_back(positions_count - 3);
|
||||||
|
entity.indices.emplace_back(positions_count - 2);
|
||||||
|
entity.indices.emplace_back(positions_count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init_data.entities.emplace_back(entity);
|
||||||
|
}
|
||||||
|
m_model.init_from(init_data);
|
||||||
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLVolume::NonManifoldEdges::render()
|
void GLVolume::NonManifoldEdges::render()
|
||||||
|
@ -1291,9 +1300,15 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||||
#endif // ENABLE_GL_CORE_PROFILE
|
#endif // ENABLE_GL_CORE_PROFILE
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
|
||||||
|
ScopeGuard transparent_sg;
|
||||||
if (type == ERenderType::Transparent) {
|
if (type == ERenderType::Transparent) {
|
||||||
glsafe(::glEnable(GL_BLEND));
|
glsafe(::glEnable(GL_BLEND));
|
||||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||||
|
glsafe(::glDepthMask(false));
|
||||||
|
transparent_sg = ScopeGuard([]() {
|
||||||
|
glsafe(::glDisable(GL_BLEND));
|
||||||
|
glsafe(::glDepthMask(true));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
glsafe(::glCullFace(GL_BACK));
|
glsafe(::glCullFace(GL_BACK));
|
||||||
|
@ -1421,9 +1436,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||||
|
|
||||||
if (disable_cullface)
|
if (disable_cullface)
|
||||||
glsafe(::glEnable(GL_CULL_FACE));
|
glsafe(::glEnable(GL_CULL_FACE));
|
||||||
|
|
||||||
if (type == ERenderType::Transparent)
|
|
||||||
glsafe(::glDisable(GL_BLEND));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
||||||
|
|
123
src/slic3r/GUI/CameraUtils.cpp
Normal file
123
src/slic3r/GUI/CameraUtils.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include "CameraUtils.hpp"
|
||||||
|
#include <igl/project.h> // projecting points
|
||||||
|
|
||||||
|
#include "slic3r/GUI/3DScene.hpp" // GLVolume
|
||||||
|
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
using namespace GUI;
|
||||||
|
|
||||||
|
Points CameraUtils::project(const Camera & camera,
|
||||||
|
const std::vector<Vec3d> &points)
|
||||||
|
{
|
||||||
|
Vec4i viewport(camera.get_viewport().data());
|
||||||
|
|
||||||
|
// Convert our std::vector to Eigen dynamic matrix.
|
||||||
|
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign>
|
||||||
|
pts(points.size(), 3);
|
||||||
|
for (size_t i = 0; i < points.size(); ++i)
|
||||||
|
pts.block<1, 3>(i, 0) = points[i];
|
||||||
|
|
||||||
|
// Get the projections.
|
||||||
|
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections;
|
||||||
|
igl::project(pts, camera.get_view_matrix().matrix(),
|
||||||
|
camera.get_projection_matrix().matrix(), viewport, projections);
|
||||||
|
|
||||||
|
Points result;
|
||||||
|
result.reserve(points.size());
|
||||||
|
int window_height = viewport[3];
|
||||||
|
|
||||||
|
// convert to points --> loss precision
|
||||||
|
for (int i = 0; i < projections.rows(); ++i) {
|
||||||
|
double x = projections(i, 0);
|
||||||
|
double y = projections(i, 1);
|
||||||
|
// opposit direction o Y
|
||||||
|
result.emplace_back(x, window_height - y);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point CameraUtils::project(const Camera &camera, const Vec3d &point)
|
||||||
|
{
|
||||||
|
// IMPROVE: do it faster when you need it (inspire in project multi point)
|
||||||
|
return project(camera, std::vector{point}).front();
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera,
|
||||||
|
const GLVolume &volume)
|
||||||
|
{
|
||||||
|
std::vector<Vec3d> vertices;
|
||||||
|
const TriangleMesh *hull = volume.convex_hull();
|
||||||
|
if (hull != nullptr) {
|
||||||
|
const indexed_triangle_set &its = hull->its;
|
||||||
|
vertices.reserve(its.vertices.size());
|
||||||
|
// cast vector
|
||||||
|
for (const Vec3f &vertex : its.vertices)
|
||||||
|
vertices.emplace_back(vertex.cast<double>());
|
||||||
|
} else {
|
||||||
|
// Negative volume doesn't have convex hull so use bounding box
|
||||||
|
auto bb = volume.bounding_box();
|
||||||
|
Vec3d &min = bb.min;
|
||||||
|
Vec3d &max = bb.max;
|
||||||
|
vertices = {min,
|
||||||
|
Vec3d(min.x(), min.y(), max.z()),
|
||||||
|
Vec3d(min.x(), max.y(), min.z()),
|
||||||
|
Vec3d(min.x(), max.y(), max.z()),
|
||||||
|
Vec3d(max.x(), min.y(), min.z()),
|
||||||
|
Vec3d(max.x(), min.y(), max.z()),
|
||||||
|
Vec3d(max.x(), max.y(), min.z()),
|
||||||
|
max};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Transform3d &trafoMat =
|
||||||
|
volume.get_instance_transformation().get_matrix() *
|
||||||
|
volume.get_volume_transformation().get_matrix();
|
||||||
|
for (Vec3d &vertex : vertices)
|
||||||
|
vertex = trafoMat * vertex.cast<double>();
|
||||||
|
|
||||||
|
Points vertices_2d = project(camera, vertices);
|
||||||
|
return Geometry::convex_hull(vertices_2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <igl/unproject.h>
|
||||||
|
Vec3d CameraUtils::create_ray(const Camera &camera, const Vec2d &coor) {
|
||||||
|
if (camera.get_type() == Camera::EType::Ortho)
|
||||||
|
return camera.get_dir_forward();
|
||||||
|
// check that it is known camera no other tha ORTHO or Persepective
|
||||||
|
assert(camera.get_type() == Camera::EType::Perspective);
|
||||||
|
|
||||||
|
Matrix4d modelview = camera.get_view_matrix().matrix();
|
||||||
|
Matrix4d projection = camera.get_projection_matrix().matrix();
|
||||||
|
Vec4i viewport(camera.get_viewport().data());
|
||||||
|
|
||||||
|
Vec3d scene_point(coor.x(), viewport[3] - coor.y(), 0.);
|
||||||
|
Vec3d unprojected_point;
|
||||||
|
igl::unproject(scene_point, modelview, projection, viewport, unprojected_point);
|
||||||
|
|
||||||
|
Vec3d p0 = camera.get_position();
|
||||||
|
Vec3d dir = unprojected_point - p0;
|
||||||
|
dir.normalize();
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor)
|
||||||
|
{
|
||||||
|
Vec3d dir = CameraUtils::create_ray(camera, coor);
|
||||||
|
Vec3d p0 = camera.get_position();
|
||||||
|
if (camera.get_type() == Camera::EType::Ortho) {
|
||||||
|
Matrix4d modelview = camera.get_view_matrix().matrix();
|
||||||
|
Matrix4d projection = camera.get_projection_matrix().matrix();
|
||||||
|
Vec4i viewport(camera.get_viewport().data());
|
||||||
|
igl::unproject(Vec3d(coor.x(), viewport[3] - coor.y(), 0.), modelview, projection, viewport, p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is approx zero
|
||||||
|
if ((fabs(dir.z()) - 1e-4) < 0)
|
||||||
|
return Vec2d(std::numeric_limits<double>::max(),
|
||||||
|
std::numeric_limits<double>::max());
|
||||||
|
|
||||||
|
// find position of ray cross plane(z = 0)
|
||||||
|
double t = p0.z() / dir.z();
|
||||||
|
Vec3d p = p0 - t * dir;
|
||||||
|
return Vec2d(p.x(), p.y());
|
||||||
|
}
|
58
src/slic3r/GUI/CameraUtils.hpp
Normal file
58
src/slic3r/GUI/CameraUtils.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef slic3r_CameraUtils_hpp_
|
||||||
|
#define slic3r_CameraUtils_hpp_
|
||||||
|
|
||||||
|
#include "Camera.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
namespace Slic3r {
|
||||||
|
class GLVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Slic3r::GUI {
|
||||||
|
/// <summary>
|
||||||
|
/// Help divide camera data and camera functions
|
||||||
|
/// This utility work with camera data by static funtions
|
||||||
|
/// </summary>
|
||||||
|
class CameraUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CameraUtils() = delete; // only static functions
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Project point throw camera to 2d coordinate into imgui window
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="camera">Projection params</param>
|
||||||
|
/// <param name="points">Point to project.</param>
|
||||||
|
/// <returns>projected points by camera into coordinate of camera.
|
||||||
|
/// x(from left to right), y(from top to bottom)</returns>
|
||||||
|
static Points project(const Camera& camera, const std::vector<Vec3d> &points);
|
||||||
|
static Point project(const Camera& camera, const Vec3d &point);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create hull around GLVolume in 2d space of camera
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="camera">Projection params</param>
|
||||||
|
/// <param name="volume">Outline by 3d object</param>
|
||||||
|
/// <returns>Polygon around object</returns>
|
||||||
|
static Polygon create_hull2d(const Camera &camera, const GLVolume &volume);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unproject screen coordinate to scene direction start from camera position
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="camera">Projection params</param>
|
||||||
|
/// <param name="coor">Coordinate on screen</param>
|
||||||
|
/// <returns>Scene direction</returns>
|
||||||
|
static Vec3d create_ray(const Camera &camera, const Vec2d &coor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unproject mouse coordinate to get position in space where z coor is zero
|
||||||
|
/// Platter surface should be in z == 0
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="camera">Projection params</param>
|
||||||
|
/// <param name="coor">Mouse position</param>
|
||||||
|
/// <returns>Position on platter under mouse</returns>
|
||||||
|
static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor);
|
||||||
|
|
||||||
|
};
|
||||||
|
} // Slic3r::GUI
|
||||||
|
|
||||||
|
#endif /* slic3r_CameraUtils_hpp_ */
|
|
@ -3727,6 +3727,24 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
else
|
else
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
|
|
||||||
|
// Detection of doubleclick on text to open emboss edit window
|
||||||
|
if (evt.LeftDClick() && m_gizmos.get_current() == nullptr && !m_hover_volume_idxs.empty()) {
|
||||||
|
for (int hover_volume_id : m_hover_volume_idxs) {
|
||||||
|
const GLVolume &hover_gl_volume = *m_volumes.volumes[hover_volume_id];
|
||||||
|
const ModelObject* hover_object = m_model->objects[hover_gl_volume.object_idx()];
|
||||||
|
int hover_volume_idx = hover_gl_volume.volume_idx();
|
||||||
|
const ModelVolume* hover_volume = hover_object->volumes[hover_volume_idx];
|
||||||
|
if (hover_volume->text_configuration.has_value()) {
|
||||||
|
//m_selection.set_mode(Selection::EMode::Volume);
|
||||||
|
//m_selection.add(hover_volume_id); // add whole instance
|
||||||
|
m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id});
|
||||||
|
m_gizmos.open_gizmo(GLGizmosManager::EType::Emboss);
|
||||||
|
wxGetApp().obj_list()->update_selections();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_moving)
|
if (m_moving)
|
||||||
show_sinking_contours();
|
show_sinking_contours();
|
||||||
|
|
||||||
|
@ -4382,6 +4400,14 @@ bool GLCanvas3D::is_object_sinking(int object_idx) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::apply_retina_scale(Vec2d &screen_coordinate) const
|
||||||
|
{
|
||||||
|
#if ENABLE_RETINA_GL
|
||||||
|
double scale = static_cast<double>(m_retina_helper->get_scale_factor());
|
||||||
|
screen_coordinate *= scale;
|
||||||
|
#endif // ENABLE_RETINA_GL
|
||||||
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_shown_on_screen() const
|
bool GLCanvas3D::_is_shown_on_screen() const
|
||||||
{
|
{
|
||||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||||
|
@ -7246,7 +7272,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||||
_3DScene::extrusionentity_to_verts(layerm->perimeters(), float(layer->print_z), copy,
|
_3DScene::extrusionentity_to_verts(layerm->perimeters(), float(layer->print_z), copy,
|
||||||
select_geometry(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
|
select_geometry(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
|
||||||
#else
|
#else
|
||||||
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
|
_3DScene::extrusionentity_to_verts(layerm->perimeters(), float(layer->print_z), copy,
|
||||||
volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
|
volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
if (ctxt.has_infill) {
|
if (ctxt.has_infill) {
|
||||||
|
|
|
@ -830,6 +830,12 @@ public:
|
||||||
Size get_canvas_size() const;
|
Size get_canvas_size() const;
|
||||||
Vec2d get_local_mouse_position() const;
|
Vec2d get_local_mouse_position() const;
|
||||||
|
|
||||||
|
// store opening position of menu
|
||||||
|
std::optional<Vec2d> m_popup_menu_positon; // position of mouse right click
|
||||||
|
void set_popup_menu_position(const Vec2d &position) { m_popup_menu_positon = position; }
|
||||||
|
const std::optional<Vec2d>& get_popup_menu_position() const { return m_popup_menu_positon; }
|
||||||
|
void clear_popup_menu_position() { m_popup_menu_positon.reset(); }
|
||||||
|
|
||||||
void set_tooltip(const std::string& tooltip);
|
void set_tooltip(const std::string& tooltip);
|
||||||
|
|
||||||
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
|
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
|
||||||
|
@ -961,6 +967,8 @@ public:
|
||||||
|
|
||||||
bool is_object_sinking(int object_idx) const;
|
bool is_object_sinking(int object_idx) const;
|
||||||
|
|
||||||
|
void apply_retina_scale(Vec2d &screen_coordinate) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "GLSelectionRectangle.hpp"
|
#include "GLSelectionRectangle.hpp"
|
||||||
#include "Camera.hpp"
|
#include "Camera.hpp"
|
||||||
|
#include "CameraUtils.hpp"
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "GLCanvas3D.hpp"
|
#include "GLCanvas3D.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
|
@ -44,30 +45,15 @@ namespace GUI {
|
||||||
m_state = EState::Off;
|
m_state = EState::Off;
|
||||||
#endif // !ENABLE_RAYCAST_PICKING
|
#endif // !ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
|
||||||
const Matrix4d modelview = camera.get_view_matrix().matrix();
|
|
||||||
const Matrix4d projection= camera.get_projection_matrix().matrix();
|
|
||||||
const Vec4i viewport(camera.get_viewport().data());
|
|
||||||
|
|
||||||
// Convert our std::vector to Eigen dynamic matrix.
|
|
||||||
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> pts(points.size(), 3);
|
|
||||||
for (size_t i=0; i<points.size(); ++i)
|
|
||||||
pts.block<1, 3>(i, 0) = points[i];
|
|
||||||
|
|
||||||
// Get the projections.
|
|
||||||
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections;
|
|
||||||
igl::project(pts, modelview, projection, viewport, projections);
|
|
||||||
|
|
||||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||||
const BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
|
const BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
|
||||||
|
|
||||||
// Iterate over all points and determine whether they're in the rectangle.
|
// Iterate over all points and determine whether they're in the rectangle.
|
||||||
for (int i = 0; i<projections.rows(); ++i)
|
const Camera &camera = wxGetApp().plater()->get_camera();
|
||||||
#if ENABLE_RAYCAST_PICKING
|
Points points_2d = CameraUtils::project(camera, points);
|
||||||
if (rectangle.contains(Point(projections(i, 0), viewport[3] - projections(i, 1))))
|
unsigned int size = static_cast<unsigned int>(points.size());
|
||||||
#else
|
for (unsigned int i = 0; i< size; ++i)
|
||||||
if (rectangle.contains(Point(projections(i, 0), canvas.get_canvas_size().get_height() - projections(i, 1))))
|
if (rectangle.contains(points_2d[i]))
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
|
||||||
out.push_back(i);
|
out.push_back(i);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -469,6 +469,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
|
||||||
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } },
|
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } },
|
||||||
/* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv, ".step"sv, ".stp"sv } },
|
/* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv, ".step"sv, ".stp"sv } },
|
||||||
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
|
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
|
||||||
|
/* FT_FONTS */ { "Font files"sv, { ".ttc"sv, ".ttf"sv } },
|
||||||
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
|
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
|
||||||
|
|
||||||
/* FT_INI */ { "INI files"sv, { ".ini"sv } },
|
/* FT_INI */ { "INI files"sv, { ".ini"sv } },
|
||||||
|
|
|
@ -62,6 +62,7 @@ enum FileType
|
||||||
FT_GCODE,
|
FT_GCODE,
|
||||||
FT_MODEL,
|
FT_MODEL,
|
||||||
FT_PROJECT,
|
FT_PROJECT,
|
||||||
|
FT_FONTS,
|
||||||
FT_GALLERY,
|
FT_GALLERY,
|
||||||
|
|
||||||
FT_INI,
|
FT_INI,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "GLCanvas3D.hpp"
|
#include "GLCanvas3D.hpp"
|
||||||
#include "Selection.hpp"
|
#include "Selection.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
|
#include "Gizmos/GLGizmoEmboss.hpp"
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||||
|
@ -163,6 +164,14 @@ const std::vector<std::pair<std::string, std::string>> MenuFactory::ADD_VOLUME_M
|
||||||
{L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER
|
{L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
|
||||||
|
const std::vector<std::pair<std::string, std::string>> MenuFactory::TEXT_VOLUME_ICONS {
|
||||||
|
// menu_item Name menu_item bitmap name
|
||||||
|
{L("Add text"), "add_text_part"}, // ~ModelVolumeType::MODEL_PART
|
||||||
|
{L("Add negative text"), "add_text_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME
|
||||||
|
{L("Add text modifier"), "add_text_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||||
|
};
|
||||||
|
|
||||||
static Plater* plater()
|
static Plater* plater()
|
||||||
{
|
{
|
||||||
return wxGetApp().plater();
|
return wxGetApp().plater();
|
||||||
|
@ -438,6 +447,15 @@ std::vector<wxBitmapBundle*> MenuFactory::get_volume_bitmaps()
|
||||||
return volume_bmps;
|
return volume_bmps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<wxBitmapBundle*> MenuFactory::get_text_volume_bitmaps()
|
||||||
|
{
|
||||||
|
std::vector<wxBitmapBundle*> volume_bmps;
|
||||||
|
volume_bmps.reserve(TEXT_VOLUME_ICONS.size());
|
||||||
|
for (auto item : TEXT_VOLUME_ICONS)
|
||||||
|
volume_bmps.push_back(get_bmp_bundle(item.second));
|
||||||
|
return volume_bmps;
|
||||||
|
}
|
||||||
|
|
||||||
void MenuFactory::append_menu_item_delete(wxMenu* menu)
|
void MenuFactory::append_menu_item_delete(wxMenu* menu)
|
||||||
{
|
{
|
||||||
append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
|
append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
|
||||||
|
@ -451,21 +469,27 @@ void MenuFactory::append_menu_item_delete(wxMenu* menu)
|
||||||
wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) {
|
wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType type) {
|
||||||
auto sub_menu = new wxMenu;
|
auto sub_menu = new wxMenu;
|
||||||
|
|
||||||
if (wxGetApp().get_mode() == comExpert && type != ModelVolumeType::INVALID) {
|
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||||
|
|
||||||
|
if (mode == comExpert && type != ModelVolumeType::INVALID ||
|
||||||
|
mode == comAdvanced && type == ModelVolumeType::MODEL_PART) {
|
||||||
append_menu_item(sub_menu, wxID_ANY, _L("Load") + " " + dots, "",
|
append_menu_item(sub_menu, wxID_ANY, _L("Load") + " " + dots, "",
|
||||||
[type](wxCommandEvent&) { obj_list()->load_subobject(type); }, "", menu);
|
[type](wxCommandEvent&) { obj_list()->load_subobject(type); }, "", menu);
|
||||||
sub_menu->AppendSeparator();
|
sub_menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
|
if (!(type == ModelVolumeType::MODEL_PART && mode == comAdvanced))
|
||||||
{
|
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") })
|
||||||
if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0)
|
{
|
||||||
continue;
|
if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0)
|
||||||
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
continue;
|
||||||
[type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu);
|
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
||||||
}
|
[type, item](wxCommandEvent&) { obj_list()->load_generic_subobject(item, type); }, "", menu);
|
||||||
|
}
|
||||||
|
|
||||||
if (wxGetApp().get_mode() >= comAdvanced) {
|
append_menu_item_add_text(sub_menu, type);
|
||||||
|
|
||||||
|
if (mode >= comAdvanced) {
|
||||||
sub_menu->AppendSeparator();
|
sub_menu->AppendSeparator();
|
||||||
append_menu_item(sub_menu, wxID_ANY, _L("Gallery"), "",
|
append_menu_item(sub_menu, wxID_ANY, _L("Gallery"), "",
|
||||||
[type](wxCommandEvent&) { obj_list()->load_subobject(type, true); }, "", menu);
|
[type](wxCommandEvent&) { obj_list()->load_subobject(type, true); }, "", menu);
|
||||||
|
@ -474,30 +498,66 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
|
||||||
return sub_menu;
|
return sub_menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MenuFactory::append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item/* = true*/)
|
||||||
|
{
|
||||||
|
auto add_text = [type](wxCommandEvent& evt) {
|
||||||
|
GLCanvas3D* canvas = plater()->canvas3D();
|
||||||
|
GLGizmosManager& mng = canvas->get_gizmos_manager();
|
||||||
|
GLGizmoBase* gizmo = mng.get_gizmo(GLGizmosManager::Emboss);
|
||||||
|
GLGizmoEmboss* emboss = dynamic_cast<GLGizmoEmboss *>(gizmo);
|
||||||
|
assert(emboss != nullptr);
|
||||||
|
if (emboss == nullptr) return;
|
||||||
|
|
||||||
|
ModelVolumeType volume_type = type;
|
||||||
|
// no selected object means create new object
|
||||||
|
if (volume_type == ModelVolumeType::INVALID)
|
||||||
|
volume_type = ModelVolumeType::MODEL_PART;
|
||||||
|
|
||||||
|
auto screen_position = canvas->get_popup_menu_position();
|
||||||
|
if (screen_position.has_value()) {
|
||||||
|
emboss->create_volume(volume_type, *screen_position);
|
||||||
|
} else {
|
||||||
|
emboss->create_volume(volume_type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( type == ModelVolumeType::MODEL_PART
|
||||||
|
|| type == ModelVolumeType::NEGATIVE_VOLUME
|
||||||
|
|| type == ModelVolumeType::PARAMETER_MODIFIER
|
||||||
|
|| type == ModelVolumeType::INVALID // cannot use gizmo without selected object
|
||||||
|
) {
|
||||||
|
wxString item_name = is_submenu_item ? "" : _(ADD_VOLUME_MENU_ITEMS[int(type)].first) + ": ";
|
||||||
|
item_name += _L("Text");
|
||||||
|
const std::string icon_name = is_submenu_item ? "" : ADD_VOLUME_MENU_ITEMS[int(type)].second;
|
||||||
|
append_menu_item(menu, wxID_ANY, item_name, "", add_text, icon_name, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
||||||
{
|
{
|
||||||
// Update "add" items(delete old & create new) settings popupmenu
|
// Update "add" items(delete old & create new) items popupmenu
|
||||||
for (auto& item : ADD_VOLUME_MENU_ITEMS) {
|
for (auto& item : ADD_VOLUME_MENU_ITEMS) {
|
||||||
const auto settings_id = menu->FindItem(_(item.first));
|
const wxString item_name = _(item.first);
|
||||||
if (settings_id != wxNOT_FOUND)
|
int item_id = menu->FindItem(item_name);
|
||||||
menu->Destroy(settings_id);
|
if (item_id != wxNOT_FOUND)
|
||||||
|
menu->Destroy(item_id);
|
||||||
|
|
||||||
|
item_id = menu->FindItem(item_name + ": " + _L("Text"));
|
||||||
|
if (item_id != wxNOT_FOUND)
|
||||||
|
menu->Destroy(item_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update "Height range Modifier" item (delete old & create new)
|
// Update "Height range Modifier" item (delete old & create new)
|
||||||
if (const auto range_id = menu->FindItem(_L("Height range Modifier")); range_id != wxNOT_FOUND)
|
if (const auto range_id = menu->FindItem(_L("Height range Modifier")); range_id != wxNOT_FOUND)
|
||||||
menu->Destroy(range_id);
|
menu->Destroy(range_id);
|
||||||
|
|
||||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
if (const auto range_id = menu->FindItem(_L("Height range Modifier")); range_id != wxNOT_FOUND)
|
||||||
|
menu->Destroy(range_id);
|
||||||
|
|
||||||
|
if (wxGetApp().get_mode() == comSimple) {
|
||||||
|
append_menu_item_add_text(menu, ModelVolumeType::MODEL_PART, false);
|
||||||
|
append_menu_item_add_text(menu, ModelVolumeType::NEGATIVE_VOLUME, false);
|
||||||
|
|
||||||
if (mode == comAdvanced) {
|
|
||||||
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "",
|
|
||||||
[](wxCommandEvent&) { obj_list()->load_subobject(ModelVolumeType::MODEL_PART); },
|
|
||||||
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr,
|
|
||||||
[]() { return obj_list()->is_instance_or_object_selected()
|
|
||||||
&& !obj_list()->is_selected_object_cut();
|
|
||||||
}, m_parent);
|
|
||||||
}
|
|
||||||
if (mode == comSimple) {
|
|
||||||
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "",
|
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "",
|
||||||
[](wxCommandEvent&) { obj_list()->load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); },
|
[](wxCommandEvent&) { obj_list()->load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); },
|
||||||
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second, nullptr,
|
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second, nullptr,
|
||||||
|
@ -510,11 +570,9 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t type = (mode == comExpert ? 0 : 1); type < ADD_VOLUME_MENU_ITEMS.size(); type++)
|
int type = 0;
|
||||||
{
|
for (auto& item : ADD_VOLUME_MENU_ITEMS) {
|
||||||
auto& item = ADD_VOLUME_MENU_ITEMS[type];
|
wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type++));
|
||||||
|
|
||||||
wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type));
|
|
||||||
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second,
|
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second,
|
||||||
[type]() {
|
[type]() {
|
||||||
bool can_add = type < size_t(ModelVolumeType::PARAMETER_MODIFIER) ? !obj_list()->is_selected_object_cut() : true;
|
bool can_add = type < size_t(ModelVolumeType::PARAMETER_MODIFIER) ? !obj_list()->is_selected_object_cut() : true;
|
||||||
|
@ -907,6 +965,37 @@ void MenuFactory::append_menu_items_mirror(wxMenu* menu)
|
||||||
[]() { return plater()->can_mirror(); }, m_parent);
|
[]() { return plater()->can_mirror(); }, m_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||||
|
{
|
||||||
|
wxString name = _L("Edit text");
|
||||||
|
|
||||||
|
auto can_edit_text = []() {
|
||||||
|
const auto& sel = plater()->get_selection();
|
||||||
|
if (sel.volumes_count() != 1) return false;
|
||||||
|
auto cid = sel.get_volume(*sel.get_volume_idxs().begin());
|
||||||
|
const ModelVolume* vol = plater()->canvas3D()->get_model()
|
||||||
|
->objects[cid->object_idx()]->volumes[cid->volume_idx()];
|
||||||
|
return vol->text_configuration.has_value();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (menu == &m_object_menu) {
|
||||||
|
auto menu_item_id = menu->FindItem(name);
|
||||||
|
if (menu_item_id != wxNOT_FOUND)
|
||||||
|
menu->Destroy(menu_item_id);
|
||||||
|
if (!can_edit_text())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString description = _L("Ability to change text, font, size, ...");
|
||||||
|
std::string icon = "";
|
||||||
|
append_menu_item(
|
||||||
|
menu, wxID_ANY, name, description,
|
||||||
|
[can_edit_text](wxCommandEvent &) {
|
||||||
|
plater()->canvas3D()->get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss);
|
||||||
|
},
|
||||||
|
icon, nullptr, can_edit_text, m_parent);
|
||||||
|
}
|
||||||
|
|
||||||
MenuFactory::MenuFactory()
|
MenuFactory::MenuFactory()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < mtCount; i++) {
|
for (int i = 0; i < mtCount; i++) {
|
||||||
|
@ -979,6 +1068,20 @@ void MenuFactory::create_sla_object_menu()
|
||||||
m_sla_object_menu.AppendSeparator();
|
m_sla_object_menu.AppendSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MenuFactory::append_immutable_part_menu_items(wxMenu* menu)
|
||||||
|
{
|
||||||
|
append_menu_items_mirror(menu);
|
||||||
|
|
||||||
|
menu->AppendSeparator();
|
||||||
|
append_menu_item_change_type(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuFactory::append_mutable_part_menu_items(wxMenu* menu)
|
||||||
|
{
|
||||||
|
append_menu_item_settings(menu);
|
||||||
|
append_menu_item_change_extruder(menu);
|
||||||
|
}
|
||||||
|
|
||||||
void MenuFactory::create_part_menu()
|
void MenuFactory::create_part_menu()
|
||||||
{
|
{
|
||||||
wxMenu* menu = &m_part_menu;
|
wxMenu* menu = &m_part_menu;
|
||||||
|
@ -991,15 +1094,24 @@ void MenuFactory::create_part_menu()
|
||||||
append_menu_item_export_stl(menu);
|
append_menu_item_export_stl(menu);
|
||||||
append_menu_item_fix_through_netfabb(menu);
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
append_menu_item_simplify(menu);
|
append_menu_item_simplify(menu);
|
||||||
append_menu_items_mirror(menu);
|
|
||||||
|
|
||||||
append_menu_item(menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual parts"),
|
append_menu_item(menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual parts"),
|
||||||
[](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", nullptr,
|
[](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", nullptr,
|
||||||
[]() { return plater()->can_split(false); }, m_parent);
|
[]() { return plater()->can_split(false); }, m_parent);
|
||||||
|
|
||||||
menu->AppendSeparator();
|
append_immutable_part_menu_items(menu);
|
||||||
append_menu_item_change_type(menu);
|
}
|
||||||
|
|
||||||
|
void MenuFactory::create_text_part_menu()
|
||||||
|
{
|
||||||
|
wxMenu* menu = &m_text_part_menu;
|
||||||
|
|
||||||
|
append_menu_item_delete(menu);
|
||||||
|
append_menu_item_edit_text(menu);
|
||||||
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
|
append_menu_item_simplify(menu);
|
||||||
|
|
||||||
|
append_immutable_part_menu_items(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuFactory::create_instance_menu()
|
void MenuFactory::create_instance_menu()
|
||||||
|
@ -1018,6 +1130,7 @@ void MenuFactory::init(wxWindow* parent)
|
||||||
create_object_menu();
|
create_object_menu();
|
||||||
create_sla_object_menu();
|
create_sla_object_menu();
|
||||||
create_part_menu();
|
create_part_menu();
|
||||||
|
create_text_part_menu();
|
||||||
create_instance_menu();
|
create_instance_menu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1039,6 +1152,7 @@ wxMenu* MenuFactory::object_menu()
|
||||||
append_menu_item_change_extruder(&m_object_menu);
|
append_menu_item_change_extruder(&m_object_menu);
|
||||||
update_menu_items_instance_manipulation(mtObjectFFF);
|
update_menu_items_instance_manipulation(mtObjectFFF);
|
||||||
append_menu_item_invalidate_cut_info(&m_object_menu);
|
append_menu_item_invalidate_cut_info(&m_object_menu);
|
||||||
|
append_menu_item_edit_text(&m_object_menu);
|
||||||
|
|
||||||
return &m_object_menu;
|
return &m_object_menu;
|
||||||
}
|
}
|
||||||
|
@ -1049,6 +1163,7 @@ wxMenu* MenuFactory::sla_object_menu()
|
||||||
append_menu_item_settings(&m_sla_object_menu);
|
append_menu_item_settings(&m_sla_object_menu);
|
||||||
update_menu_items_instance_manipulation(mtObjectSLA);
|
update_menu_items_instance_manipulation(mtObjectSLA);
|
||||||
append_menu_item_invalidate_cut_info(&m_sla_object_menu);
|
append_menu_item_invalidate_cut_info(&m_sla_object_menu);
|
||||||
|
append_menu_item_edit_text(&m_sla_object_menu);
|
||||||
|
|
||||||
return &m_sla_object_menu;
|
return &m_sla_object_menu;
|
||||||
}
|
}
|
||||||
|
@ -1056,12 +1171,19 @@ wxMenu* MenuFactory::sla_object_menu()
|
||||||
wxMenu* MenuFactory::part_menu()
|
wxMenu* MenuFactory::part_menu()
|
||||||
{
|
{
|
||||||
append_menu_items_convert_unit(&m_part_menu, 2);
|
append_menu_items_convert_unit(&m_part_menu, 2);
|
||||||
append_menu_item_settings(&m_part_menu);
|
|
||||||
append_menu_item_change_extruder(&m_part_menu);
|
append_mutable_part_menu_items(&m_part_menu);
|
||||||
|
|
||||||
return &m_part_menu;
|
return &m_part_menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxMenu* MenuFactory::text_part_menu()
|
||||||
|
{
|
||||||
|
append_mutable_part_menu_items(&m_text_part_menu);
|
||||||
|
|
||||||
|
return &m_text_part_menu;
|
||||||
|
}
|
||||||
|
|
||||||
wxMenu* MenuFactory::instance_menu()
|
wxMenu* MenuFactory::instance_menu()
|
||||||
{
|
{
|
||||||
return &m_instance_menu;
|
return &m_instance_menu;
|
||||||
|
|
|
@ -34,7 +34,9 @@ class MenuFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS;
|
static const std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS;
|
||||||
static std::vector<wxBitmapBundle*> get_volume_bitmaps();
|
static const std::vector<std::pair<std::string, std::string>> TEXT_VOLUME_ICONS;
|
||||||
|
static std::vector<wxBitmapBundle*> get_volume_bitmaps();
|
||||||
|
static std::vector<wxBitmapBundle*> get_text_volume_bitmaps();
|
||||||
|
|
||||||
MenuFactory();
|
MenuFactory();
|
||||||
~MenuFactory() = default;
|
~MenuFactory() = default;
|
||||||
|
@ -51,6 +53,7 @@ public:
|
||||||
wxMenu* object_menu();
|
wxMenu* object_menu();
|
||||||
wxMenu* sla_object_menu();
|
wxMenu* sla_object_menu();
|
||||||
wxMenu* part_menu();
|
wxMenu* part_menu();
|
||||||
|
wxMenu* text_part_menu();
|
||||||
wxMenu* instance_menu();
|
wxMenu* instance_menu();
|
||||||
wxMenu* layer_menu();
|
wxMenu* layer_menu();
|
||||||
wxMenu* multi_selection_menu();
|
wxMenu* multi_selection_menu();
|
||||||
|
@ -66,6 +69,7 @@ private:
|
||||||
|
|
||||||
MenuWithSeparators m_object_menu;
|
MenuWithSeparators m_object_menu;
|
||||||
MenuWithSeparators m_part_menu;
|
MenuWithSeparators m_part_menu;
|
||||||
|
MenuWithSeparators m_text_part_menu;
|
||||||
MenuWithSeparators m_sla_object_menu;
|
MenuWithSeparators m_sla_object_menu;
|
||||||
MenuWithSeparators m_default_menu;
|
MenuWithSeparators m_default_menu;
|
||||||
MenuWithSeparators m_instance_menu;
|
MenuWithSeparators m_instance_menu;
|
||||||
|
@ -79,10 +83,14 @@ private:
|
||||||
void create_common_object_menu(wxMenu *menu);
|
void create_common_object_menu(wxMenu *menu);
|
||||||
void create_object_menu();
|
void create_object_menu();
|
||||||
void create_sla_object_menu();
|
void create_sla_object_menu();
|
||||||
|
void append_immutable_part_menu_items(wxMenu* menu);
|
||||||
|
void append_mutable_part_menu_items(wxMenu* menu);
|
||||||
void create_part_menu();
|
void create_part_menu();
|
||||||
|
void create_text_part_menu();
|
||||||
void create_instance_menu();
|
void create_instance_menu();
|
||||||
|
|
||||||
wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type);
|
wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type);
|
||||||
|
void append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item = true);
|
||||||
void append_menu_items_add_volume(wxMenu* menu);
|
void append_menu_items_add_volume(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
|
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||||
|
@ -103,6 +111,7 @@ private:
|
||||||
void append_menu_item_merge_to_multipart_object(wxMenu *menu);
|
void append_menu_item_merge_to_multipart_object(wxMenu *menu);
|
||||||
// void append_menu_item_merge_to_single_object(wxMenu *menu);
|
// void append_menu_item_merge_to_single_object(wxMenu *menu);
|
||||||
void append_menu_items_mirror(wxMenu *menu);
|
void append_menu_items_mirror(wxMenu *menu);
|
||||||
|
void append_menu_item_edit_text(wxMenu *menu);
|
||||||
void append_menu_items_instance_manipulation(wxMenu *menu);
|
void append_menu_items_instance_manipulation(wxMenu *menu);
|
||||||
void update_menu_items_instance_manipulation(MenuType type);
|
void update_menu_items_instance_manipulation(MenuType type);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#include "libslic3r/TextConfiguration.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "GUI_Factories.hpp"
|
#include "GUI_Factories.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
|
@ -649,14 +650,22 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
|
||||||
|
|
||||||
ModelObject* obj = object(obj_idx);
|
ModelObject* obj = object(obj_idx);
|
||||||
if (m_objects_model->GetItemType(item) & itObject) {
|
if (m_objects_model->GetItemType(item) & itObject) {
|
||||||
obj->name = m_objects_model->GetName(item).ToUTF8().data();
|
obj->name = into_u8(m_objects_model->GetName(item));
|
||||||
// if object has just one volume, rename this volume too
|
// if object has just one volume, rename this volume too
|
||||||
if (obj->volumes.size() == 1)
|
if (obj->volumes.size() == 1 && !obj->volumes[0]->text_configuration.has_value())
|
||||||
obj->volumes[0]->name = obj->name;
|
obj->volumes[0]->name = obj->name;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volume_id < 0) return;
|
if (volume_id < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Renaming of the text volume is suppressed
|
||||||
|
// So, revert the name in object list
|
||||||
|
if (obj->volumes[volume_id]->text_configuration.has_value()) {
|
||||||
|
m_objects_model->SetName(from_u8(obj->volumes[volume_id]->name), item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
obj->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data();
|
obj->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,6 +681,10 @@ void ObjectList::update_name_in_list(int obj_idx, int vol_idx) const
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_objects_model->SetName(new_name, item);
|
m_objects_model->SetName(new_name, item);
|
||||||
|
|
||||||
|
// if object has just one volume, rename object too
|
||||||
|
if (ModelObject* obj = object(obj_idx); obj->volumes.size() == 1)
|
||||||
|
obj->name = obj->volumes.front()->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::selection_changed()
|
void ObjectList::selection_changed()
|
||||||
|
@ -968,11 +981,17 @@ void ObjectList::show_context_menu(const bool evt_context_menu)
|
||||||
const ItemType type = m_objects_model->GetItemType(item);
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
if (!(type & (itObject | itVolume | itLayer | itInstance)))
|
if (!(type & (itObject | itVolume | itLayer | itInstance)))
|
||||||
return;
|
return;
|
||||||
|
if (type & itVolume) {
|
||||||
menu = type & itInstance ? plater->instance_menu() :
|
int obj_idx, vol_idx;
|
||||||
type & itLayer ? plater->layer_menu() :
|
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||||
m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? plater->part_menu() :
|
if (obj_idx < 0 || vol_idx < 0)
|
||||||
printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu();
|
return;
|
||||||
|
menu = object(obj_idx)->volumes[vol_idx]->text_configuration.has_value() ? plater->text_part_menu() : plater->part_menu();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
menu = type & itInstance ? plater->instance_menu() :
|
||||||
|
type & itLayer ? plater->layer_menu() :
|
||||||
|
printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu();
|
||||||
}
|
}
|
||||||
else if (evt_context_menu)
|
else if (evt_context_menu)
|
||||||
menu = plater->default_menu();
|
menu = plater->default_menu();
|
||||||
|
@ -1749,7 +1768,7 @@ void ObjectList::load_shape_object(const std::string& type_name)
|
||||||
// Create mesh
|
// Create mesh
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
TriangleMesh mesh = create_mesh(type_name, bb);
|
TriangleMesh mesh = create_mesh(type_name, bb);
|
||||||
load_mesh_object(mesh, _L("Shape") + "-" + _(type_name));
|
load_mesh_object(mesh, _u8L("Shape") + "-" + type_name);
|
||||||
#if ENABLE_RELOAD_FROM_DISK_REWORK
|
#if ENABLE_RELOAD_FROM_DISK_REWORK
|
||||||
if (!m_objects->empty())
|
if (!m_objects->empty())
|
||||||
m_objects->back()->volumes.front()->source.is_from_builtin_objects = true;
|
m_objects->back()->volumes.front()->source.is_from_builtin_objects = true;
|
||||||
|
@ -1789,7 +1808,12 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files
|
||||||
wxGetApp().mainframe->update_title();
|
wxGetApp().mainframe->update_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)
|
void ObjectList::load_mesh_object(
|
||||||
|
const TriangleMesh & mesh,
|
||||||
|
const std::string & name,
|
||||||
|
bool center,
|
||||||
|
const TextConfiguration *text_config /* = nullptr*/,
|
||||||
|
const Transform3d * transformation /* = nullptr*/)
|
||||||
{
|
{
|
||||||
// Add mesh to model as a new object
|
// Add mesh to model as a new object
|
||||||
Model& model = wxGetApp().plater()->model();
|
Model& model = wxGetApp().plater()->model();
|
||||||
|
@ -1799,22 +1823,29 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
|
||||||
#endif /* _DEBUG */
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
std::vector<size_t> object_idxs;
|
std::vector<size_t> object_idxs;
|
||||||
auto bb = mesh.bounding_box();
|
|
||||||
ModelObject* new_object = model.add_object();
|
ModelObject* new_object = model.add_object();
|
||||||
new_object->name = into_u8(name);
|
new_object->name = name;
|
||||||
new_object->add_instance(); // each object should have at list one instance
|
new_object->add_instance(); // each object should have at list one instance
|
||||||
|
|
||||||
ModelVolume* new_volume = new_object->add_volume(mesh);
|
ModelVolume* new_volume = new_object->add_volume(mesh);
|
||||||
new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
new_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
|
||||||
new_volume->name = into_u8(name);
|
new_volume->name = name;
|
||||||
|
if (text_config)
|
||||||
|
new_volume->text_configuration = *text_config;
|
||||||
// set a default extruder value, since user can't add it manually
|
// set a default extruder value, since user can't add it manually
|
||||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
new_object->invalidate_bounding_box();
|
new_object->invalidate_bounding_box();
|
||||||
new_object->translate(-bb.center());
|
if (transformation) {
|
||||||
|
assert(!center);
|
||||||
new_object->instances[0]->set_offset(center ?
|
Slic3r::Geometry::Transformation tr(*transformation);
|
||||||
to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) :
|
new_object->instances[0]->set_transformation(tr);
|
||||||
|
} else {
|
||||||
|
auto bb = mesh.bounding_box();
|
||||||
|
new_object->translate(-bb.center());
|
||||||
|
new_object->instances[0]->set_offset(
|
||||||
|
center ? to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) :
|
||||||
bb.center());
|
bb.center());
|
||||||
|
}
|
||||||
|
|
||||||
new_object->ensure_on_bed();
|
new_object->ensure_on_bed();
|
||||||
|
|
||||||
|
@ -2898,6 +2929,7 @@ wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, st
|
||||||
from_u8(volume->name),
|
from_u8(volume->name),
|
||||||
volume_idx,
|
volume_idx,
|
||||||
volume->type(),
|
volume->type(),
|
||||||
|
volume->text_configuration.has_value(),
|
||||||
get_warning_icon_name(volume->mesh().stats()),
|
get_warning_icon_name(volume->mesh().stats()),
|
||||||
extruder2str(volume->config.has("extruder") ? volume->config.extruder() : 0));
|
extruder2str(volume->config.has("extruder") ? volume->config.extruder() : 0));
|
||||||
add_settings_item(vol_item, &volume->config.get());
|
add_settings_item(vol_item, &volume->config.get());
|
||||||
|
@ -4160,14 +4192,14 @@ void ObjectList::change_part_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_cut_object = obj->is_cut();
|
const bool is_cut_object = obj->is_cut();
|
||||||
|
|
||||||
wxArrayString names;
|
wxArrayString names;
|
||||||
names.Alloc(is_cut_object ? 3 : 5);
|
|
||||||
if (!is_cut_object)
|
if (!is_cut_object)
|
||||||
for (const wxString& type : { _L("Part"), _L("Negative Volume") })
|
for (const wxString& type : { _L("Part"), _L("Negative Volume") })
|
||||||
names.Add(type);
|
names.Add(type);
|
||||||
for (const wxString& type : { _L("Modifier"), _L("Support Blocker"), _L("Support Enforcer") } )
|
names.Add(_L("Modifier"));
|
||||||
names.Add(type);
|
if (!volume->text_configuration.has_value())
|
||||||
|
for (const wxString& name : { _L("Support Blocker"), _L("Support Enforcer") })
|
||||||
|
names.Add(name);
|
||||||
|
|
||||||
const int type_shift = is_cut_object ? 2 : 0;
|
const int type_shift = is_cut_object ? 2 : 0;
|
||||||
auto new_type = ModelVolumeType(type_shift + wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type) - type_shift));
|
auto new_type = ModelVolumeType(type_shift + wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type) - type_shift));
|
||||||
|
|
|
@ -27,6 +27,7 @@ class ModelConfig;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
class ModelVolume;
|
class ModelVolume;
|
||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
|
struct TextConfiguration;
|
||||||
enum class ModelVolumeType : int;
|
enum class ModelVolumeType : int;
|
||||||
|
|
||||||
// FIXME: broken build on mac os because of this is missing:
|
// FIXME: broken build on mac os because of this is missing:
|
||||||
|
@ -257,7 +258,8 @@ public:
|
||||||
void load_shape_object(const std::string &type_name);
|
void load_shape_object(const std::string &type_name);
|
||||||
void load_shape_object_from_gallery();
|
void load_shape_object_from_gallery();
|
||||||
void load_shape_object_from_gallery(const wxArrayString& input_files);
|
void load_shape_object_from_gallery(const wxArrayString& input_files);
|
||||||
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
|
void load_mesh_object(const TriangleMesh &mesh, const std::string &name, bool center = true,
|
||||||
|
const TextConfiguration* text_config = nullptr, const Transform3d* transformation = nullptr);
|
||||||
bool del_object(const int obj_idx);
|
bool del_object(const int obj_idx);
|
||||||
bool del_subobject_item(wxDataViewItem& item);
|
bool del_subobject_item(wxDataViewItem& item);
|
||||||
void del_settings_from_config(const wxDataViewItem& parent_item);
|
void del_settings_from_config(const wxDataViewItem& parent_item);
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace GUI
|
||||||
{
|
{
|
||||||
|
|
||||||
const double ObjectManipulation::in_to_mm = 25.4;
|
const double ObjectManipulation::in_to_mm = 25.4;
|
||||||
const double ObjectManipulation::mm_to_in = 0.0393700787;
|
const double ObjectManipulation::mm_to_in = 1 / ObjectManipulation::in_to_mm;
|
||||||
|
|
||||||
// Helper function to be used by drop to bed button. Returns lowest point of this
|
// Helper function to be used by drop to bed button. Returns lowest point of this
|
||||||
// volume in world coordinate system.
|
// volume in world coordinate system.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,113 +1,116 @@
|
||||||
#ifndef slic3r_GLGizmoBase_hpp_
|
#ifndef slic3r_GLGizmoBase_hpp_
|
||||||
#define slic3r_GLGizmoBase_hpp_
|
#define slic3r_GLGizmoBase_hpp_
|
||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/Color.hpp"
|
#include "libslic3r/Color.hpp"
|
||||||
|
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/GLModel.hpp"
|
#include "slic3r/GUI/GLModel.hpp"
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#if ENABLE_RAYCAST_PICKING
|
||||||
#include "slic3r/GUI/MeshUtils.hpp"
|
#include "slic3r/GUI/MeshUtils.hpp"
|
||||||
#include "slic3r/GUI/SceneRaycaster.hpp"
|
#include "slic3r/GUI/SceneRaycaster.hpp"
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
#include <cereal/archives/binary.hpp>
|
#include <cereal/archives/binary.hpp>
|
||||||
|
|
||||||
class wxWindow;
|
class wxWindow;
|
||||||
class wxMouseEvent;
|
class wxMouseEvent;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class BoundingBoxf3;
|
class BoundingBoxf3;
|
||||||
class Linef3;
|
class Linef3;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
static const ColorRGBA DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f };
|
static const ColorRGBA DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f };
|
||||||
static const ColorRGBA DEFAULT_DRAG_COLOR = ColorRGBA::WHITE();
|
static const ColorRGBA DEFAULT_DRAG_COLOR = ColorRGBA::WHITE();
|
||||||
static const ColorRGBA DEFAULT_HIGHLIGHT_COLOR = ColorRGBA::ORANGE();
|
static const ColorRGBA DEFAULT_HIGHLIGHT_COLOR = ColorRGBA::ORANGE();
|
||||||
static const std::array<ColorRGBA, 3> AXES_COLOR = {{ ColorRGBA::X(), ColorRGBA::Y(), ColorRGBA::Z() }};
|
static const std::array<ColorRGBA, 3> AXES_COLOR = {{ ColorRGBA::X(), ColorRGBA::Y(), ColorRGBA::Z() }};
|
||||||
static const ColorRGBA CONSTRAINED_COLOR = ColorRGBA::GRAY();
|
static const ColorRGBA CONSTRAINED_COLOR = ColorRGBA::GRAY();
|
||||||
|
|
||||||
class ImGuiWrapper;
|
class ImGuiWrapper;
|
||||||
class GLCanvas3D;
|
class GLCanvas3D;
|
||||||
enum class CommonGizmosDataID;
|
enum class CommonGizmosDataID;
|
||||||
class CommonGizmosDataPool;
|
class CommonGizmosDataPool;
|
||||||
|
|
||||||
class GLGizmoBase
|
class GLGizmoBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Starting value for ids to avoid clashing with ids used by GLVolumes
|
// Starting value for ids to avoid clashing with ids used by GLVolumes
|
||||||
// (254 is choosen to leave some space for forward compatibility)
|
// (254 is choosen to leave some space for forward compatibility)
|
||||||
static const unsigned int BASE_ID = 255 * 255 * 254;
|
static const unsigned int BASE_ID = 255 * 255 * 254;
|
||||||
static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7;
|
static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7;
|
||||||
|
|
||||||
enum class EGrabberExtension
|
enum class EGrabberExtension
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
PosX = 1 << 0,
|
PosX = 1 << 0,
|
||||||
NegX = 1 << 1,
|
NegX = 1 << 1,
|
||||||
PosY = 1 << 2,
|
PosY = 1 << 2,
|
||||||
NegY = 1 << 3,
|
NegY = 1 << 3,
|
||||||
PosZ = 1 << 4,
|
PosZ = 1 << 4,
|
||||||
NegZ = 1 << 5,
|
NegZ = 1 << 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
// Represents NO key(button on keyboard) value
|
||||||
struct Grabber
|
static const int NO_SHORTCUT_KEY_VALUE = 0;
|
||||||
{
|
|
||||||
static const float SizeFactor;
|
protected:
|
||||||
static const float MinHalfSize;
|
struct Grabber
|
||||||
static const float DraggingScaleFactor;
|
{
|
||||||
|
static const float SizeFactor;
|
||||||
bool enabled{ true };
|
static const float MinHalfSize;
|
||||||
bool dragging{ false };
|
static const float DraggingScaleFactor;
|
||||||
Vec3d center{ Vec3d::Zero() };
|
|
||||||
Vec3d angles{ Vec3d::Zero() };
|
bool enabled{ true };
|
||||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
bool dragging{ false };
|
||||||
Transform3d matrix{ Transform3d::Identity() };
|
Vec3d center{ Vec3d::Zero() };
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
Vec3d angles{ Vec3d::Zero() };
|
||||||
ColorRGBA color{ ColorRGBA::WHITE() };
|
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
EGrabberExtension extensions{ EGrabberExtension::None };
|
Transform3d matrix{ Transform3d::Identity() };
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
// the picking id shared by all the elements
|
ColorRGBA color{ ColorRGBA::WHITE() };
|
||||||
int picking_id{ -1 };
|
EGrabberExtension extensions{ EGrabberExtension::None };
|
||||||
std::array<std::shared_ptr<SceneRaycasterItem>, GRABBER_ELEMENTS_MAX_COUNT> raycasters = { nullptr };
|
#if ENABLE_RAYCAST_PICKING
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
// the picking id shared by all the elements
|
||||||
|
int picking_id{ -1 };
|
||||||
Grabber() = default;
|
std::array<std::shared_ptr<SceneRaycasterItem>, GRABBER_ELEMENTS_MAX_COUNT> raycasters = { nullptr };
|
||||||
~Grabber();
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
#if ENABLE_RAYCAST_PICKING
|
Grabber() = default;
|
||||||
void render(bool hover, float size) { render(size, hover ? complementary(color) : color); }
|
~Grabber();
|
||||||
#else
|
|
||||||
void render(bool hover, float size) { render(size, hover ? complementary(color) : color, false); }
|
#if ENABLE_RAYCAST_PICKING
|
||||||
void render_for_picking(float size) { render(size, color, true); }
|
void render(bool hover, float size) { render(size, hover ? complementary(color) : color); }
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#else
|
||||||
|
void render(bool hover, float size) { render(size, hover ? complementary(color) : color, false); }
|
||||||
float get_half_size(float size) const;
|
void render_for_picking(float size) { render(size, color, true); }
|
||||||
float get_dragging_half_size(float size) const;
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
#if ENABLE_RAYCAST_PICKING
|
float get_half_size(float size) const;
|
||||||
void register_raycasters_for_picking(int id);
|
float get_dragging_half_size(float size) const;
|
||||||
void unregister_raycasters_for_picking();
|
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#if ENABLE_RAYCAST_PICKING
|
||||||
|
void register_raycasters_for_picking(int id);
|
||||||
private:
|
void unregister_raycasters_for_picking();
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
void render(float size, const ColorRGBA& render_color);
|
|
||||||
#else
|
private:
|
||||||
void render(float size, const ColorRGBA& render_color, bool picking);
|
#if ENABLE_RAYCAST_PICKING
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
void render(float size, const ColorRGBA& render_color);
|
||||||
|
#else
|
||||||
#if ENABLE_RAYCAST_PICKING
|
void render(float size, const ColorRGBA& render_color, bool picking);
|
||||||
static PickingModel s_cube;
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
static PickingModel s_cone;
|
|
||||||
#else
|
#if ENABLE_RAYCAST_PICKING
|
||||||
static GLModel s_cube;
|
static PickingModel s_cube;
|
||||||
static GLModel s_cone;
|
static PickingModel s_cone;
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#else
|
||||||
|
static GLModel s_cube;
|
||||||
|
static GLModel s_cone;
|
||||||
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -131,17 +134,17 @@ public:
|
||||||
protected:
|
protected:
|
||||||
GLCanvas3D& m_parent;
|
GLCanvas3D& m_parent;
|
||||||
|
|
||||||
int m_group_id{ -1 }; // TODO: remove only for rotate
|
int m_group_id; // TODO: remove only for rotate
|
||||||
EState m_state{ Off };
|
EState m_state;
|
||||||
int m_shortcut_key{ 0 };
|
int m_shortcut_key;
|
||||||
std::string m_icon_filename;
|
std::string m_icon_filename;
|
||||||
unsigned int m_sprite_id;
|
unsigned int m_sprite_id;
|
||||||
int m_hover_id{ -1 };
|
int m_hover_id{ -1 };
|
||||||
bool m_dragging{ false };
|
bool m_dragging{ false };
|
||||||
mutable std::vector<Grabber> m_grabbers;
|
mutable std::vector<Grabber> m_grabbers;
|
||||||
ImGuiWrapper* m_imgui;
|
ImGuiWrapper* m_imgui;
|
||||||
bool m_first_input_window_render{ true };
|
bool m_first_input_window_render{ true };
|
||||||
CommonGizmosDataPool* m_c{ nullptr };
|
CommonGizmosDataPool* m_c{ nullptr };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLGizmoBase(GLCanvas3D& parent,
|
GLGizmoBase(GLCanvas3D& parent,
|
||||||
|
@ -183,9 +186,9 @@ public:
|
||||||
bool update_items_state();
|
bool update_items_state();
|
||||||
|
|
||||||
void render() { on_render(); }
|
void render() { on_render(); }
|
||||||
#if !ENABLE_RAYCAST_PICKING
|
#if !ENABLE_RAYCAST_PICKING
|
||||||
void render_for_picking() { on_render_for_picking(); }
|
void render_for_picking() { on_render_for_picking(); }
|
||||||
#endif // !ENABLE_RAYCAST_PICKING
|
#endif // !ENABLE_RAYCAST_PICKING
|
||||||
void render_input_window(float x, float y, float bottom_limit);
|
void render_input_window(float x, float y, float bottom_limit);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -207,13 +210,13 @@ public:
|
||||||
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
|
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
|
||||||
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
|
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
|
||||||
|
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#if ENABLE_RAYCAST_PICKING
|
||||||
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
|
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
|
||||||
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
|
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
virtual bool is_in_editing_mode() const { return false; }
|
virtual bool is_in_editing_mode() const { return false; }
|
||||||
virtual bool is_selection_rectangle_dragging() const { return false; }
|
virtual bool is_selection_rectangle_dragging() const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool on_init() = 0;
|
virtual bool on_init() = 0;
|
||||||
|
@ -234,27 +237,27 @@ protected:
|
||||||
virtual void on_dragging(const UpdateData& data) {}
|
virtual void on_dragging(const UpdateData& data) {}
|
||||||
|
|
||||||
virtual void on_render() = 0;
|
virtual void on_render() = 0;
|
||||||
#if !ENABLE_RAYCAST_PICKING
|
#if !ENABLE_RAYCAST_PICKING
|
||||||
virtual void on_render_for_picking() = 0;
|
virtual void on_render_for_picking() = 0;
|
||||||
#endif // !ENABLE_RAYCAST_PICKING
|
#endif // !ENABLE_RAYCAST_PICKING
|
||||||
virtual void on_render_input_window(float x, float y, float bottom_limit) {}
|
virtual void on_render_input_window(float x, float y, float bottom_limit) {}
|
||||||
|
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#if ENABLE_RAYCAST_PICKING
|
||||||
void register_grabbers_for_picking();
|
void register_grabbers_for_picking();
|
||||||
void unregister_grabbers_for_picking();
|
void unregister_grabbers_for_picking();
|
||||||
virtual void on_register_raycasters_for_picking() {}
|
virtual void on_register_raycasters_for_picking() {}
|
||||||
virtual void on_unregister_raycasters_for_picking() {}
|
virtual void on_unregister_raycasters_for_picking() {}
|
||||||
#else
|
#else
|
||||||
// Returns the picking color for the given id, based on the BASE_ID constant
|
// Returns the picking color for the given id, based on the BASE_ID constant
|
||||||
// No check is made for clashing with other picking color (i.e. GLVolumes)
|
// No check is made for clashing with other picking color (i.e. GLVolumes)
|
||||||
ColorRGBA picking_color_component(unsigned int id) const;
|
ColorRGBA picking_color_component(unsigned int id) const;
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
void render_grabbers(const BoundingBoxf3& box) const;
|
void render_grabbers(const BoundingBoxf3& box) const;
|
||||||
void render_grabbers(float size) const;
|
void render_grabbers(float size) const;
|
||||||
#if !ENABLE_RAYCAST_PICKING
|
#if !ENABLE_RAYCAST_PICKING
|
||||||
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
|
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
|
||||||
#endif // !ENABLE_RAYCAST_PICKING
|
#endif // !ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
std::string format(float value, unsigned int decimals) const;
|
std::string format(float value, unsigned int decimals) const;
|
||||||
|
|
||||||
|
@ -277,7 +280,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
// Flag for dirty visible state of Gizmo
|
// Flag for dirty visible state of Gizmo
|
||||||
// When True then need new rendering
|
// When True then need new rendering
|
||||||
bool m_dirty{ false };
|
bool m_dirty{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
|
|
3438
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
3438
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
File diff suppressed because it is too large
Load diff
360
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
360
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
#ifndef slic3r_GLGizmoEmboss_hpp_
|
||||||
|
#define slic3r_GLGizmoEmboss_hpp_
|
||||||
|
|
||||||
|
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
|
||||||
|
// which overrides our localization "L" macro.
|
||||||
|
#include "GLGizmoBase.hpp"
|
||||||
|
#include "GLGizmoRotate.hpp"
|
||||||
|
#include "slic3r/GUI/GLTexture.hpp"
|
||||||
|
#include "slic3r/Utils/RaycastManager.hpp"
|
||||||
|
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
||||||
|
|
||||||
|
#include "admesh/stl.h" // indexed_triangle_set
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "libslic3r/Emboss.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/TextConfiguration.hpp"
|
||||||
|
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
class wxFont;
|
||||||
|
namespace Slic3r{
|
||||||
|
class AppConfig;
|
||||||
|
class GLVolume;
|
||||||
|
|
||||||
|
enum class ModelVolumeType : int;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Slic3r::GUI {
|
||||||
|
class MeshRaycaster;
|
||||||
|
class GLGizmoEmboss : public GLGizmoBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GLGizmoEmboss(GLCanvas3D& parent);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new embossed text volume by type on position of mouse
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
|
/// <param name="mouse_pos">Define position of new volume</param>
|
||||||
|
void create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new text without given position
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume_type">Object part / Negative volume / Modifier</param>
|
||||||
|
void create_volume(ModelVolumeType volume_type);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool on_init() override;
|
||||||
|
std::string on_get_name() const override;
|
||||||
|
void on_render() override;
|
||||||
|
#if ENABLE_RAYCAST_PICKING
|
||||||
|
virtual void on_register_raycasters_for_picking() override;
|
||||||
|
virtual void on_unregister_raycasters_for_picking() override;
|
||||||
|
#else // !ENABLE_RAYCAST_PICKING
|
||||||
|
void on_render_for_picking() override;
|
||||||
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||||
|
bool on_is_activable() const override { return true; }
|
||||||
|
bool on_is_selectable() const override { return false; }
|
||||||
|
void on_set_state() override;
|
||||||
|
|
||||||
|
void on_set_hover_id() override{ m_rotate_gizmo.set_hover_id(m_hover_id); }
|
||||||
|
void on_enable_grabber(unsigned int id) override { m_rotate_gizmo.enable_grabber(); }
|
||||||
|
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
|
||||||
|
void on_start_dragging() override;
|
||||||
|
void on_stop_dragging() override;
|
||||||
|
void on_dragging(const UpdateData &data) override;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate by text on dragging rotate grabers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mouse_event">Information about mouse</param>
|
||||||
|
/// <returns>Propagete normaly return false.</returns>
|
||||||
|
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||||
|
|
||||||
|
bool wants_enter_leave_snapshots() const override { return true; }
|
||||||
|
std::string get_gizmo_entering_text() const override { return _u8L("Enter emboss gizmo"); }
|
||||||
|
std::string get_gizmo_leaving_text() const override { return _u8L("Leave emboss gizmo"); }
|
||||||
|
std::string get_action_snapshot_name() override { return _u8L("Embossing actions"); }
|
||||||
|
private:
|
||||||
|
void initialize();
|
||||||
|
static EmbossStyles create_default_styles();
|
||||||
|
// localized default text
|
||||||
|
void set_default_text();
|
||||||
|
|
||||||
|
void check_selection();
|
||||||
|
ModelVolume *get_selected_volume();
|
||||||
|
// create volume from text - main functionality
|
||||||
|
bool process();
|
||||||
|
void close();
|
||||||
|
void discard_and_close();
|
||||||
|
void draw_window();
|
||||||
|
void draw_text_input();
|
||||||
|
void draw_model_type();
|
||||||
|
void fix_transformation(const FontProp &from, const FontProp &to);
|
||||||
|
void draw_style_list();
|
||||||
|
void draw_delete_style_button();
|
||||||
|
void draw_style_rename_popup();
|
||||||
|
void draw_style_rename_button();
|
||||||
|
void draw_style_save_button(bool is_modified);
|
||||||
|
void draw_style_save_as_popup();
|
||||||
|
void draw_style_add_button();
|
||||||
|
void init_font_name_texture();
|
||||||
|
struct FaceName;
|
||||||
|
void draw_font_preview(FaceName &face, bool is_visible);
|
||||||
|
void draw_font_list();
|
||||||
|
void draw_style_edit();
|
||||||
|
bool draw_italic_button();
|
||||||
|
bool draw_bold_button();
|
||||||
|
void draw_advanced();
|
||||||
|
|
||||||
|
bool select_facename(const wxString& facename);
|
||||||
|
void init_face_names();
|
||||||
|
|
||||||
|
void do_translate(const Vec3d& relative_move);
|
||||||
|
void do_rotate(float relative_z_angle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move window for edit emboss text near to embossed object
|
||||||
|
/// NOTE: embossed object must be selected
|
||||||
|
/// </summary>
|
||||||
|
void set_fine_position();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reversible input float with option to restor default value
|
||||||
|
/// TODO: make more general, static and move to ImGuiWrapper
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True when value changed otherwise FALSE.</returns>
|
||||||
|
bool rev_input(const std::string &name, float &value, const float *default_value,
|
||||||
|
const std::string &undo_tooltip, float step, float step_fast, const char *format,
|
||||||
|
ImGuiInputTextFlags flags = 0);
|
||||||
|
bool rev_checkbox(const std::string &name, bool &value, const bool* default_value, const std::string &undo_tooltip);
|
||||||
|
bool rev_slider(const std::string &name, std::optional<int>& value, const std::optional<int> *default_value,
|
||||||
|
const std::string &undo_tooltip, int v_min, int v_max, const std::string &format, const wxString &tooltip);
|
||||||
|
bool rev_slider(const std::string &name, std::optional<float>& value, const std::optional<float> *default_value,
|
||||||
|
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip);
|
||||||
|
bool rev_slider(const std::string &name, float &value, const float *default_value,
|
||||||
|
const std::string &undo_tooltip, float v_min, float v_max, const std::string &format, const wxString &tooltip);
|
||||||
|
template<typename T, typename Draw>
|
||||||
|
bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw);
|
||||||
|
|
||||||
|
void set_minimal_window_size(bool is_advance_edit_style);
|
||||||
|
const ImVec2 &get_minimal_window_size() const;
|
||||||
|
|
||||||
|
// process mouse event
|
||||||
|
bool on_mouse_for_rotation(const wxMouseEvent &mouse_event);
|
||||||
|
bool on_mouse_for_translate(const wxMouseEvent &mouse_event);
|
||||||
|
|
||||||
|
bool choose_font_by_wxdialog();
|
||||||
|
bool choose_true_type_file();
|
||||||
|
bool choose_svg_file();
|
||||||
|
|
||||||
|
bool load_configuration(ModelVolume *volume);
|
||||||
|
|
||||||
|
// When open text loaded from .3mf it could be written with unknown font
|
||||||
|
bool m_is_unknown_font;
|
||||||
|
void create_notification_not_valid_font(const TextConfiguration& tc);
|
||||||
|
void remove_notification_not_valid_font();
|
||||||
|
|
||||||
|
// This configs holds GUI layout size given by translated texts.
|
||||||
|
// etc. When language changes, GUI is recreated and this class constructed again,
|
||||||
|
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||||
|
struct GuiCfg
|
||||||
|
{
|
||||||
|
// Zero means it is calculated in init function
|
||||||
|
ImVec2 minimal_window_size = ImVec2(0, 0);
|
||||||
|
ImVec2 minimal_window_size_with_advance = ImVec2(0, 0);
|
||||||
|
ImVec2 minimal_window_size_with_collections = ImVec2(0, 0);
|
||||||
|
float input_width = 0.f;
|
||||||
|
float delete_pos_x = 0.f;
|
||||||
|
float max_style_name_width = 0.f;
|
||||||
|
unsigned int icon_width = 0;
|
||||||
|
|
||||||
|
// maximal width and height of style image
|
||||||
|
Vec2i max_style_image_size = Vec2i(0, 0);
|
||||||
|
|
||||||
|
float style_offset = 0.f;
|
||||||
|
float input_offset = 0.f;
|
||||||
|
float advanced_input_offset = 0.f;
|
||||||
|
|
||||||
|
ImVec2 text_size;
|
||||||
|
|
||||||
|
// maximal size of face name image
|
||||||
|
Vec2i face_name_size = Vec2i(100, 0);
|
||||||
|
float face_name_max_width = 100.f;
|
||||||
|
float face_name_texture_offset_x = 105.f;
|
||||||
|
|
||||||
|
// maximal texture generate jobs running at once
|
||||||
|
unsigned int max_count_opened_font_files = 10;
|
||||||
|
|
||||||
|
// Only translations needed for calc GUI size
|
||||||
|
struct Translations
|
||||||
|
{
|
||||||
|
std::string type;
|
||||||
|
std::string style;
|
||||||
|
std::string font;
|
||||||
|
std::string size;
|
||||||
|
std::string depth;
|
||||||
|
std::string use_surface;
|
||||||
|
|
||||||
|
// advanced
|
||||||
|
std::string char_gap;
|
||||||
|
std::string line_gap;
|
||||||
|
std::string boldness;
|
||||||
|
std::string italic;
|
||||||
|
std::string surface_distance;
|
||||||
|
std::string angle;
|
||||||
|
std::string collection;
|
||||||
|
};
|
||||||
|
Translations translations;
|
||||||
|
|
||||||
|
GuiCfg() = default;
|
||||||
|
};
|
||||||
|
std::optional<const GuiCfg> m_gui_cfg;
|
||||||
|
// setted only when wanted to use - not all the time
|
||||||
|
std::optional<ImVec2> m_set_window_offset;
|
||||||
|
bool m_is_advanced_edit_style = false;
|
||||||
|
|
||||||
|
Emboss::StyleManager m_style_manager;
|
||||||
|
|
||||||
|
struct FaceName{
|
||||||
|
wxString wx_name;
|
||||||
|
std::string name_truncated = "";
|
||||||
|
size_t texture_index = 0;
|
||||||
|
// State for generation of texture
|
||||||
|
// when start generate create share pointers
|
||||||
|
std::shared_ptr<std::atomic<bool>> cancel = nullptr;
|
||||||
|
// R/W only on main thread - finalize of job
|
||||||
|
std::shared_ptr<bool> is_created = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep sorted list of loadable face names
|
||||||
|
struct Facenames
|
||||||
|
{
|
||||||
|
// flag to keep need of enumeration fonts from OS
|
||||||
|
// false .. wants new enumeration check by Hash
|
||||||
|
// true .. already enumerated(During opened combo box)
|
||||||
|
bool is_init = false;
|
||||||
|
|
||||||
|
// data of can_load() faces
|
||||||
|
std::vector<FaceName> faces = {};
|
||||||
|
// Sorter set of Non valid face names in OS
|
||||||
|
std::vector<wxString> bad = {};
|
||||||
|
|
||||||
|
// Configuration of font encoding
|
||||||
|
const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM;
|
||||||
|
|
||||||
|
// Identify if preview texture exists
|
||||||
|
GLuint texture_id = 0;
|
||||||
|
|
||||||
|
// protection for open too much font files together
|
||||||
|
// Gtk:ERROR:../../../../gtk/gtkiconhelper.c:494:ensure_surface_for_gicon: assertion failed (error == NULL): Failed to load /usr/share/icons/Yaru/48x48/status/image-missing.png: Error opening file /usr/share/icons/Yaru/48x48/status/image-missing.png: Too many open files (g-io-error-quark, 31)
|
||||||
|
unsigned int count_opened_font_files = 0;
|
||||||
|
|
||||||
|
// Configuration for texture height
|
||||||
|
const int count_cached_textures = 32;
|
||||||
|
|
||||||
|
// index for new generated texture index(must be lower than count_cached_textures)
|
||||||
|
size_t texture_index = 0;
|
||||||
|
|
||||||
|
// hash created from enumerated font from OS
|
||||||
|
// check when new font was installed
|
||||||
|
size_t hash = 0;
|
||||||
|
} m_face_names;
|
||||||
|
static bool store(const Facenames &facenames);
|
||||||
|
static bool load(Facenames &facenames);
|
||||||
|
|
||||||
|
|
||||||
|
// Text to emboss
|
||||||
|
std::string m_text;
|
||||||
|
|
||||||
|
// actual volume
|
||||||
|
ModelVolume *m_volume;
|
||||||
|
|
||||||
|
// state of volume when open EmbossGizmo
|
||||||
|
struct EmbossVolume
|
||||||
|
{
|
||||||
|
TriangleMesh tm;
|
||||||
|
TextConfiguration tc;
|
||||||
|
Transform3d tr;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
std::optional<EmbossVolume> m_unmodified_volume;
|
||||||
|
|
||||||
|
// True when m_text contain character unknown by selected font
|
||||||
|
bool m_text_contain_unknown_glyph = false;
|
||||||
|
|
||||||
|
// cancel for previous update of volume to cancel finalize part
|
||||||
|
std::shared_ptr<std::atomic<bool>> m_update_job_cancel;
|
||||||
|
|
||||||
|
// Rotation gizmo
|
||||||
|
GLGizmoRotate m_rotate_gizmo;
|
||||||
|
// Value is set only when dragging rotation to calculate actual angle
|
||||||
|
std::optional<float> m_rotate_start_angle;
|
||||||
|
|
||||||
|
// when draging with text object hold screen offset of cursor from object center
|
||||||
|
std::optional<Vec2d> m_dragging_mouse_offset;
|
||||||
|
|
||||||
|
// TODO: it should be accessible by other gizmo too.
|
||||||
|
// May be move to plater?
|
||||||
|
RaycastManager m_raycast_manager;
|
||||||
|
|
||||||
|
// Only when drag text object it stores world position
|
||||||
|
std::optional<Transform3d> m_temp_transformation;
|
||||||
|
|
||||||
|
// drawing icons
|
||||||
|
GLTexture m_icons_texture;
|
||||||
|
void init_icons();
|
||||||
|
enum class IconType : unsigned {
|
||||||
|
rename = 0,
|
||||||
|
erase,
|
||||||
|
add,
|
||||||
|
save,
|
||||||
|
undo,
|
||||||
|
italic,
|
||||||
|
unitalic,
|
||||||
|
bold,
|
||||||
|
unbold,
|
||||||
|
system_selector,
|
||||||
|
open_file,
|
||||||
|
// VolumeType icons
|
||||||
|
part,
|
||||||
|
negative,
|
||||||
|
modifier,
|
||||||
|
// automatic calc of icon's count
|
||||||
|
_count
|
||||||
|
};
|
||||||
|
enum class IconState: unsigned { activable = 0, hovered /*1*/, disabled /*2*/};
|
||||||
|
void draw_icon(IconType icon, IconState state, ImVec2 size = ImVec2(0,0));
|
||||||
|
void draw_transparent_icon();
|
||||||
|
bool draw_clickable(IconType icon, IconState state, IconType hover_icon, IconState hover_state);
|
||||||
|
bool draw_button(IconType icon, bool disable = false);
|
||||||
|
|
||||||
|
// only temporary solution
|
||||||
|
static const std::string M_ICON_FILENAME;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Check if text is last solid part of object
|
||||||
|
/// TODO: move to emboss gui utils
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Model volume of Text</param>
|
||||||
|
/// <returns>True when object otherwise False</returns>
|
||||||
|
static bool is_text_object(const ModelVolume *text);
|
||||||
|
|
||||||
|
// TODO: move to file utils
|
||||||
|
static std::string get_file_name(const std::string &file_path);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
|
#endif // slic3r_GLGizmoEmboss_hpp_
|
|
@ -295,7 +295,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||||
const float buttons_width = m_imgui->scaled(0.5f);
|
const float buttons_width = m_imgui->scaled(0.5f);
|
||||||
const float minimal_slider_width = m_imgui->scaled(4.f);
|
const float minimal_slider_width = m_imgui->scaled(4.f);
|
||||||
const float color_button_width = m_imgui->calc_text_size("").x + m_imgui->scaled(1.75f);
|
const float color_button_width = m_imgui->scaled(1.75f);
|
||||||
const float combo_label_width = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x,
|
const float combo_label_width = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x,
|
||||||
m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f);
|
m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f);
|
||||||
|
|
||||||
|
|
|
@ -220,8 +220,8 @@ void GLGizmoMove3D::on_render()
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
|
||||||
init_data.color = AXES_COLOR[id];
|
init_data.color = AXES_COLOR[id];
|
||||||
init_data.reserve_vertices(2);
|
init_data.vertices.reserve(2);
|
||||||
init_data.reserve_indices(2);
|
init_data.indices.reserve(2);
|
||||||
|
|
||||||
// vertices
|
// vertices
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
@ -484,4 +484,4 @@ void GLGizmoMove3D::calc_selection_box_and_center()
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
|
@ -1,88 +1,88 @@
|
||||||
#ifndef slic3r_GLGizmoMove_hpp_
|
#ifndef slic3r_GLGizmoMove_hpp_
|
||||||
#define slic3r_GLGizmoMove_hpp_
|
#define slic3r_GLGizmoMove_hpp_
|
||||||
|
|
||||||
#include "GLGizmoBase.hpp"
|
#include "GLGizmoBase.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
class Selection;
|
class Selection;
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
class GLGizmoMove3D : public GLGizmoBase
|
class GLGizmoMove3D : public GLGizmoBase
|
||||||
{
|
{
|
||||||
static const double Offset;
|
static const double Offset;
|
||||||
|
|
||||||
Vec3d m_displacement{ Vec3d::Zero() };
|
Vec3d m_displacement{ Vec3d::Zero() };
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
Vec3d m_center{ Vec3d::Zero() };
|
Vec3d m_center{ Vec3d::Zero() };
|
||||||
BoundingBoxf3 m_bounding_box;
|
BoundingBoxf3 m_bounding_box;
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
double m_snap_step{ 1.0 };
|
double m_snap_step{ 1.0 };
|
||||||
Vec3d m_starting_drag_position{ Vec3d::Zero() };
|
Vec3d m_starting_drag_position{ Vec3d::Zero() };
|
||||||
Vec3d m_starting_box_center{ Vec3d::Zero() };
|
Vec3d m_starting_box_center{ Vec3d::Zero() };
|
||||||
Vec3d m_starting_box_bottom_center{ Vec3d::Zero() };
|
Vec3d m_starting_box_bottom_center{ Vec3d::Zero() };
|
||||||
|
|
||||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
struct GrabberConnection
|
struct GrabberConnection
|
||||||
{
|
{
|
||||||
GLModel model;
|
GLModel model;
|
||||||
Vec3d old_center{ Vec3d::Zero() };
|
Vec3d old_center{ Vec3d::Zero() };
|
||||||
};
|
};
|
||||||
std::array<GrabberConnection, 3> m_grabber_connections;
|
std::array<GrabberConnection, 3> m_grabber_connections;
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||||
virtual ~GLGizmoMove3D() = default;
|
virtual ~GLGizmoMove3D() = default;
|
||||||
|
|
||||||
double get_snap_step(double step) const { return m_snap_step; }
|
double get_snap_step(double step) const { return m_snap_step; }
|
||||||
void set_snap_step(double step) { m_snap_step = step; }
|
void set_snap_step(double step) { m_snap_step = step; }
|
||||||
|
|
||||||
std::string get_tooltip() const override;
|
std::string get_tooltip() const override;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Postpone to Grabber for move
|
/// Postpone to Grabber for move
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||||
/// <returns>Return True when use the information otherwise False.</returns>
|
/// <returns>Return True when use the information otherwise False.</returns>
|
||||||
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Detect reduction of move for wipetover on selection change
|
/// Detect reduction of move for wipetover on selection change
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void data_changed() override;
|
void data_changed() override;
|
||||||
protected:
|
protected:
|
||||||
bool on_init() override;
|
bool on_init() override;
|
||||||
std::string on_get_name() const override;
|
std::string on_get_name() const override;
|
||||||
bool on_is_activable() const override;
|
bool on_is_activable() const override;
|
||||||
void on_start_dragging() override;
|
void on_start_dragging() override;
|
||||||
void on_stop_dragging() override;
|
void on_stop_dragging() override;
|
||||||
void on_dragging(const UpdateData& data) override;
|
void on_dragging(const UpdateData& data) override;
|
||||||
void on_render() override;
|
void on_render() override;
|
||||||
#if ENABLE_RAYCAST_PICKING
|
#if ENABLE_RAYCAST_PICKING
|
||||||
virtual void on_register_raycasters_for_picking() override;
|
virtual void on_register_raycasters_for_picking() override;
|
||||||
virtual void on_unregister_raycasters_for_picking() override;
|
virtual void on_unregister_raycasters_for_picking() override;
|
||||||
#else
|
#else
|
||||||
void on_render_for_picking() override;
|
void on_render_for_picking() override;
|
||||||
#endif // ENABLE_RAYCAST_PICKING
|
#endif // ENABLE_RAYCAST_PICKING
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double calc_projection(const UpdateData& data) const;
|
double calc_projection(const UpdateData& data) const;
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
Transform3d local_transform(const Selection& selection) const;
|
Transform3d local_transform(const Selection& selection) const;
|
||||||
#else
|
#else
|
||||||
void transform_to_local(const Selection& selection) const;
|
void transform_to_local(const Selection& selection) const;
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
void calc_selection_box_and_center();
|
void calc_selection_box_and_center();
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // slic3r_GLGizmoMove_hpp_
|
#endif // slic3r_GLGizmoMove_hpp_
|
|
@ -287,7 +287,8 @@ void GLGizmoRotate::on_render_for_picking()
|
||||||
void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
||||||
{
|
{
|
||||||
ECoordinatesType coordinates_type;
|
ECoordinatesType coordinates_type;
|
||||||
if (selection.is_wipe_tower())
|
if (m_using_local_coordinate ||
|
||||||
|
selection.is_wipe_tower())
|
||||||
coordinates_type = ECoordinatesType::Local;
|
coordinates_type = ECoordinatesType::Local;
|
||||||
else
|
else
|
||||||
coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
|
coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
|
||||||
|
|
|
@ -57,6 +57,9 @@ private:
|
||||||
float m_old_angle{ 0.0f };
|
float m_old_angle{ 0.0f };
|
||||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||||
|
|
||||||
|
// emboss need to draw rotation gizmo in local coordinate systems
|
||||||
|
bool m_using_local_coordinate{false};
|
||||||
|
|
||||||
ColorRGBA m_drag_color;
|
ColorRGBA m_drag_color;
|
||||||
ColorRGBA m_highlight_color;
|
ColorRGBA m_highlight_color;
|
||||||
|
|
||||||
|
@ -69,6 +72,9 @@ public:
|
||||||
|
|
||||||
std::string get_tooltip() const override;
|
std::string get_tooltip() const override;
|
||||||
|
|
||||||
|
void set_group_id(int group_id) { m_group_id = group_id; }
|
||||||
|
void set_using_local_coordinate(bool use) { m_using_local_coordinate =use;}
|
||||||
|
|
||||||
void start_dragging();
|
void start_dragging();
|
||||||
void stop_dragging();
|
void stop_dragging();
|
||||||
|
|
||||||
|
@ -230,4 +236,4 @@ private:
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // slic3r_GLGizmoRotate_hpp_
|
#endif // slic3r_GLGizmoRotate_hpp_
|
|
@ -937,4 +937,4 @@ void GLGizmoScale3D::transform_to_local(const Selection& selection) const
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
|
@ -125,4 +125,4 @@ private:
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // slic3r_GLGizmoScale_hpp_
|
#endif // slic3r_GLGizmoScale_hpp_
|
|
@ -95,10 +95,8 @@ static std::string create_volumes_name(const std::set<ObjectID>& ids, const Sele
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
|
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D &parent)
|
||||||
const std::string &icon_filename,
|
: GLGizmoBase(parent, M_ICON_FILENAME, -1)
|
||||||
unsigned int sprite_id)
|
|
||||||
: GLGizmoBase(parent, icon_filename, -1)
|
|
||||||
, m_show_wireframe(false)
|
, m_show_wireframe(false)
|
||||||
, m_move_to_center(false)
|
, m_move_to_center(false)
|
||||||
, m_original_triangle_count(0)
|
, m_original_triangle_count(0)
|
||||||
|
@ -588,7 +586,7 @@ void GLGizmoSimplify::on_set_state()
|
||||||
|
|
||||||
void GLGizmoSimplify::create_gui_cfg() {
|
void GLGizmoSimplify::create_gui_cfg() {
|
||||||
if (m_gui_cfg.has_value()) return;
|
if (m_gui_cfg.has_value()) return;
|
||||||
int space_size = m_imgui->calc_text_size(":MM").x;
|
int space_size = m_imgui->calc_text_size(std::string_view{":MM"}).x;
|
||||||
GuiCfg cfg;
|
GuiCfg cfg;
|
||||||
cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x,
|
cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x,
|
||||||
m_imgui->calc_text_size(tr_triangles).x)
|
m_imgui->calc_text_size(tr_triangles).x)
|
||||||
|
@ -844,4 +842,7 @@ void GLGizmoSimplify::Configuration::fix_count_by_ratio(size_t triangle_count)
|
||||||
triangle_count * (100.f - decimate_ratio) / 100.f));
|
triangle_count * (100.f - decimate_ratio) / 100.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// any existing icon filename to not influence GUI
|
||||||
|
const std::string GLGizmoSimplify::M_ICON_FILENAME = "cut.svg";
|
||||||
|
|
||||||
} // namespace Slic3r::GUI
|
} // namespace Slic3r::GUI
|
||||||
|
|
|
@ -20,7 +20,7 @@ class NotificationManager; // for simplify suggestion
|
||||||
class GLGizmoSimplify: public GLGizmoBase
|
class GLGizmoSimplify: public GLGizmoBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoSimplify(GLCanvas3D& parent);
|
||||||
virtual ~GLGizmoSimplify();
|
virtual ~GLGizmoSimplify();
|
||||||
bool on_esc_key_down();
|
bool on_esc_key_down();
|
||||||
static void add_simplify_suggestion_notification(
|
static void add_simplify_suggestion_notification(
|
||||||
|
@ -155,6 +155,9 @@ private:
|
||||||
return L("Model simplification has been canceled");
|
return L("Model simplification has been canceled");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// only temporary solution
|
||||||
|
static const std::string M_ICON_FILENAME;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||||
|
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
||||||
|
|
||||||
#include "libslic3r/format.hpp"
|
#include "libslic3r/format.hpp"
|
||||||
|
@ -106,8 +107,9 @@ bool GLGizmosManager::init()
|
||||||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
|
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
|
||||||
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
|
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
|
||||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9));
|
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9));
|
||||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10));
|
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 10));
|
||||||
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11));
|
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent));
|
||||||
|
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent));
|
||||||
|
|
||||||
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
||||||
|
|
||||||
|
@ -1064,9 +1066,12 @@ bool GLGizmosManager::generate_icons_texture()
|
||||||
{
|
{
|
||||||
std::string path = resources_dir() + "/icons/";
|
std::string path = resources_dir() + "/icons/";
|
||||||
std::vector<std::string> filenames;
|
std::vector<std::string> filenames;
|
||||||
for (size_t idx = 0; idx<m_gizmos.size(); ++idx) {
|
for (size_t idx=0; idx<m_gizmos.size(); ++idx)
|
||||||
if (m_gizmos[idx] != nullptr) {
|
{
|
||||||
const std::string& icon_filename = m_gizmos[idx]->get_icon_filename();
|
auto &gizmo = m_gizmos[idx];
|
||||||
|
if (gizmo != nullptr)
|
||||||
|
{
|
||||||
|
const std::string& icon_filename = gizmo->get_icon_filename();
|
||||||
if (!icon_filename.empty())
|
if (!icon_filename.empty())
|
||||||
filenames.push_back(path + icon_filename);
|
filenames.push_back(path + icon_filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,8 +79,9 @@ public:
|
||||||
FdmSupports,
|
FdmSupports,
|
||||||
Seam,
|
Seam,
|
||||||
MmuSegmentation,
|
MmuSegmentation,
|
||||||
Simplify,
|
|
||||||
Measure,
|
Measure,
|
||||||
|
Emboss,
|
||||||
|
Simplify,
|
||||||
Undefined
|
Undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
#include <nanosvg/nanosvg.h>
|
#include <nanosvg/nanosvg.h>
|
||||||
#include <nanosvg/nanosvgrast.h>
|
#include <nanosvg/nanosvgrast.h>
|
||||||
|
|
||||||
|
// suggest location
|
||||||
|
#include "libslic3r/ClipperUtils.hpp" // Slic3r::intersection
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
@ -300,10 +303,27 @@ void ImGuiWrapper::render()
|
||||||
m_new_frame_open = false;
|
m_new_frame_open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const
|
ImVec2 ImGuiWrapper::calc_text_size(std::string_view text,
|
||||||
|
bool hide_text_after_double_hash,
|
||||||
|
float wrap_width)
|
||||||
|
{
|
||||||
|
return ImGui::CalcTextSize(text.data(), text.data() + text.length(),
|
||||||
|
hide_text_after_double_hash, wrap_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiWrapper::calc_text_size(const std::string& text,
|
||||||
|
bool hide_text_after_double_hash,
|
||||||
|
float wrap_width)
|
||||||
|
{
|
||||||
|
return ImGui::CalcTextSize(text.c_str(), NULL, hide_text_after_double_hash, wrap_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text,
|
||||||
|
bool hide_text_after_double_hash,
|
||||||
|
float wrap_width)
|
||||||
{
|
{
|
||||||
auto text_utf8 = into_u8(text);
|
auto text_utf8 = into_u8(text);
|
||||||
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width);
|
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, hide_text_after_double_hash, wrap_width);
|
||||||
|
|
||||||
/*#ifdef __linux__
|
/*#ifdef __linux__
|
||||||
size.x *= m_style_scaling;
|
size.x *= m_style_scaling;
|
||||||
|
@ -389,6 +409,18 @@ bool ImGuiWrapper::button(const wxString& label, float width, float height)
|
||||||
return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
|
return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::button(const wxString& label, const ImVec2 &size, bool enable)
|
||||||
|
{
|
||||||
|
disabled_begin(!enable);
|
||||||
|
|
||||||
|
auto label_utf8 = into_u8(label);
|
||||||
|
bool res = ImGui::Button(label_utf8.c_str(), size);
|
||||||
|
|
||||||
|
disabled_end();
|
||||||
|
return (enable) ? res : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
||||||
{
|
{
|
||||||
auto label_utf8 = into_u8(label);
|
auto label_utf8 = into_u8(label);
|
||||||
|
@ -442,13 +474,13 @@ void ImGuiWrapper::text(const char *label)
|
||||||
|
|
||||||
void ImGuiWrapper::text(const std::string &label)
|
void ImGuiWrapper::text(const std::string &label)
|
||||||
{
|
{
|
||||||
this->text(label.c_str());
|
ImGuiWrapper::text(label.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiWrapper::text(const wxString &label)
|
void ImGuiWrapper::text(const wxString &label)
|
||||||
{
|
{
|
||||||
auto label_utf8 = into_u8(label);
|
auto label_utf8 = into_u8(label);
|
||||||
this->text(label_utf8.c_str());
|
ImGuiWrapper::text(label_utf8.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
||||||
|
@ -458,13 +490,13 @@ void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
||||||
|
|
||||||
void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label)
|
void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label)
|
||||||
{
|
{
|
||||||
this->text_colored(color, label.c_str());
|
ImGuiWrapper::text_colored(color, label.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
|
void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
|
||||||
{
|
{
|
||||||
auto label_utf8 = into_u8(label);
|
auto label_utf8 = into_u8(label);
|
||||||
this->text_colored(color, label_utf8.c_str());
|
ImGuiWrapper::text_colored(color, label_utf8.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiWrapper::text_wrapped(const char *label, float wrap_width)
|
void ImGuiWrapper::text_wrapped(const char *label, float wrap_width)
|
||||||
|
@ -1152,6 +1184,307 @@ ColorRGBA ImGuiWrapper::from_ImVec4(const ImVec4& color)
|
||||||
return { color.x, color.y, color.z, color.w };
|
return { color.x, color.y, color.z, color.w };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Func>
|
||||||
|
static bool input_optional(std::optional<T> &v, Func& f, std::function<bool(const T&)> is_default, const T& def_val)
|
||||||
|
{
|
||||||
|
if (v.has_value()) {
|
||||||
|
if (f(*v)) {
|
||||||
|
if (is_default(*v)) v.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
T val = def_val;
|
||||||
|
if (f(val)) {
|
||||||
|
if (!is_default(val)) v = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::input_optional_int(const char * label,
|
||||||
|
std::optional<int>& v,
|
||||||
|
int step,
|
||||||
|
int step_fast,
|
||||||
|
ImGuiInputTextFlags flags,
|
||||||
|
int def_val)
|
||||||
|
{
|
||||||
|
auto func = [&](int &value) {
|
||||||
|
return ImGui::InputInt(label, &value, step, step_fast, flags);
|
||||||
|
};
|
||||||
|
std::function<bool(const int &)> is_default =
|
||||||
|
[def_val](const int &value) -> bool { return value == def_val; };
|
||||||
|
return input_optional(v, func, is_default, def_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::input_optional_float(const char * label,
|
||||||
|
std::optional<float> &v,
|
||||||
|
float step,
|
||||||
|
float step_fast,
|
||||||
|
const char * format,
|
||||||
|
ImGuiInputTextFlags flags,
|
||||||
|
float def_val)
|
||||||
|
{
|
||||||
|
auto func = [&](float &value) {
|
||||||
|
return ImGui::InputFloat(label, &value, step, step_fast, format, flags);
|
||||||
|
};
|
||||||
|
std::function<bool(const float &)> is_default =
|
||||||
|
[def_val](const float &value) -> bool {
|
||||||
|
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
|
||||||
|
};
|
||||||
|
return input_optional(v, func, is_default, def_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::drag_optional_float(const char * label,
|
||||||
|
std::optional<float> &v,
|
||||||
|
float v_speed,
|
||||||
|
float v_min,
|
||||||
|
float v_max,
|
||||||
|
const char * format,
|
||||||
|
float power,
|
||||||
|
float def_val)
|
||||||
|
{
|
||||||
|
auto func = [&](float &value) {
|
||||||
|
return ImGui::DragFloat(label, &value, v_speed, v_min, v_max, format, power);
|
||||||
|
};
|
||||||
|
std::function<bool(const float &)> is_default =
|
||||||
|
[def_val](const float &value) -> bool {
|
||||||
|
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
|
||||||
|
};
|
||||||
|
return input_optional(v, func, is_default, def_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::slider_optional_float(const char *label,
|
||||||
|
std::optional<float> &v,
|
||||||
|
float v_min,
|
||||||
|
float v_max,
|
||||||
|
const char *format,
|
||||||
|
float power,
|
||||||
|
bool clamp,
|
||||||
|
const wxString &tooltip,
|
||||||
|
bool show_edit_btn,
|
||||||
|
float def_val)
|
||||||
|
{
|
||||||
|
auto func = [&](float &value) {
|
||||||
|
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||||
|
};
|
||||||
|
std::function<bool(const float &)> is_default =
|
||||||
|
[def_val](const float &value) -> bool {
|
||||||
|
return std::fabs(value - def_val) <= std::numeric_limits<float>::epsilon();
|
||||||
|
};
|
||||||
|
return input_optional(v, func, is_default, def_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::slider_optional_int(const char *label,
|
||||||
|
std::optional<int> &v,
|
||||||
|
int v_min,
|
||||||
|
int v_max,
|
||||||
|
const char *format,
|
||||||
|
float power,
|
||||||
|
bool clamp,
|
||||||
|
const wxString &tooltip,
|
||||||
|
bool show_edit_btn,
|
||||||
|
int def_val)
|
||||||
|
{
|
||||||
|
std::optional<float> val;
|
||||||
|
if (v.has_value()) val = static_cast<float>(*v);
|
||||||
|
auto func = [&](float &value) {
|
||||||
|
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
|
||||||
|
};
|
||||||
|
std::function<bool(const float &)> is_default =
|
||||||
|
[def_val](const float &value) -> bool {
|
||||||
|
return std::fabs(value - def_val) < 0.9f;
|
||||||
|
};
|
||||||
|
|
||||||
|
float default_value = static_cast<float>(def_val);
|
||||||
|
if (input_optional(val, func, is_default, default_value)) {
|
||||||
|
if (val.has_value())
|
||||||
|
v = static_cast<int>(std::round(*val));
|
||||||
|
else
|
||||||
|
v.reset();
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ImGuiWrapper::trunc(const std::string &text,
|
||||||
|
float width,
|
||||||
|
const char * tail)
|
||||||
|
{
|
||||||
|
float text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||||
|
if (text_width < width) return text;
|
||||||
|
float tail_width = ImGui::CalcTextSize(tail).x;
|
||||||
|
assert(width > tail_width);
|
||||||
|
if (width <= tail_width) return "Error: Can't add tail and not be under wanted width.";
|
||||||
|
float allowed_width = width - tail_width;
|
||||||
|
|
||||||
|
// guess approx count of letter
|
||||||
|
float average_letter_width = calc_text_size(std::string_view("n")).x; // average letter width
|
||||||
|
unsigned count_letter = static_cast<unsigned>(allowed_width / average_letter_width);
|
||||||
|
|
||||||
|
std::string_view text_ = text;
|
||||||
|
std::string_view result_text = text_.substr(0, count_letter);
|
||||||
|
text_width = calc_text_size(result_text).x;
|
||||||
|
if (text_width < allowed_width) {
|
||||||
|
// increase letter count
|
||||||
|
while (count_letter < text.length()) {
|
||||||
|
++count_letter;
|
||||||
|
std::string_view act_text = text_.substr(0, count_letter);
|
||||||
|
text_width = calc_text_size(act_text).x;
|
||||||
|
if (text_width > allowed_width) break;
|
||||||
|
result_text = act_text;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// decrease letter count
|
||||||
|
while (count_letter > 1) {
|
||||||
|
--count_letter;
|
||||||
|
result_text = text_.substr(0, count_letter);
|
||||||
|
text_width = calc_text_size(result_text).x;
|
||||||
|
if (text_width < allowed_width) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::string(result_text) + tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiWrapper::escape_double_hash(std::string &text)
|
||||||
|
{
|
||||||
|
// add space between hashes
|
||||||
|
const std::string search = "##";
|
||||||
|
const std::string replace = "# #";
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = text.find(search, pos)) != std::string::npos)
|
||||||
|
text.replace(pos, search.length(), replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiWrapper::suggest_location(const ImVec2 &dialog_size,
|
||||||
|
const Slic3r::Polygon &interest,
|
||||||
|
const ImVec2 &canvas_size)
|
||||||
|
{
|
||||||
|
// IMPROVE 1: do not select place over menu
|
||||||
|
// BoundingBox top_menu;
|
||||||
|
// GLGizmosManager &gizmo_mng = canvas->get_gizmos_manager();
|
||||||
|
// BoundingBox side_menu; // gizmo_mng.get_size();
|
||||||
|
// BoundingBox left_bottom_menu; // is permanent?
|
||||||
|
// NotificationManager *notify_mng = plater->get_notification_manager();
|
||||||
|
// BoundingBox notifications; // notify_mng->get_size();
|
||||||
|
// m_window_width, m_window_height + position
|
||||||
|
|
||||||
|
// IMPROVE 2: use polygon of interest not only bounding box
|
||||||
|
BoundingBox bb(interest.points);
|
||||||
|
Point center = bb.center(); // interest.centroid();
|
||||||
|
|
||||||
|
// area size
|
||||||
|
Point window_center(canvas_size.x / 2, canvas_size.y / 2);
|
||||||
|
|
||||||
|
// mov on side
|
||||||
|
Point bb_half_size = (bb.max - bb.min) / 2 + Point(1,1);
|
||||||
|
Point diff_center = window_center - center;
|
||||||
|
Vec2d diff_norm(diff_center.x() / (double) bb_half_size.x(),
|
||||||
|
diff_center.y() / (double) bb_half_size.y());
|
||||||
|
if (diff_norm.x() > 1.) diff_norm.x() = 1.;
|
||||||
|
if (diff_norm.x() < -1.) diff_norm.x() = -1.;
|
||||||
|
if (diff_norm.y() > 1.) diff_norm.y() = 1.;
|
||||||
|
if (diff_norm.y() < -1.) diff_norm.y() = -1.;
|
||||||
|
|
||||||
|
Vec2d abs_diff(abs(diff_norm.x()), abs(diff_norm.y()));
|
||||||
|
if (abs_diff.x() < 1. && abs_diff.y() < 1.) {
|
||||||
|
if (abs_diff.x() > abs_diff.y())
|
||||||
|
diff_norm.x() = (diff_norm.x() < 0.) ? (-1.) : 1.;
|
||||||
|
else
|
||||||
|
diff_norm.y() = (diff_norm.y() < 0.) ? (-1.) : 1.;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point half_dialog_size(dialog_size.x / 2., dialog_size.y / 2.);
|
||||||
|
Point move_size = bb_half_size + half_dialog_size;
|
||||||
|
Point offseted_center = center - half_dialog_size;
|
||||||
|
Vec2d offset(offseted_center.x() + diff_norm.x() * move_size.x(),
|
||||||
|
offseted_center.y() + diff_norm.y() * move_size.y());
|
||||||
|
|
||||||
|
// move offset close to center
|
||||||
|
Points window_polygon = {offset.cast<int>(),
|
||||||
|
Point(offset.x(), offset.y() + dialog_size.y),
|
||||||
|
Point(offset.x() + dialog_size.x,
|
||||||
|
offset.y() + dialog_size.y),
|
||||||
|
Point(offset.x() + dialog_size.x, offset.y())};
|
||||||
|
// check that position by Bounding box is not intersecting
|
||||||
|
assert(Slic3r::intersection(interest, Polygon(window_polygon)).empty());
|
||||||
|
|
||||||
|
double allowed_space = 10; // in px
|
||||||
|
double allowed_space_sq = allowed_space * allowed_space;
|
||||||
|
Vec2d move_vec = (center - (offset.cast<int>() + half_dialog_size))
|
||||||
|
.cast<double>();
|
||||||
|
Vec2d result_move(0, 0);
|
||||||
|
do {
|
||||||
|
move_vec = move_vec / 2.;
|
||||||
|
Point move_point = (move_vec + result_move).cast<int>();
|
||||||
|
Points moved_polygon = window_polygon; // copy
|
||||||
|
for (Point &p : moved_polygon) p += move_point;
|
||||||
|
if (Slic3r::intersection(interest, Polygon(moved_polygon)).empty())
|
||||||
|
result_move += move_vec;
|
||||||
|
|
||||||
|
} while (move_vec.squaredNorm() >= allowed_space_sq);
|
||||||
|
offset += result_move;
|
||||||
|
|
||||||
|
return ImVec2(offset.x(), offset.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiWrapper::draw(
|
||||||
|
const Polygon &polygon,
|
||||||
|
ImDrawList * draw_list /* = ImGui::GetOverlayDrawList()*/,
|
||||||
|
ImU32 color /* = ImGui::GetColorU32(COL_ORANGE_LIGHT)*/,
|
||||||
|
float thickness /* = 3.f*/)
|
||||||
|
{
|
||||||
|
// minimal one line consist of 2 points
|
||||||
|
if (polygon.size() < 2) return;
|
||||||
|
// need a place to draw
|
||||||
|
if (draw_list == nullptr) return;
|
||||||
|
|
||||||
|
const Point *prev_point = &polygon.points.back();
|
||||||
|
for (const Point &point : polygon.points) {
|
||||||
|
ImVec2 p1(prev_point->x(), prev_point->y());
|
||||||
|
ImVec2 p2(point.x(), point.y());
|
||||||
|
draw_list->AddLine(p1, p2, color, thickness);
|
||||||
|
prev_point = &point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
|
||||||
|
const std::string &text)
|
||||||
|
{
|
||||||
|
if (font == nullptr) return false;
|
||||||
|
if (!font->IsLoaded()) return false;
|
||||||
|
const ImFontConfig *fc = font->ConfigData;
|
||||||
|
if (fc == nullptr) return false;
|
||||||
|
if (text.empty()) return true;
|
||||||
|
return is_chars_in_ranges(fc->GlyphRanges, text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiWrapper::is_char_in_ranges(const ImWchar *ranges,
|
||||||
|
unsigned int letter)
|
||||||
|
{
|
||||||
|
for (const ImWchar *range = ranges; range[0] && range[1]; range += 2) {
|
||||||
|
ImWchar from = range[0];
|
||||||
|
ImWchar to = range[1];
|
||||||
|
if (from <= letter && letter <= to) return true;
|
||||||
|
if (letter < to) return false; // ranges should be sorted
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ImGuiWrapper::is_chars_in_ranges(const ImWchar *ranges,
|
||||||
|
const char *chars_ptr)
|
||||||
|
{
|
||||||
|
while (*chars_ptr) {
|
||||||
|
unsigned int c = 0;
|
||||||
|
// UTF-8 to 32-bit character need imgui_internal
|
||||||
|
int c_len = ImTextCharFromUtf8(&c, chars_ptr, NULL);
|
||||||
|
chars_ptr += c_len;
|
||||||
|
if (c_len == 0) break;
|
||||||
|
if (!is_char_in_ranges(ranges, c)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
static const ImWchar ranges_keyboard_shortcuts[] =
|
static const ImWchar ranges_keyboard_shortcuts[] =
|
||||||
{
|
{
|
||||||
|
@ -1206,7 +1539,7 @@ void ImGuiWrapper::init_font(bool compress)
|
||||||
|
|
||||||
// Create ranges of characters from m_glyph_ranges, possibly adding some OS specific special characters.
|
// Create ranges of characters from m_glyph_ranges, possibly adding some OS specific special characters.
|
||||||
ImVector<ImWchar> ranges;
|
ImVector<ImWchar> ranges;
|
||||||
ImFontAtlas::GlyphRangesBuilder builder;
|
ImFontGlyphRangesBuilder builder;
|
||||||
builder.AddRanges(m_glyph_ranges);
|
builder.AddRanges(m_glyph_ranges);
|
||||||
|
|
||||||
if (m_font_cjk) {
|
if (m_font_cjk) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define slic3r_ImGuiWrapper_hpp_
|
#define slic3r_ImGuiWrapper_hpp_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/Color.hpp"
|
#include "libslic3r/Color.hpp"
|
||||||
|
#include "libslic3r/Polygon.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace Search {
|
namespace Search {
|
||||||
|
@ -52,8 +54,6 @@ public:
|
||||||
ImGuiWrapper();
|
ImGuiWrapper();
|
||||||
~ImGuiWrapper();
|
~ImGuiWrapper();
|
||||||
|
|
||||||
void read_glsl_version();
|
|
||||||
|
|
||||||
void set_language(const std::string &language);
|
void set_language(const std::string &language);
|
||||||
void set_display_size(float w, float h);
|
void set_display_size(float w, float h);
|
||||||
void set_scaling(float font_size, float scale_style, float scale_both);
|
void set_scaling(float font_size, float scale_style, float scale_both);
|
||||||
|
@ -62,13 +62,19 @@ public:
|
||||||
|
|
||||||
float get_font_size() const { return m_font_size; }
|
float get_font_size() const { return m_font_size; }
|
||||||
float get_style_scaling() const { return m_style_scaling; }
|
float get_style_scaling() const { return m_style_scaling; }
|
||||||
|
const ImWchar *get_glyph_ranges() const { return m_glyph_ranges; } // language specific
|
||||||
|
|
||||||
void new_frame();
|
void new_frame();
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
float scaled(float x) const { return x * m_font_size; }
|
float scaled(float x) const { return x * m_font_size; }
|
||||||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||||
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const;
|
/// <summary>
|
||||||
|
/// Extend ImGui::CalcTextSize to use string_view
|
||||||
|
/// </summary>
|
||||||
|
static ImVec2 calc_text_size(std::string_view text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||||
|
static ImVec2 calc_text_size(const std::string& text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||||
|
static ImVec2 calc_text_size(const wxString &text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
|
||||||
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
|
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
|
||||||
|
|
||||||
ImVec2 get_item_spacing() const;
|
ImVec2 get_item_spacing() const;
|
||||||
|
@ -87,15 +93,16 @@ public:
|
||||||
|
|
||||||
bool button(const wxString &label);
|
bool button(const wxString &label);
|
||||||
bool button(const wxString& label, float width, float height);
|
bool button(const wxString& label, float width, float height);
|
||||||
|
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
|
||||||
bool radio_button(const wxString &label, bool active);
|
bool radio_button(const wxString &label, bool active);
|
||||||
bool draw_radio_button(const std::string& name, float size, bool active, std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback);
|
bool draw_radio_button(const std::string& name, float size, bool active, std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback);
|
||||||
bool checkbox(const wxString &label, bool &value);
|
bool checkbox(const wxString &label, bool &value);
|
||||||
void text(const char *label);
|
static void text(const char *label);
|
||||||
void text(const std::string &label);
|
static void text(const std::string &label);
|
||||||
void text(const wxString &label);
|
static void text(const wxString &label);
|
||||||
void text_colored(const ImVec4& color, const char* label);
|
static void text_colored(const ImVec4& color, const char* label);
|
||||||
void text_colored(const ImVec4& color, const std::string& label);
|
static void text_colored(const ImVec4& color, const std::string& label);
|
||||||
void text_colored(const ImVec4& color, const wxString& label);
|
static void text_colored(const ImVec4& color, const wxString& label);
|
||||||
void text_wrapped(const char *label, float wrap_width);
|
void text_wrapped(const char *label, float wrap_width);
|
||||||
void text_wrapped(const std::string &label, float wrap_width);
|
void text_wrapped(const std::string &label, float wrap_width);
|
||||||
void text_wrapped(const wxString &label, float wrap_width);
|
void text_wrapped(const wxString &label, float wrap_width);
|
||||||
|
@ -126,6 +133,77 @@ public:
|
||||||
bool want_text_input() const;
|
bool want_text_input() const;
|
||||||
bool want_any_input() const;
|
bool want_any_input() const;
|
||||||
|
|
||||||
|
// Optional inputs are used for set up value inside of an optional, with default value
|
||||||
|
//
|
||||||
|
// Extended function ImGui::InputInt to work with std::optional<int>, when value == def_val optional is released.
|
||||||
|
static bool input_optional_int(const char *label, std::optional<int>& v, int step=1, int step_fast=100, ImGuiInputTextFlags flags=0, int def_val = 0);
|
||||||
|
// Extended function ImGui::InputFloat to work with std::optional<float> value near def_val cause release of optional
|
||||||
|
static bool input_optional_float(const char* label, std::optional<float> &v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0, float def_val = .0f);
|
||||||
|
// Extended function ImGui::DragFloat to work with std::optional<float> value near def_val cause release of optional
|
||||||
|
static bool drag_optional_float(const char* label, std::optional<float> &v, float v_speed, float v_min, float v_max, const char* format, float power, float def_val = .0f);
|
||||||
|
// Extended function ImGuiWrapper::slider_float to work with std::optional<float> value near def_val cause release of optional
|
||||||
|
bool slider_optional_float(const char* label, std::optional<float> &v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, float def_val = .0f);
|
||||||
|
// Extended function ImGuiWrapper::slider_float to work with std::optional<int>, when value == def_val than optional release its value
|
||||||
|
bool slider_optional_int(const char* label, std::optional<int> &v, int v_min, int v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true, int def_val = 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Truncate text by ImGui draw function to specific width
|
||||||
|
/// NOTE 1: ImGui must be initialized
|
||||||
|
/// NOTE 2: Calculation for actual acive imgui font
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Text to be truncated</param>
|
||||||
|
/// <param name="width">Maximal width before truncate</param>
|
||||||
|
/// <param name="tail">String puted on end of text to be visible truncation</param>
|
||||||
|
/// <returns>Truncated text</returns>
|
||||||
|
static std::string trunc(const std::string &text,
|
||||||
|
float width,
|
||||||
|
const char *tail = " ..");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Escape ## in data by add space between hashes
|
||||||
|
/// Needed when user written text is visualized by ImGui.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">In/Out text to be escaped</param>
|
||||||
|
static void escape_double_hash(std::string &text);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suggest loacation of dialog window,
|
||||||
|
/// dependent on actual visible thing on platter
|
||||||
|
/// like Gizmo menu size, notifications, ...
|
||||||
|
/// To be near of polygon interest and not over it.
|
||||||
|
/// And also not out of visible area.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dialog_size">Define width and height of diaog window</param>
|
||||||
|
/// <param name="interest">Area of interest. Result should be close to it</param>
|
||||||
|
/// <param name="canvas_size">Available space a.k.a GLCanvas3D::get_current_canvas3D()</param>
|
||||||
|
/// <returns>Suggestion for dialog offest</returns>
|
||||||
|
static ImVec2 suggest_location(const ImVec2 &dialog_size,
|
||||||
|
const Slic3r::Polygon &interest,
|
||||||
|
const ImVec2 &canvas_size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Visualization of polygon
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="polygon">Define what to draw</param>
|
||||||
|
/// <param name="draw_list">Define where to draw it</param>
|
||||||
|
/// <param name="color">Color of polygon</param>
|
||||||
|
/// <param name="thickness">Width of polygon line</param>
|
||||||
|
static void draw(const Polygon &polygon,
|
||||||
|
ImDrawList * draw_list = ImGui::GetOverlayDrawList(),
|
||||||
|
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
||||||
|
float thickness = 3.f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check that font ranges contain all chars in string
|
||||||
|
/// (rendered Unicodes are stored in GlyphRanges)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">Contain glyph ranges</param>
|
||||||
|
/// <param name="text">Vector of character to check</param>
|
||||||
|
/// <returns>True when all glyphs from text are in font ranges</returns>
|
||||||
|
static bool contain_all_glyphs(const ImFont *font, const std::string &text);
|
||||||
|
static bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr);
|
||||||
|
static bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
|
||||||
|
|
||||||
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
||||||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||||
|
|
144
src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
Normal file
144
src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#include "CreateFontNameImageJob.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/Emboss.hpp"
|
||||||
|
// rasterization of ExPoly
|
||||||
|
#include "libslic3r/SLA/AGGRaster.hpp"
|
||||||
|
|
||||||
|
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||||
|
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||||
|
|
||||||
|
// ability to request new frame after finish rendering
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
|
|
||||||
|
#include "wx/fontenum.h"
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
using namespace Slic3r::GUI;
|
||||||
|
|
||||||
|
CreateFontImageJob::CreateFontImageJob(FontImageData &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(!m_input.text.empty());
|
||||||
|
assert(wxFontEnumerator::IsValidFacename(m_input.font_name));
|
||||||
|
assert(m_input.gray_level > 0 && m_input.gray_level < 255);
|
||||||
|
assert(m_input.texture_id != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateFontImageJob::process(Ctl &ctl)
|
||||||
|
{
|
||||||
|
if (!wxFontEnumerator::IsValidFacename(m_input.font_name)) return;
|
||||||
|
// Select font
|
||||||
|
wxFont wx_font(
|
||||||
|
wxFontInfo().FaceName(m_input.font_name).Encoding(m_input.encoding));
|
||||||
|
if (!wx_font.IsOk()) return;
|
||||||
|
|
||||||
|
std::unique_ptr<Emboss::FontFile> font_file =
|
||||||
|
WxFontUtils::create_font_file(wx_font);
|
||||||
|
if (font_file == nullptr) return;
|
||||||
|
|
||||||
|
Emboss::FontFileWithCache font_file_with_cache(std::move(font_file));
|
||||||
|
FontProp fp;
|
||||||
|
// use only first line of text
|
||||||
|
std::string text = m_input.text;
|
||||||
|
size_t enter_pos = text.find('\n');
|
||||||
|
if (enter_pos < text.size()) {
|
||||||
|
// text start with enter
|
||||||
|
if (enter_pos == 0) return;
|
||||||
|
// exist enter, soo delete all after enter
|
||||||
|
text = text.substr(0, enter_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<bool()> was_canceled = [&ctl, cancel = m_input.cancel]() -> bool {
|
||||||
|
if (ctl.was_canceled()) return true;
|
||||||
|
if (cancel->load()) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ExPolygons shapes = Emboss::text2shapes(font_file_with_cache, text.c_str(), fp, was_canceled);
|
||||||
|
// normalize height of font
|
||||||
|
BoundingBox bounding_box;
|
||||||
|
for (ExPolygon &shape : shapes)
|
||||||
|
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||||
|
if (bounding_box.size().x() < 1 || bounding_box.size().y() < 1) return;
|
||||||
|
double scale = m_input.size.y() / (double) bounding_box.size().y();
|
||||||
|
BoundingBoxf bb2(bounding_box.min.cast<double>(),
|
||||||
|
bounding_box.max.cast<double>());
|
||||||
|
bb2.scale(scale);
|
||||||
|
Vec2d size_f = bb2.size();
|
||||||
|
m_tex_size = Point(std::ceil(size_f.x()), std::ceil(size_f.y()));
|
||||||
|
// crop image width
|
||||||
|
if (m_tex_size.x() > m_input.size.x()) m_tex_size.x() = m_input.size.x();
|
||||||
|
if (m_tex_size.y() > m_input.size.y()) m_tex_size.y() = m_input.size.y();
|
||||||
|
|
||||||
|
// Set up result
|
||||||
|
m_result = std::vector<unsigned char>(m_tex_size.x() * m_tex_size.y() * 4, {255});
|
||||||
|
|
||||||
|
sla::Resolution resolution(m_tex_size.x(), m_tex_size.y());
|
||||||
|
double pixel_dim = SCALING_FACTOR / scale;
|
||||||
|
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||||
|
double gamma = 1.;
|
||||||
|
std::unique_ptr<sla::RasterBase> r =
|
||||||
|
sla::create_raster_grayscale_aa(resolution, dim, gamma);
|
||||||
|
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||||
|
for (const ExPolygon &shape : shapes) r->draw(shape);
|
||||||
|
|
||||||
|
// copy rastered data to pixels
|
||||||
|
sla::RasterEncoder encoder =
|
||||||
|
[&pix = m_result, w = m_tex_size.x(), h = m_tex_size.y(),
|
||||||
|
gray_level = m_input.gray_level]
|
||||||
|
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||||
|
size_t size {static_cast<size_t>(w*h)};
|
||||||
|
const unsigned char *ptr2 = (const unsigned char *) ptr;
|
||||||
|
for (size_t x = 0; x < width; ++x)
|
||||||
|
for (size_t y = 0; y < height; ++y) {
|
||||||
|
size_t index = y*w + x;
|
||||||
|
assert(index < size);
|
||||||
|
if (index >= size) continue;
|
||||||
|
pix[3+4*index] = ptr2[y * width + x] / gray_level;
|
||||||
|
}
|
||||||
|
return sla::EncodedRaster();
|
||||||
|
};
|
||||||
|
r->encode(encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateFontImageJob::finalize(bool canceled, std::exception_ptr &)
|
||||||
|
{
|
||||||
|
if (m_input.count_opened_font_files)
|
||||||
|
--(*m_input.count_opened_font_files);
|
||||||
|
if (canceled || m_input.cancel->load()) return;
|
||||||
|
|
||||||
|
*m_input.is_created = true;
|
||||||
|
|
||||||
|
// Exist result bitmap with preview?
|
||||||
|
// (not valid input. e.g. not loadable font)
|
||||||
|
if (m_result.empty()) {
|
||||||
|
// TODO: write text cannot load into texture
|
||||||
|
m_result = std::vector<unsigned char>(m_tex_size.x() * m_tex_size.y() * 4, {255});
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload texture on GPU
|
||||||
|
const GLenum target = GL_TEXTURE_2D;
|
||||||
|
glsafe(::glBindTexture(target, m_input.texture_id));
|
||||||
|
|
||||||
|
GLint
|
||||||
|
w = m_tex_size.x(), h = m_tex_size.y(),
|
||||||
|
xoffset = m_input.size.x() - m_tex_size.x(), // arrange right
|
||||||
|
yoffset = m_input.size.y() * m_input.index;
|
||||||
|
glsafe(::glTexSubImage2D(target, m_input.level, xoffset, yoffset, w, h,
|
||||||
|
m_input.format, m_input.type, m_result.data()));
|
||||||
|
|
||||||
|
// bind default texture
|
||||||
|
GLuint no_texture_id = 0;
|
||||||
|
glsafe(::glBindTexture(target, no_texture_id));
|
||||||
|
|
||||||
|
// show rendered texture
|
||||||
|
wxGetApp().plater()->canvas3D()->schedule_extra_frame(0);
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info)
|
||||||
|
<< "Generate Preview font('" << m_input.font_name << "' id:" << m_input.index << ") "
|
||||||
|
<< "with text: '" << m_input.text << "' "
|
||||||
|
<< "texture_size " << m_input.size.x() << " x " << m_input.size.y();
|
||||||
|
}
|
75
src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp
Normal file
75
src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef slic3r_CreateFontNameImageJob_hpp_
|
||||||
|
#define slic3r_CreateFontNameImageJob_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#include <wx/string.h>
|
||||||
|
#include <wx/fontenc.h>
|
||||||
|
#include "Job.hpp"
|
||||||
|
#include "libslic3r/Point.hpp" // Vec2i
|
||||||
|
|
||||||
|
namespace Slic3r::GUI {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keep data for rasterization of text by font face
|
||||||
|
/// </summary>
|
||||||
|
struct FontImageData
|
||||||
|
{
|
||||||
|
// Text to rasterize
|
||||||
|
std::string text;
|
||||||
|
// Define font face
|
||||||
|
wxString font_name;
|
||||||
|
wxFontEncoding encoding;
|
||||||
|
// texture for copy result to
|
||||||
|
// texture MUST BE initialized
|
||||||
|
GLuint texture_id;
|
||||||
|
// Index of face name, define place in texture
|
||||||
|
size_t index;
|
||||||
|
// Height of each text
|
||||||
|
// And Limit for width
|
||||||
|
Vec2i size; // in px
|
||||||
|
|
||||||
|
// bigger value create darker image
|
||||||
|
// divide value 255
|
||||||
|
unsigned char gray_level = 5;
|
||||||
|
|
||||||
|
// texture meta data
|
||||||
|
GLenum format = GL_ALPHA, type = GL_UNSIGNED_BYTE;
|
||||||
|
GLint level = 0;
|
||||||
|
|
||||||
|
// prevent opening too much files
|
||||||
|
// it is decreased in finalize phase
|
||||||
|
unsigned int *count_opened_font_files = nullptr;
|
||||||
|
|
||||||
|
std::shared_ptr<std::atomic<bool>> cancel = nullptr;
|
||||||
|
std::shared_ptr<bool> is_created = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create image for face name
|
||||||
|
/// </summary>
|
||||||
|
class CreateFontImageJob : public Job
|
||||||
|
{
|
||||||
|
FontImageData m_input;
|
||||||
|
std::vector<unsigned char> m_result;
|
||||||
|
Point m_tex_size;
|
||||||
|
public:
|
||||||
|
CreateFontImageJob(FontImageData &&input);
|
||||||
|
/// <summary>
|
||||||
|
/// Rasterize text into image (result)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctl">Check for cancelation</param>
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy image data into OpenGL texture
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="canceled"></param>
|
||||||
|
/// <param name=""></param>
|
||||||
|
void finalize(bool canceled, std::exception_ptr &) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
|
#endif // slic3r_CreateFontNameImageJob_hpp_
|
164
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp
Normal file
164
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include "CreateFontStyleImagesJob.hpp"
|
||||||
|
|
||||||
|
// rasterization of ExPoly
|
||||||
|
#include "libslic3r/SLA/AGGRaster.hpp"
|
||||||
|
|
||||||
|
// for get DPI
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/MainFrame.hpp"
|
||||||
|
|
||||||
|
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||||
|
|
||||||
|
// ability to request new frame after finish rendering
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
using namespace Slic3r::Emboss;
|
||||||
|
using namespace Slic3r::GUI;
|
||||||
|
using namespace Slic3r::GUI::Emboss;
|
||||||
|
|
||||||
|
|
||||||
|
CreateFontStyleImagesJob::CreateFontStyleImagesJob(
|
||||||
|
StyleManager::StyleImagesData &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(m_input.result != nullptr);
|
||||||
|
assert(!m_input.styles.empty());
|
||||||
|
assert(!m_input.text.empty());
|
||||||
|
assert(m_input.max_size.x() > 1);
|
||||||
|
assert(m_input.max_size.y() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateFontStyleImagesJob::process(Ctl &ctl)
|
||||||
|
{
|
||||||
|
// create shapes and calc size (bounding boxes)
|
||||||
|
std::vector<ExPolygons> name_shapes(m_input.styles.size());
|
||||||
|
std::vector<double> scales(m_input.styles.size());
|
||||||
|
images = std::vector<StyleManager::StyleImage>(m_input.styles.size());
|
||||||
|
|
||||||
|
for (auto &item : m_input.styles) {
|
||||||
|
size_t index = &item - &m_input.styles.front();
|
||||||
|
ExPolygons &shapes = name_shapes[index];
|
||||||
|
shapes = text2shapes(item.font, m_input.text.c_str(), item.prop);
|
||||||
|
|
||||||
|
// create image description
|
||||||
|
StyleManager::StyleImage &image = images[index];
|
||||||
|
BoundingBox &bounding_box = image.bounding_box;
|
||||||
|
for (ExPolygon &shape : shapes)
|
||||||
|
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||||
|
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||||
|
|
||||||
|
// calculate conversion from FontPoint to screen pixels by size of font
|
||||||
|
auto mf = wxGetApp().mainframe;
|
||||||
|
// dot per inch for monitor
|
||||||
|
int dpi = get_dpi_for_window(mf);
|
||||||
|
double ppm = dpi / 25.4; // pixel per milimeter
|
||||||
|
const auto &cn = item.prop.collection_number;
|
||||||
|
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||||
|
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
|
||||||
|
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * ppm;
|
||||||
|
scales[index] = scale;
|
||||||
|
|
||||||
|
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
||||||
|
BoundingBoxf bb2(bounding_box.min.cast<double>(),
|
||||||
|
bounding_box.max.cast<double>());
|
||||||
|
bb2.scale(scale);
|
||||||
|
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x());
|
||||||
|
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y());
|
||||||
|
|
||||||
|
// crop image width
|
||||||
|
if (image.tex_size.x > m_input.max_size.x())
|
||||||
|
image.tex_size.x = m_input.max_size.x();
|
||||||
|
// crop image height
|
||||||
|
if (image.tex_size.y > m_input.max_size.y())
|
||||||
|
image.tex_size.y = m_input.max_size.y();
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrange bounding boxes
|
||||||
|
int offset_y = 0;
|
||||||
|
width = 0;
|
||||||
|
for (StyleManager::StyleImage &image : images) {
|
||||||
|
image.offset.y() = offset_y;
|
||||||
|
offset_y += image.tex_size.y+1;
|
||||||
|
if (width < image.tex_size.x)
|
||||||
|
width = image.tex_size.x;
|
||||||
|
}
|
||||||
|
height = offset_y;
|
||||||
|
for (StyleManager::StyleImage &image : images) {
|
||||||
|
const Point &o = image.offset;
|
||||||
|
const ImVec2 &s = image.tex_size;
|
||||||
|
image.uv0 = ImVec2(o.x() / (double) width,
|
||||||
|
o.y() / (double) height);
|
||||||
|
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
|
||||||
|
(o.y() + s.y) / (double) height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up result
|
||||||
|
pixels = std::vector<unsigned char>(4*width * height, {255});
|
||||||
|
|
||||||
|
// upload sub textures
|
||||||
|
for (StyleManager::StyleImage &image : images) {
|
||||||
|
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
|
||||||
|
size_t index = &image - &images.front();
|
||||||
|
double pixel_dim = SCALING_FACTOR / scales[index];
|
||||||
|
sla::PixelDim dim(pixel_dim, pixel_dim);
|
||||||
|
double gamma = 1.;
|
||||||
|
std::unique_ptr<sla::RasterBase> r =
|
||||||
|
sla::create_raster_grayscale_aa(resolution, dim, gamma);
|
||||||
|
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
|
||||||
|
|
||||||
|
// copy rastered data to pixels
|
||||||
|
sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height]
|
||||||
|
(const void *ptr, size_t width, size_t height, size_t num_components) {
|
||||||
|
// bigger value create darker image
|
||||||
|
unsigned char gray_level = 5;
|
||||||
|
size_t size {static_cast<size_t>(w*h)};
|
||||||
|
assert((offset.x() + width) <= (size_t)w);
|
||||||
|
assert((offset.y() + height) <= (size_t)h);
|
||||||
|
const unsigned char *ptr2 = (const unsigned char *) ptr;
|
||||||
|
for (size_t x = 0; x < width; ++x)
|
||||||
|
for (size_t y = 0; y < height; ++y) {
|
||||||
|
size_t index = (offset.y() + y)*w + offset.x() + x;
|
||||||
|
assert(index < size);
|
||||||
|
if (index >= size) continue;
|
||||||
|
pix[4*index+3] = ptr2[y * width + x] / gray_level;
|
||||||
|
}
|
||||||
|
return sla::EncodedRaster();
|
||||||
|
};
|
||||||
|
r->encode(encoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
|
||||||
|
{
|
||||||
|
if (canceled) return;
|
||||||
|
// upload texture on GPU
|
||||||
|
GLuint tex_id;
|
||||||
|
GLenum target = GL_TEXTURE_2D, format = GL_RGBA, type = GL_UNSIGNED_BYTE;
|
||||||
|
GLint level = 0, border = 0;
|
||||||
|
glsafe(::glGenTextures(1, &tex_id));
|
||||||
|
glsafe(::glBindTexture(target, tex_id));
|
||||||
|
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||||
|
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||||
|
GLint w = width, h=height;
|
||||||
|
glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type,
|
||||||
|
(const void *) pixels.data()));
|
||||||
|
|
||||||
|
// set up texture id
|
||||||
|
void *texture_id = (void *) (intptr_t) tex_id;
|
||||||
|
for (StyleManager::StyleImage &image : images)
|
||||||
|
image.texture_id = texture_id;
|
||||||
|
|
||||||
|
// move to result
|
||||||
|
m_input.result->styles = std::move(m_input.styles);
|
||||||
|
m_input.result->images = std::move(images);
|
||||||
|
|
||||||
|
// bind default texture
|
||||||
|
GLuint no_texture_id = 0;
|
||||||
|
glsafe(::glBindTexture(target, no_texture_id));
|
||||||
|
|
||||||
|
// show rendered texture
|
||||||
|
wxGetApp().plater()->canvas3D()->schedule_extra_frame(0);
|
||||||
|
}
|
36
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp
Normal file
36
src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef slic3r_CreateFontStyleImagesJob_hpp_
|
||||||
|
#define slic3r_CreateFontStyleImagesJob_hpp_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <libslic3r/Emboss.hpp>
|
||||||
|
#include "slic3r/Utils/EmbossStyleManager.hpp"
|
||||||
|
#include "Job.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r::GUI::Emboss {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create texture with name of styles written by its style
|
||||||
|
/// NOTE: Access to glyph cache is possible only from job
|
||||||
|
/// </summary>
|
||||||
|
class CreateFontStyleImagesJob : public Job
|
||||||
|
{
|
||||||
|
StyleManager::StyleImagesData m_input;
|
||||||
|
|
||||||
|
// Output data
|
||||||
|
// texture size
|
||||||
|
int width, height;
|
||||||
|
// texture data
|
||||||
|
std::vector<unsigned char> pixels;
|
||||||
|
// descriptors of sub textures
|
||||||
|
std::vector<StyleManager::StyleImage> images;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input);
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
void finalize(bool canceled, std::exception_ptr &) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
|
#endif // slic3r_CreateFontStyleImagesJob_hpp_
|
791
src/slic3r/GUI/Jobs/EmbossJob.cpp
Normal file
791
src/slic3r/GUI/Jobs/EmbossJob.cpp
Normal file
|
@ -0,0 +1,791 @@
|
||||||
|
#include "EmbossJob.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <libslic3r/Model.hpp>
|
||||||
|
#include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh
|
||||||
|
#include <libslic3r/CutSurface.hpp> // use surface cuts
|
||||||
|
|
||||||
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
#include "slic3r/GUI/NotificationManager.hpp"
|
||||||
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
|
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||||
|
#include "slic3r/GUI/MainFrame.hpp"
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||||
|
#include "slic3r/GUI/CameraUtils.hpp"
|
||||||
|
#include "slic3r/GUI/format.hpp"
|
||||||
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
using namespace Slic3r::Emboss;
|
||||||
|
using namespace Slic3r::GUI;
|
||||||
|
using namespace Slic3r::GUI::Emboss;
|
||||||
|
|
||||||
|
// private namespace
|
||||||
|
namespace priv{
|
||||||
|
// create sure that emboss object is bigger than source object [in mm]
|
||||||
|
constexpr float safe_extension = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assert check of inputs data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool check(const DataBase &input, bool check_fontfile = true, bool use_surface = false);
|
||||||
|
bool check(const DataCreateVolume &input, bool is_main_thread = false);
|
||||||
|
bool check(const DataCreateObject &input);
|
||||||
|
bool check(const DataUpdate &input, bool is_main_thread = false, bool use_surface = false);
|
||||||
|
bool check(const CreateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||||
|
bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread = false);
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
/// Try to create mesh from text
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Text to convert on mesh
|
||||||
|
/// + Shape of characters + Property of font</param>
|
||||||
|
/// <param name="font">Font file with cache
|
||||||
|
/// NOTE: Cache glyphs is changed</param>
|
||||||
|
/// <param name="was_canceled">To check if process was canceled</param>
|
||||||
|
/// <returns>Triangle mesh model</returns>
|
||||||
|
template<typename Fnc> static TriangleMesh try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled);
|
||||||
|
template<typename Fnc> static TriangleMesh create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl &ctl);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create default mesh for embossed text
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Not empty model(index trinagle set - its)</returns>
|
||||||
|
static TriangleMesh create_default_mesh();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Must be called on main thread
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mesh">New mesh data</param>
|
||||||
|
/// <param name="data">Text configuration, ...</param>
|
||||||
|
static void update_volume(TriangleMesh &&mesh, const DataUpdate &data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add new volume to object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mesh">triangles of new volume</param>
|
||||||
|
/// <param name="object_id">Object where to add volume</param>
|
||||||
|
/// <param name="type">Type of new volume</param>
|
||||||
|
/// <param name="trmat">Transformation of volume inside of object</param>
|
||||||
|
/// <param name="data">Text configuration and New VolumeName</param>
|
||||||
|
static void create_volume(TriangleMesh &&mesh, const ObjectID& object_id,
|
||||||
|
const ModelVolumeType type, const Transform3d trmat, const DataBase &data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Select Volume from objects
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="objects">All objects in scene</param>
|
||||||
|
/// <param name="volume_id">Identifier of volume in object</param>
|
||||||
|
/// <returns>Pointer to volume when exist otherwise nullptr</returns>
|
||||||
|
static ModelVolume *get_volume(ModelObjectPtrs &objects, const ObjectID &volume_id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create projection for cut surface from mesh
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tr">Volume transformation in object</param>
|
||||||
|
/// <param name="shape_scale">Convert shape to milimeters</param>
|
||||||
|
/// <param name="z_range">Bounding box 3d of model volume for projection ranges</param>
|
||||||
|
/// <returns>Orthogonal cut_projection</returns>
|
||||||
|
static OrthoProject create_projection_for_cut(Transform3d tr, double shape_scale, const std::pair<float, float> &z_range);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create tranformation for emboss Cutted surface
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="is_outside">True .. raise, False .. engrave</param>
|
||||||
|
/// <param name="emboss">Depth of embossing</param>
|
||||||
|
/// <param name="tr">Text voliume transformation inside object</param>
|
||||||
|
/// <param name="cut">Cutted surface from model</param>
|
||||||
|
/// <returns>Projection</returns>
|
||||||
|
static OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cut surface into triangle mesh
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input1">(can't be const - cache of font)</param>
|
||||||
|
/// <param name="input2">SurfaceVolume data</param>
|
||||||
|
/// <param name="was_canceled">Check to interupt execution</param>
|
||||||
|
/// <returns>Extruded object from cuted surace</returns>
|
||||||
|
static TriangleMesh cut_surface(/*const*/ DataBase &input1, const SurfaceVolumeData &input2, std::function<bool()> was_canceled);
|
||||||
|
|
||||||
|
static void create_message(const std::string &message); // only in finalize
|
||||||
|
static bool process(std::exception_ptr &eptr);
|
||||||
|
|
||||||
|
class JobException : public std::runtime_error {
|
||||||
|
public: JobException(const char* message):runtime_error(message){}};
|
||||||
|
|
||||||
|
}// namespace priv
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
/// Create Volume
|
||||||
|
CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(priv::check(m_input, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateVolumeJob::process(Ctl &ctl) {
|
||||||
|
if (!priv::check(m_input)) throw std::runtime_error("Bad input data for EmbossCreateVolumeJob.");
|
||||||
|
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); };
|
||||||
|
m_result = priv::create_mesh(m_input, was_canceled, ctl);
|
||||||
|
// center result
|
||||||
|
Vec3f c = m_result.bounding_box().center().cast<float>();
|
||||||
|
if (!c.isApprox(Vec3f::Zero())) m_result.translate(-c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||||
|
// doesn't care about exception when process was canceled by user
|
||||||
|
if (canceled) {
|
||||||
|
eptr = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (priv::process(eptr)) return;
|
||||||
|
if (m_result.its.empty())
|
||||||
|
return priv::create_message(_u8L("Can't create empty volume."));
|
||||||
|
|
||||||
|
priv::create_volume(std::move(m_result), m_input.object_id, m_input.volume_type, m_input.trmat, m_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
/// Create Object
|
||||||
|
CreateObjectJob::CreateObjectJob(DataCreateObject &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(priv::check(m_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateObjectJob::process(Ctl &ctl)
|
||||||
|
{
|
||||||
|
if (!priv::check(m_input))
|
||||||
|
throw std::runtime_error("Bad input data for EmbossCreateObjectJob.");
|
||||||
|
|
||||||
|
auto was_canceled = [&ctl]()->bool { return ctl.was_canceled(); };
|
||||||
|
m_result = priv::create_mesh(m_input, was_canceled, ctl);
|
||||||
|
if (was_canceled()) return;
|
||||||
|
|
||||||
|
// Create new object
|
||||||
|
// calculate X,Y offset position for lay on platter in place of
|
||||||
|
// mouse click
|
||||||
|
Vec2d bed_coor = CameraUtils::get_z0_position(
|
||||||
|
m_input.camera, m_input.screen_coor);
|
||||||
|
|
||||||
|
// check point is on build plate:
|
||||||
|
Points bed_shape_;
|
||||||
|
bed_shape_.reserve(m_input.bed_shape.size());
|
||||||
|
for (const Vec2d &p : m_input.bed_shape)
|
||||||
|
bed_shape_.emplace_back(p.cast<int>());
|
||||||
|
Polygon bed(bed_shape_);
|
||||||
|
if (!bed.contains(bed_coor.cast<int>()))
|
||||||
|
// mouse pose is out of build plate so create object in center of plate
|
||||||
|
bed_coor = bed.centroid().cast<double>();
|
||||||
|
|
||||||
|
double z = m_input.text_configuration.style.prop.emboss / 2;
|
||||||
|
Vec3d offset(bed_coor.x(), bed_coor.y(), z);
|
||||||
|
offset -= m_result.center();
|
||||||
|
Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z());
|
||||||
|
m_transformation = Transform3d(tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||||
|
{
|
||||||
|
// doesn't care about exception when process was canceled by user
|
||||||
|
if (canceled) {
|
||||||
|
eptr = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (priv::process(eptr)) return;
|
||||||
|
|
||||||
|
// only for sure
|
||||||
|
if (m_result.empty())
|
||||||
|
return priv::create_message(_u8L("Can't create empty object."));
|
||||||
|
|
||||||
|
GUI_App &app = wxGetApp();
|
||||||
|
Plater *plater = app.plater();
|
||||||
|
ObjectList *obj_list = app.obj_list();
|
||||||
|
GLCanvas3D *canvas = plater->canvas3D();
|
||||||
|
|
||||||
|
plater->take_snapshot(_L("Add Emboss text object"));
|
||||||
|
|
||||||
|
// Create new object and change selection
|
||||||
|
bool center = false;
|
||||||
|
obj_list->load_mesh_object(std::move(m_result), m_input.volume_name,
|
||||||
|
center, &m_input.text_configuration,
|
||||||
|
&m_transformation);
|
||||||
|
|
||||||
|
// When add new object selection is empty.
|
||||||
|
// When cursor move and no one object is selected than
|
||||||
|
// Manager::reset_all() So Gizmo could be closed before end of creation object
|
||||||
|
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
||||||
|
if (manager.get_current_type() != GLGizmosManager::Emboss)
|
||||||
|
manager.open_gizmo(GLGizmosManager::Emboss);
|
||||||
|
|
||||||
|
// redraw scene
|
||||||
|
canvas->reload_scene(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
/// Update Volume
|
||||||
|
UpdateJob::UpdateJob(DataUpdate&& input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(priv::check(m_input, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateJob::process(Ctl &ctl)
|
||||||
|
{
|
||||||
|
if (!priv::check(m_input))
|
||||||
|
throw std::runtime_error("Bad input data for EmbossUpdateJob.");
|
||||||
|
|
||||||
|
auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool {
|
||||||
|
if (cancel->load()) return true;
|
||||||
|
return ctl.was_canceled();
|
||||||
|
};
|
||||||
|
m_result = priv::try_create_mesh(m_input, m_input.font_file, was_canceled);
|
||||||
|
if (was_canceled()) return;
|
||||||
|
if (m_result.its.empty())
|
||||||
|
throw priv::JobException(_u8L("Created text volume is empty. Change text or font.").c_str());
|
||||||
|
|
||||||
|
// center triangle mesh
|
||||||
|
Vec3d shift = m_result.bounding_box().center();
|
||||||
|
m_result.translate(-shift.cast<float>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||||
|
{
|
||||||
|
// doesn't care about exception when process was canceled by user
|
||||||
|
if (canceled || m_input.cancel->load()) {
|
||||||
|
eptr = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (priv::process(eptr)) return;
|
||||||
|
priv::update_volume(std::move(m_result), m_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Slic3r::GUI::Emboss {
|
||||||
|
|
||||||
|
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id)
|
||||||
|
{
|
||||||
|
SurfaceVolumeData::ModelSources result;
|
||||||
|
result.reserve(volumes.size() - 1);
|
||||||
|
for (const ModelVolume *v : volumes) {
|
||||||
|
if (text_volume_id.has_value() && v->id().id == *text_volume_id) continue;
|
||||||
|
// skip modifiers and negative volumes, ...
|
||||||
|
if (!v->is_model_part()) continue;
|
||||||
|
const TriangleMesh &tm = v->mesh();
|
||||||
|
if (tm.empty()) continue;
|
||||||
|
if (tm.its.empty()) continue;
|
||||||
|
result.push_back({v->get_mesh_shared_ptr(), v->get_matrix()});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume)
|
||||||
|
{
|
||||||
|
if (text_volume == nullptr) return {};
|
||||||
|
if (!text_volume->text_configuration.has_value()) return {};
|
||||||
|
const ModelVolumePtrs &volumes = text_volume->get_object()->volumes;
|
||||||
|
// no other volume in object
|
||||||
|
if (volumes.size() <= 1) return {};
|
||||||
|
return create_sources(volumes, text_volume->id().id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI::Emboss
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
/// Create Surface volume
|
||||||
|
CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(priv::check(m_input, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateSurfaceVolumeJob::process(Ctl &ctl) {
|
||||||
|
if (!priv::check(m_input))
|
||||||
|
throw std::runtime_error("Bad input data for CreateSurfaceVolumeJob.");
|
||||||
|
// check cancelation of process
|
||||||
|
auto was_canceled = [&ctl]() -> bool { return ctl.was_canceled(); };
|
||||||
|
m_result = priv::cut_surface(m_input, m_input, was_canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||||
|
// doesn't care about exception when process was canceled by user
|
||||||
|
if (canceled) return;
|
||||||
|
if (priv::process(eptr)) return;
|
||||||
|
|
||||||
|
// TODO: Find better way to Not center volume data when add !!!
|
||||||
|
TriangleMesh mesh = m_result; // Part1: copy
|
||||||
|
|
||||||
|
priv::create_volume(std::move(m_result), m_input.object_id,
|
||||||
|
m_input.volume_type, m_input.text_tr, m_input);
|
||||||
|
|
||||||
|
// Part2: update volume data
|
||||||
|
//auto vol = wxGetApp().plater()->model().objects[m_input.object_idx]->volumes.back();
|
||||||
|
//UpdateJob::update_volume(vol, std::move(mesh), m_input.text_configuration, m_input.volume_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
/// Cut Surface
|
||||||
|
UpdateSurfaceVolumeJob::UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input)
|
||||||
|
: m_input(std::move(input))
|
||||||
|
{
|
||||||
|
assert(priv::check(m_input, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSurfaceVolumeJob::process(Ctl &ctl)
|
||||||
|
{
|
||||||
|
if (!priv::check(m_input))
|
||||||
|
throw std::runtime_error("Bad input data for UseSurfaceJob.");
|
||||||
|
|
||||||
|
// check cancelation of process
|
||||||
|
auto was_canceled = [&ctl, &cancel = m_input.cancel]()->bool {
|
||||||
|
if (cancel->load()) return true;
|
||||||
|
return ctl.was_canceled();
|
||||||
|
};
|
||||||
|
m_result = priv::cut_surface(m_input, m_input, was_canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSurfaceVolumeJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||||
|
{
|
||||||
|
// doesn't care about exception when process was canceled by user
|
||||||
|
if (m_input.cancel->load()) {
|
||||||
|
eptr = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (canceled) return;
|
||||||
|
if (priv::process(eptr)) return;
|
||||||
|
priv::update_volume(std::move(m_result), m_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
/// private namespace implementation
|
||||||
|
bool priv::check(const DataBase &input, bool check_fontfile, bool use_surface)
|
||||||
|
{
|
||||||
|
bool res = true;
|
||||||
|
if (check_fontfile) {
|
||||||
|
assert(input.font_file.has_value());
|
||||||
|
res &= input.font_file.has_value();
|
||||||
|
}
|
||||||
|
assert(!input.text_configuration.fix_3mf_tr.has_value());
|
||||||
|
res &= !input.text_configuration.fix_3mf_tr.has_value();
|
||||||
|
assert(!input.text_configuration.text.empty());
|
||||||
|
res &= !input.text_configuration.text.empty();
|
||||||
|
assert(!input.volume_name.empty());
|
||||||
|
res &= !input.volume_name.empty();
|
||||||
|
assert(input.text_configuration.style.prop.use_surface == use_surface);
|
||||||
|
res &= input.text_configuration.style.prop.use_surface == use_surface;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool priv::check(const DataCreateVolume &input, bool is_main_thread) {
|
||||||
|
bool check_fontfile = false;
|
||||||
|
bool res = check((DataBase) input, check_fontfile);
|
||||||
|
assert(input.volume_type != ModelVolumeType::INVALID);
|
||||||
|
res &= input.volume_type != ModelVolumeType::INVALID;
|
||||||
|
assert(input.object_id.id >= 0);
|
||||||
|
res &= input.object_id.id >= 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool priv::check(const DataCreateObject &input) {
|
||||||
|
bool check_fontfile = false;
|
||||||
|
bool res = check((DataBase) input, check_fontfile);
|
||||||
|
assert(input.screen_coor.x() >= 0.);
|
||||||
|
res &= input.screen_coor.x() >= 0.;
|
||||||
|
assert(input.screen_coor.y() >= 0.);
|
||||||
|
res &= input.screen_coor.y() >= 0.;
|
||||||
|
assert(input.bed_shape.size() >= 3); // at least triangle
|
||||||
|
res &= input.bed_shape.size() >= 3;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool priv::check(const DataUpdate &input, bool is_main_thread, bool use_surface){
|
||||||
|
bool check_fontfile = true;
|
||||||
|
bool res = check((DataBase) input, check_fontfile, use_surface);
|
||||||
|
assert(input.volume_id.id >= 0);
|
||||||
|
res &= input.volume_id.id >= 0;
|
||||||
|
if (is_main_thread)
|
||||||
|
assert(get_volume(wxGetApp().model().objects, input.volume_id) != nullptr);
|
||||||
|
assert(input.cancel != nullptr);
|
||||||
|
res &= input.cancel != nullptr;
|
||||||
|
if (is_main_thread)
|
||||||
|
assert(!input.cancel->load());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool priv::check(const CreateSurfaceVolumeData &input, bool is_main_thread)
|
||||||
|
{
|
||||||
|
bool use_surface = true;
|
||||||
|
bool res = check((DataBase)input, is_main_thread, use_surface);
|
||||||
|
assert(!input.sources.empty());
|
||||||
|
res &= !input.sources.empty();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool priv::check(const UpdateSurfaceVolumeData &input, bool is_main_thread){
|
||||||
|
bool use_surface = true;
|
||||||
|
bool res = check((DataUpdate)input, is_main_thread, use_surface);
|
||||||
|
assert(!input.sources.empty());
|
||||||
|
res &= !input.sources.empty();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fnc>
|
||||||
|
TriangleMesh priv::try_create_mesh(const DataBase &input, FontFileWithCache &font, Fnc was_canceled)
|
||||||
|
{
|
||||||
|
const TextConfiguration &tc = input.text_configuration;
|
||||||
|
const char *text = tc.text.c_str();
|
||||||
|
const FontProp &prop = tc.style.prop;
|
||||||
|
|
||||||
|
assert(font.has_value());
|
||||||
|
if (!font.has_value()) return {};
|
||||||
|
|
||||||
|
ExPolygons shapes = text2shapes(font, text, prop, was_canceled);
|
||||||
|
if (shapes.empty()) return {};
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
|
||||||
|
const auto &cn = prop.collection_number;
|
||||||
|
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||||
|
assert(font_index < font.font_file->infos.size());
|
||||||
|
int unit_per_em = font.font_file->infos[font_index].unit_per_em;
|
||||||
|
float scale = prop.size_in_mm / unit_per_em;
|
||||||
|
float depth = prop.emboss / scale;
|
||||||
|
auto projectZ = std::make_unique<ProjectZ>(depth);
|
||||||
|
ProjectScale project(std::move(projectZ), scale);
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
return TriangleMesh(polygons2model(shapes, project));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fnc>
|
||||||
|
TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl)
|
||||||
|
{
|
||||||
|
// It is neccessary to create some shape
|
||||||
|
// Emboss text window is opened by creation new emboss text object
|
||||||
|
TriangleMesh result;
|
||||||
|
if (input.font_file.has_value()) {
|
||||||
|
result = try_create_mesh(input, input.font_file, was_canceled);
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.its.empty()) {
|
||||||
|
result = priv::create_default_mesh();
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
// only info
|
||||||
|
ctl.call_on_main_thread([]() {
|
||||||
|
create_message(_u8L("It is used default volume for embossed "
|
||||||
|
"text, try to change text or font for fix it."));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!result.its.empty());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriangleMesh priv::create_default_mesh()
|
||||||
|
{
|
||||||
|
// When cant load any font use default object loaded from file
|
||||||
|
std::string path = Slic3r::resources_dir() + "/data/embossed_text.stl";
|
||||||
|
TriangleMesh triangle_mesh;
|
||||||
|
if (!load_obj(path.c_str(), &triangle_mesh)) {
|
||||||
|
// when can't load mesh use cube
|
||||||
|
return TriangleMesh(its_make_cube(36., 4., 2.5));
|
||||||
|
}
|
||||||
|
return triangle_mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateJob::update_volume(ModelVolume *volume,
|
||||||
|
TriangleMesh &&mesh,
|
||||||
|
const TextConfiguration &text_configuration,
|
||||||
|
const std::string &volume_name)
|
||||||
|
{
|
||||||
|
// check inputs
|
||||||
|
bool is_valid_input =
|
||||||
|
volume != nullptr &&
|
||||||
|
!mesh.empty() &&
|
||||||
|
!volume_name.empty();
|
||||||
|
assert(is_valid_input);
|
||||||
|
if (!is_valid_input) return;
|
||||||
|
|
||||||
|
// update volume
|
||||||
|
volume->set_mesh(std::move(mesh));
|
||||||
|
volume->set_new_unique_id();
|
||||||
|
volume->calculate_convex_hull();
|
||||||
|
volume->get_object()->invalidate_bounding_box();
|
||||||
|
volume->text_configuration = text_configuration;
|
||||||
|
|
||||||
|
GUI_App &app = wxGetApp(); // may be move to input
|
||||||
|
GLCanvas3D *canvas = app.plater()->canvas3D();
|
||||||
|
const Selection &selection = canvas->get_selection();
|
||||||
|
const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
|
int object_idx = gl_volume->object_idx();
|
||||||
|
|
||||||
|
if (volume->name != volume_name) {
|
||||||
|
volume->name = volume_name;
|
||||||
|
|
||||||
|
// update volume name in right panel( volume / object name)
|
||||||
|
int volume_idx = gl_volume->volume_idx();
|
||||||
|
ObjectList *obj_list = app.obj_list();
|
||||||
|
obj_list->update_name_in_list(object_idx, volume_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update printable state on canvas
|
||||||
|
if (volume->type() == ModelVolumeType::MODEL_PART)
|
||||||
|
canvas->update_instance_printable_state_for_object((size_t) object_idx);
|
||||||
|
|
||||||
|
// Move object on bed
|
||||||
|
if (GLGizmoEmboss::is_text_object(volume)) volume->get_object()->ensure_on_bed();
|
||||||
|
|
||||||
|
// redraw scene
|
||||||
|
bool refresh_immediately = false;
|
||||||
|
canvas->reload_scene(refresh_immediately);
|
||||||
|
}
|
||||||
|
|
||||||
|
void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data)
|
||||||
|
{
|
||||||
|
// for sure that some object will be created
|
||||||
|
if (mesh.its.empty())
|
||||||
|
return priv::create_message("Empty mesh can't be created.");
|
||||||
|
|
||||||
|
Plater *plater = wxGetApp().plater();
|
||||||
|
GLCanvas3D *canvas = plater->canvas3D();
|
||||||
|
|
||||||
|
// Check emboss gizmo is still open
|
||||||
|
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
||||||
|
if (manager.get_current_type() != GLGizmosManager::Emboss) return;
|
||||||
|
|
||||||
|
std::string snap_name = GUI::format(_L("Text: %1%"), data.text_configuration.text);
|
||||||
|
Plater::TakeSnapshot snapshot(plater, snap_name, UndoRedo::SnapshotType::GizmoAction);
|
||||||
|
ModelVolume *volume = get_volume(plater->model().objects, data.volume_id);
|
||||||
|
// could appear when user delete edited volume
|
||||||
|
if (volume == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// apply fix matrix made by store to .3mf
|
||||||
|
const auto &tc = volume->text_configuration;
|
||||||
|
assert(tc.has_value());
|
||||||
|
if (tc.has_value() && tc->fix_3mf_tr.has_value())
|
||||||
|
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse());
|
||||||
|
|
||||||
|
UpdateJob::update_volume(volume, std::move(mesh), data.text_configuration, data.volume_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void priv::create_volume(
|
||||||
|
TriangleMesh &&mesh, const ObjectID& object_id,
|
||||||
|
const ModelVolumeType type, const Transform3d trmat, const DataBase &data)
|
||||||
|
{
|
||||||
|
GUI_App &app = wxGetApp();
|
||||||
|
Plater *plater = app.plater();
|
||||||
|
ObjectList *obj_list = app.obj_list();
|
||||||
|
GLCanvas3D *canvas = plater->canvas3D();
|
||||||
|
ModelObjectPtrs &objects = plater->model().objects;
|
||||||
|
|
||||||
|
ModelObject *obj = nullptr;
|
||||||
|
size_t object_idx = 0;
|
||||||
|
for (; object_idx < objects.size(); ++object_idx) {
|
||||||
|
ModelObject *o = objects[object_idx];
|
||||||
|
if (o->id() == object_id) {
|
||||||
|
obj = o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent object for text volume was propably removed.
|
||||||
|
// Assumption: User know what he does, so text volume is no more needed.
|
||||||
|
if (obj == nullptr)
|
||||||
|
return priv::create_message(_u8L("Bad object to create volume."));
|
||||||
|
|
||||||
|
if (mesh.its.empty())
|
||||||
|
return priv::create_message(_u8L("Can't create empty volume."));
|
||||||
|
|
||||||
|
plater->take_snapshot(_L("Add Emboss text Volume"));
|
||||||
|
|
||||||
|
// NOTE: be carefull add volume also center mesh !!!
|
||||||
|
// So first add simple shape(convex hull is also calculated)
|
||||||
|
ModelVolume *volume = obj->add_volume(make_cube(1., 1., 1.), type);
|
||||||
|
|
||||||
|
// TODO: Refactor to create better way to not set cube at begining
|
||||||
|
// Revert mesh centering by set mesh after add cube
|
||||||
|
volume->set_mesh(std::move(mesh));
|
||||||
|
volume->calculate_convex_hull();
|
||||||
|
|
||||||
|
|
||||||
|
// set a default extruder value, since user can't add it manually
|
||||||
|
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
// do not allow model reload from disk
|
||||||
|
volume->source.is_from_builtin_objects = true;
|
||||||
|
|
||||||
|
volume->name = data.volume_name; // copy
|
||||||
|
volume->text_configuration = data.text_configuration; // copy
|
||||||
|
volume->set_transformation(trmat);
|
||||||
|
|
||||||
|
// update volume name in object list
|
||||||
|
// updata selection after new volume added
|
||||||
|
// change name of volume in right panel
|
||||||
|
// select only actual volume
|
||||||
|
// when new volume is created change selection to this volume
|
||||||
|
auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; };
|
||||||
|
wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection);
|
||||||
|
if (!sel.IsEmpty()) obj_list->select_item(sel.front());
|
||||||
|
|
||||||
|
// update printable state on canvas
|
||||||
|
if (type == ModelVolumeType::MODEL_PART) canvas->update_instance_printable_state_for_object(object_idx);
|
||||||
|
|
||||||
|
obj_list->selection_changed();
|
||||||
|
|
||||||
|
// Now is valid text volume selected open emboss gizmo
|
||||||
|
GLGizmosManager &manager = canvas->get_gizmos_manager();
|
||||||
|
if (manager.get_current_type() != GLGizmosManager::Emboss)
|
||||||
|
manager.open_gizmo(GLGizmosManager::Emboss);
|
||||||
|
|
||||||
|
// redraw scene
|
||||||
|
canvas->reload_scene(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelVolume *priv::get_volume(ModelObjectPtrs &objects,
|
||||||
|
const ObjectID &volume_id)
|
||||||
|
{
|
||||||
|
for (ModelObject *obj : objects)
|
||||||
|
for (ModelVolume *vol : obj->volumes)
|
||||||
|
if (vol->id() == volume_id) return vol;
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
OrthoProject priv::create_projection_for_cut(
|
||||||
|
Transform3d tr,
|
||||||
|
double shape_scale,
|
||||||
|
const std::pair<float, float> &z_range)
|
||||||
|
{
|
||||||
|
double min_z = z_range.first - priv::safe_extension;
|
||||||
|
double max_z = z_range.second + priv::safe_extension;
|
||||||
|
assert(min_z < max_z);
|
||||||
|
// range between min and max value
|
||||||
|
double projection_size = max_z - min_z;
|
||||||
|
Matrix3d transformation_for_vector = tr.linear();
|
||||||
|
// Projection must be negative value.
|
||||||
|
// System of text coordinate
|
||||||
|
// X .. from left to right
|
||||||
|
// Y .. from bottom to top
|
||||||
|
// Z .. from text to eye
|
||||||
|
Vec3d untransformed_direction(0., 0., projection_size);
|
||||||
|
Vec3d project_direction = transformation_for_vector * untransformed_direction;
|
||||||
|
|
||||||
|
// Projection is in direction from far plane
|
||||||
|
tr.translate(Vec3d(0., 0., min_z));
|
||||||
|
tr.scale(shape_scale);
|
||||||
|
return OrthoProject(tr, project_direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
OrthoProject3d priv::create_emboss_projection(
|
||||||
|
bool is_outside, float emboss, Transform3d tr, SurfaceCut &cut)
|
||||||
|
{
|
||||||
|
// Offset of clossed side to model
|
||||||
|
const float surface_offset = 1e-3f; // [in mm]
|
||||||
|
float
|
||||||
|
front_move = (is_outside) ? emboss : surface_offset,
|
||||||
|
back_move = -((is_outside) ? surface_offset : emboss);
|
||||||
|
its_transform(cut, tr.pretranslate(Vec3d(0., 0., front_move)));
|
||||||
|
Vec3d from_front_to_back(0., 0., back_move - front_move);
|
||||||
|
return OrthoProject3d(from_front_to_back);
|
||||||
|
}
|
||||||
|
|
||||||
|
// input can't be const - cache of font
|
||||||
|
TriangleMesh priv::cut_surface(DataBase& input1, const SurfaceVolumeData& input2, std::function<bool()> was_canceled)
|
||||||
|
{
|
||||||
|
const TextConfiguration &tc = input1.text_configuration;
|
||||||
|
const char *text = tc.text.c_str();
|
||||||
|
const FontProp &fp = tc.style.prop;
|
||||||
|
|
||||||
|
ExPolygons shapes = text2shapes(input1.font_file, text, fp, was_canceled);
|
||||||
|
if (shapes.empty() || shapes.front().contour.empty())
|
||||||
|
throw JobException(_u8L("Font doesn't have any shape for given text.").c_str());
|
||||||
|
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
|
||||||
|
// Define alignment of text - left, right, center, top bottom, ....
|
||||||
|
BoundingBox bb = get_extents(shapes);
|
||||||
|
Point projection_center = bb.center();
|
||||||
|
for (ExPolygon &shape : shapes) shape.translate(-projection_center);
|
||||||
|
bb.translate(-projection_center);
|
||||||
|
|
||||||
|
const FontFile &ff = *input1.font_file.font_file;
|
||||||
|
double shape_scale = get_shape_scale(fp, ff);
|
||||||
|
|
||||||
|
const SurfaceVolumeData::ModelSources &sources = input2.sources;
|
||||||
|
const SurfaceVolumeData::ModelSource *biggest = nullptr;
|
||||||
|
|
||||||
|
size_t biggest_count = 0;
|
||||||
|
// convert index from (s)ources to (i)ndexed (t)riangle (s)ets
|
||||||
|
std::vector<size_t> s_to_itss(sources.size(), std::numeric_limits<size_t>::max());
|
||||||
|
std::vector<indexed_triangle_set> itss;
|
||||||
|
itss.reserve(sources.size());
|
||||||
|
for (const SurfaceVolumeData::ModelSource &s : sources) {
|
||||||
|
Transform3d mesh_tr_inv = s.tr.inverse();
|
||||||
|
Transform3d cut_projection_tr = mesh_tr_inv * input2.text_tr;
|
||||||
|
std::pair<float, float> z_range{0., 1.};
|
||||||
|
OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
|
||||||
|
// copy only part of source model
|
||||||
|
indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection);
|
||||||
|
if (its.indices.empty()) continue;
|
||||||
|
if (biggest_count < its.vertices.size()) {
|
||||||
|
biggest_count = its.vertices.size();
|
||||||
|
biggest = &s;
|
||||||
|
}
|
||||||
|
s_to_itss[&s - &sources.front()] = itss.size();
|
||||||
|
itss.emplace_back(std::move(its));
|
||||||
|
}
|
||||||
|
if (itss.empty()) throw JobException(_u8L("There is no volume in projection direction.").c_str());
|
||||||
|
|
||||||
|
Transform3d tr_inv = biggest->tr.inverse();
|
||||||
|
size_t itss_index = s_to_itss[biggest - &sources.front()];
|
||||||
|
BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]);
|
||||||
|
for (const SurfaceVolumeData::ModelSource &s : sources) {
|
||||||
|
if (&s == biggest) continue;
|
||||||
|
size_t itss_index = s_to_itss[&s - &sources.front()];
|
||||||
|
if (itss_index == std::numeric_limits<size_t>::max()) continue;
|
||||||
|
Transform3d tr = s.tr * tr_inv;
|
||||||
|
indexed_triangle_set &its = itss[itss_index];
|
||||||
|
its_transform(its, tr);
|
||||||
|
BoundingBoxf3 bb = bounding_box(its);
|
||||||
|
mesh_bb.merge(bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tr_inv = transformation of mesh inverted
|
||||||
|
Transform3d cut_projection_tr = tr_inv * input2.text_tr;
|
||||||
|
Transform3d emboss_tr = cut_projection_tr.inverse();
|
||||||
|
BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr);
|
||||||
|
std::pair<float, float> z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()};
|
||||||
|
OrthoProject cut_projection = create_projection_for_cut(cut_projection_tr, shape_scale, z_range);
|
||||||
|
float projection_ratio = (-z_range.first + safe_extension) / (z_range.second - z_range.first + 2 * safe_extension);
|
||||||
|
|
||||||
|
// Use CGAL to cut surface from triangle mesh
|
||||||
|
SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio);
|
||||||
|
if (cut.empty()) throw JobException(_u8L("There is no valid surface for text projection.").c_str());
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
|
||||||
|
// !! Projection needs to transform cut
|
||||||
|
OrthoProject3d projection = create_emboss_projection(input2.is_outside, fp.emboss, emboss_tr, cut);
|
||||||
|
|
||||||
|
indexed_triangle_set new_its = cut2model(cut, projection);
|
||||||
|
assert(!new_its.empty());
|
||||||
|
|
||||||
|
if (was_canceled()) return {};
|
||||||
|
return TriangleMesh(std::move(new_its));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool priv::process(std::exception_ptr &eptr) {
|
||||||
|
if (!eptr) return false;
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(eptr);
|
||||||
|
} catch (priv::JobException &e) {
|
||||||
|
create_message(e.what());
|
||||||
|
eptr = nullptr;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
|
void priv::create_message(const std::string &message) {
|
||||||
|
wxMessageBox(wxString(message), _L("Issue during embossing the text."),
|
||||||
|
wxOK | wxICON_WARNING);
|
||||||
|
}
|
237
src/slic3r/GUI/Jobs/EmbossJob.hpp
Normal file
237
src/slic3r/GUI/Jobs/EmbossJob.hpp
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
#ifndef slic3r_EmbossJob_hpp_
|
||||||
|
#define slic3r_EmbossJob_hpp_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <libslic3r/Emboss.hpp>
|
||||||
|
//#include <libslic3r/ModelVolumeType.hpp>
|
||||||
|
#include "slic3r/Utils/RaycastManager.hpp"
|
||||||
|
#include "slic3r/GUI/Camera.hpp"
|
||||||
|
#include "Job.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
class ModelVolume;
|
||||||
|
class TriangleMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Slic3r::GUI::Emboss {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base data holder for embossing
|
||||||
|
/// </summary>
|
||||||
|
struct DataBase
|
||||||
|
{
|
||||||
|
// Keep pointer on Data of font (glyph shapes)
|
||||||
|
Slic3r::Emboss::FontFileWithCache font_file;
|
||||||
|
// font item is not used for create object
|
||||||
|
TextConfiguration text_configuration;
|
||||||
|
// new volume name created from text
|
||||||
|
std::string volume_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hold neccessary data to create ModelVolume in job
|
||||||
|
/// Volume is created on the surface of existing volume in object.
|
||||||
|
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
|
||||||
|
/// </summary>
|
||||||
|
struct DataCreateVolume : public DataBase
|
||||||
|
{
|
||||||
|
// define embossed volume type
|
||||||
|
ModelVolumeType volume_type;
|
||||||
|
|
||||||
|
// parent ModelObject index where to create volume
|
||||||
|
ObjectID object_id;
|
||||||
|
|
||||||
|
// new created volume transformation
|
||||||
|
Transform3d trmat;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new TextVolume on the surface of ModelObject
|
||||||
|
/// Should not be stopped
|
||||||
|
/// NOTE: EmbossDataBase::font_file doesn't have to be valid !!!
|
||||||
|
/// </summary>
|
||||||
|
class CreateVolumeJob : public Job
|
||||||
|
{
|
||||||
|
DataCreateVolume m_input;
|
||||||
|
TriangleMesh m_result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CreateVolumeJob(DataCreateVolume&& input);
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hold neccessary data to create ModelObject in job
|
||||||
|
/// Object is placed on bed under screen coor
|
||||||
|
/// OR to center of scene when it is out of bed shape
|
||||||
|
/// </summary>
|
||||||
|
struct DataCreateObject : public DataBase
|
||||||
|
{
|
||||||
|
// define position on screen where to create object
|
||||||
|
Vec2d screen_coor;
|
||||||
|
|
||||||
|
// projection property
|
||||||
|
Camera camera;
|
||||||
|
|
||||||
|
// shape of bed in case of create volume on bed
|
||||||
|
std::vector<Vec2d> bed_shape;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new TextObject on the platter
|
||||||
|
/// Should not be stopped
|
||||||
|
/// </summary>
|
||||||
|
class CreateObjectJob : public Job
|
||||||
|
{
|
||||||
|
DataCreateObject m_input;
|
||||||
|
TriangleMesh m_result;
|
||||||
|
Transform3d m_transformation;
|
||||||
|
public:
|
||||||
|
CreateObjectJob(DataCreateObject&& input);
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hold neccessary data to update embossed text object in job
|
||||||
|
/// </summary>
|
||||||
|
struct DataUpdate : public DataBase
|
||||||
|
{
|
||||||
|
// unique identifier of volume to change
|
||||||
|
ObjectID volume_id;
|
||||||
|
|
||||||
|
// flag that job is canceled
|
||||||
|
// for time after process.
|
||||||
|
std::shared_ptr<std::atomic<bool>> cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update text shape in existing text volume
|
||||||
|
/// Predict that there is only one runnig(not canceled) instance of it
|
||||||
|
/// </summary>
|
||||||
|
class UpdateJob : public Job
|
||||||
|
{
|
||||||
|
DataUpdate m_input;
|
||||||
|
TriangleMesh m_result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// move params to private variable
|
||||||
|
UpdateJob(DataUpdate &&input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new embossed volume by m_input data and store to m_result
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ctl">Control containing cancel flag</param>
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update volume - change object_id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="canceled">Was process canceled.
|
||||||
|
/// NOTE: Be carefull it doesn't care about
|
||||||
|
/// time between finished process and started finalize part.</param>
|
||||||
|
/// <param name="">unused</param>
|
||||||
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update text volume
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume">Volume to be updated</param>
|
||||||
|
/// <param name="mesh">New Triangle mesh for volume</param>
|
||||||
|
/// <param name="text_configuration">Parametric description of volume</param>
|
||||||
|
/// <param name="volume_name">Name of volume</param>
|
||||||
|
static void update_volume(ModelVolume *volume,
|
||||||
|
TriangleMesh &&mesh,
|
||||||
|
const TextConfiguration &text_configuration,
|
||||||
|
const std::string &volume_name);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SurfaceVolumeData
|
||||||
|
{
|
||||||
|
// Transformation of text volume inside of object
|
||||||
|
Transform3d text_tr;
|
||||||
|
|
||||||
|
// Define projection move
|
||||||
|
// True (raised) .. move outside from surface
|
||||||
|
// False (engraved).. move into object
|
||||||
|
bool is_outside;
|
||||||
|
|
||||||
|
struct ModelSource
|
||||||
|
{
|
||||||
|
// source volumes
|
||||||
|
std::shared_ptr<const TriangleMesh> mesh;
|
||||||
|
// Transformation of volume inside of object
|
||||||
|
Transform3d tr;
|
||||||
|
};
|
||||||
|
using ModelSources = std::vector<ModelSource>;
|
||||||
|
ModelSources sources;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hold neccessary data to create(cut) volume from surface object in job
|
||||||
|
/// </summary>
|
||||||
|
struct CreateSurfaceVolumeData : public DataBase, public SurfaceVolumeData{
|
||||||
|
// define embossed volume type
|
||||||
|
ModelVolumeType volume_type;
|
||||||
|
|
||||||
|
// parent ModelObject index where to create volume
|
||||||
|
ObjectID object_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cut surface from object and create cutted volume
|
||||||
|
/// Should not be stopped
|
||||||
|
/// </summary>
|
||||||
|
class CreateSurfaceVolumeJob : public Job
|
||||||
|
{
|
||||||
|
CreateSurfaceVolumeData m_input;
|
||||||
|
TriangleMesh m_result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input);
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hold neccessary data to update embossed text object in job
|
||||||
|
/// </summary>
|
||||||
|
struct UpdateSurfaceVolumeData : public DataUpdate, public SurfaceVolumeData{};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copied triangles from object to be able create mesh for cut surface from
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volumes">Source object volumes for cut surface from</param>
|
||||||
|
/// <param name="text_volume_id">Source volume id</param>
|
||||||
|
/// <returns>Source data for cut surface from</returns>
|
||||||
|
SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, std::optional<size_t> text_volume_id = {});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copied triangles from object to be able create mesh for cut surface from
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text_volume">Define text in object</param>
|
||||||
|
/// <returns>Source data for cut surface from</returns>
|
||||||
|
SurfaceVolumeData::ModelSources create_volume_sources(const ModelVolume *text_volume);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update text volume to use surface from object
|
||||||
|
/// </summary>
|
||||||
|
class UpdateSurfaceVolumeJob : public Job
|
||||||
|
{
|
||||||
|
UpdateSurfaceVolumeData m_input;
|
||||||
|
TriangleMesh m_result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// move params to private variable
|
||||||
|
UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input);
|
||||||
|
void process(Ctl &ctl) override;
|
||||||
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI
|
||||||
|
|
||||||
|
#endif // slic3r_EmbossJob_hpp_
|
|
@ -2,6 +2,7 @@
|
||||||
#define PLATERWORKER_HPP
|
#define PLATERWORKER_HPP
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
#include "Worker.hpp"
|
#include "Worker.hpp"
|
||||||
#include "BusyCursorJob.hpp"
|
#include "BusyCursorJob.hpp"
|
||||||
|
@ -24,6 +25,7 @@ class PlaterWorker: public Worker {
|
||||||
class PlaterJob : public Job {
|
class PlaterJob : public Job {
|
||||||
std::unique_ptr<Job> m_job;
|
std::unique_ptr<Job> m_job;
|
||||||
Plater *m_plater;
|
Plater *m_plater;
|
||||||
|
long long m_process_duration; // [ms]
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void process(Ctl &c) override
|
void process(Ctl &c) override
|
||||||
|
@ -55,12 +57,27 @@ class PlaterWorker: public Worker {
|
||||||
} wctl{c};
|
} wctl{c};
|
||||||
|
|
||||||
CursorSetterRAII busycursor{wctl};
|
CursorSetterRAII busycursor{wctl};
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
steady_clock::time_point process_start = steady_clock::now();
|
||||||
m_job->process(wctl);
|
m_job->process(wctl);
|
||||||
|
steady_clock::time_point process_end = steady_clock::now();
|
||||||
|
m_process_duration = duration_cast<milliseconds>(process_end - process_start).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
void finalize(bool canceled, std::exception_ptr &eptr) override
|
void finalize(bool canceled, std::exception_ptr &eptr) override
|
||||||
{
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
steady_clock::time_point finalize_start = steady_clock::now();
|
||||||
m_job->finalize(canceled, eptr);
|
m_job->finalize(canceled, eptr);
|
||||||
|
steady_clock::time_point finalize_end = steady_clock::now();
|
||||||
|
long long finalize_duration = duration_cast<milliseconds>(finalize_end - finalize_start).count();
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info)
|
||||||
|
<< std::fixed // do not use scientific notations
|
||||||
|
<< "Job '" << typeid(*m_job).name() << "' "
|
||||||
|
<< "spend " << m_process_duration + finalize_duration << "ms "
|
||||||
|
<< "(process " << m_process_duration << "ms + finalize " << finalize_duration << "ms)";
|
||||||
|
|
||||||
if (eptr) try {
|
if (eptr) try {
|
||||||
std::rethrow_exception(eptr);
|
std::rethrow_exception(eptr);
|
||||||
|
|
|
@ -202,8 +202,14 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
|
||||||
if (m_id == 0)
|
if (m_id == 0)
|
||||||
m_id = m_id_provider.allocate_id();
|
m_id = m_id_provider.allocate_id();
|
||||||
std::string name = "!!Ntfctn" + std::to_string(m_id);
|
std::string name = "!!Ntfctn" + std::to_string(m_id);
|
||||||
|
|
||||||
|
int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar |
|
||||||
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_NoScrollbar |
|
||||||
|
ImGuiWindowFlags_NoScrollWithMouse |
|
||||||
|
ImGuiWindowFlags_NoFocusOnAppearing;
|
||||||
|
|
||||||
if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
if (imgui.begin(name, flags)) {
|
||||||
ImVec2 win_size = ImGui::GetWindowSize();
|
ImVec2 win_size = ImGui::GetWindowSize();
|
||||||
|
|
||||||
render_left_sign(imgui);
|
render_left_sign(imgui);
|
||||||
|
|
|
@ -111,6 +111,8 @@ enum class NotificationType
|
||||||
// Give user advice to simplify object with big amount of triangles
|
// Give user advice to simplify object with big amount of triangles
|
||||||
// Contains ObjectID for closing when object is deleted
|
// Contains ObjectID for closing when object is deleted
|
||||||
SimplifySuggestion,
|
SimplifySuggestion,
|
||||||
|
// Change of text will change font to similar one on.
|
||||||
|
UnknownFont,
|
||||||
// information about netfabb is finished repairing model (blocking proccess)
|
// information about netfabb is finished repairing model (blocking proccess)
|
||||||
NetfabbFinished,
|
NetfabbFinished,
|
||||||
// Short meesage to fill space between start and finish of export
|
// Short meesage to fill space between start and finish of export
|
||||||
|
|
|
@ -74,12 +74,14 @@ const std::map<InfoItemType, InfoItemAtributes> INFO_ITEMS{
|
||||||
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||||
const wxString& sub_obj_name,
|
const wxString& sub_obj_name,
|
||||||
Slic3r::ModelVolumeType type,
|
Slic3r::ModelVolumeType type,
|
||||||
|
const bool is_text_volume,
|
||||||
const wxString& extruder,
|
const wxString& extruder,
|
||||||
const int idx/* = -1*/) :
|
const int idx/* = -1*/) :
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_name(sub_obj_name),
|
m_name(sub_obj_name),
|
||||||
m_type(itVolume),
|
m_type(itVolume),
|
||||||
m_volume_type(type),
|
m_volume_type(type),
|
||||||
|
m_is_text_volume(is_text_volume),
|
||||||
m_idx(idx),
|
m_idx(idx),
|
||||||
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
|
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
|
||||||
{
|
{
|
||||||
|
@ -330,6 +332,7 @@ static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType roo
|
||||||
ObjectDataViewModel::ObjectDataViewModel()
|
ObjectDataViewModel::ObjectDataViewModel()
|
||||||
{
|
{
|
||||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||||
|
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
|
||||||
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
||||||
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
||||||
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
||||||
|
@ -352,7 +355,7 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node)
|
||||||
bool is_volume_node = vol_type >= 0;
|
bool is_volume_node = vol_type >= 0;
|
||||||
|
|
||||||
if (!node->has_warning_icon() && !node->has_lock()) {
|
if (!node->has_warning_icon() && !node->has_lock()) {
|
||||||
node->SetBitmap(is_volume_node ? *m_volume_bmps.at(vol_type) : m_empty_bmp);
|
node->SetBitmap(is_volume_node ? (node->is_text_volume() ? *m_text_volume_bmps.at(vol_type) : *m_volume_bmps.at(vol_type)) : m_empty_bmp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +376,7 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node)
|
||||||
if (node->has_lock())
|
if (node->has_lock())
|
||||||
bmps.emplace_back(&m_lock_bmp);
|
bmps.emplace_back(&m_lock_bmp);
|
||||||
if (is_volume_node)
|
if (is_volume_node)
|
||||||
bmps.emplace_back(m_volume_bmps[vol_type]);
|
bmps.emplace_back(node->is_text_volume() ? m_text_volume_bmps[vol_type] : m_volume_bmps[vol_type]);
|
||||||
bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps);
|
bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,6 +412,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||||
const wxString &name,
|
const wxString &name,
|
||||||
const int volume_idx,
|
const int volume_idx,
|
||||||
const Slic3r::ModelVolumeType volume_type,
|
const Slic3r::ModelVolumeType volume_type,
|
||||||
|
const bool is_text_volume,
|
||||||
const std::string& warning_icon_name,
|
const std::string& warning_icon_name,
|
||||||
const wxString& extruder)
|
const wxString& extruder)
|
||||||
{
|
{
|
||||||
|
@ -420,7 +424,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||||
if (insert_position < 0)
|
if (insert_position < 0)
|
||||||
insert_position = get_root_idx(root, itInstanceRoot);
|
insert_position = get_root_idx(root, itInstanceRoot);
|
||||||
|
|
||||||
const auto node = new ObjectDataViewModelNode(root, name, volume_type, extruder, volume_idx);
|
const auto node = new ObjectDataViewModelNode(root, name, volume_type, is_text_volume, extruder, volume_idx);
|
||||||
UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER);
|
UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER);
|
||||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||||
|
|
||||||
|
@ -769,8 +773,12 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
|
||||||
// get index of the last VolumeItem in CildrenList
|
// get index of the last VolumeItem in CildrenList
|
||||||
size_t vol_idx = GetItemIndexForFirstVolume(node_parent);
|
size_t vol_idx = GetItemIndexForFirstVolume(node_parent);
|
||||||
|
|
||||||
// delete this last volume
|
|
||||||
ObjectDataViewModelNode *last_child_node = node_parent->GetNthChild(vol_idx);
|
ObjectDataViewModelNode *last_child_node = node_parent->GetNthChild(vol_idx);
|
||||||
|
// if last volume is text then don't delete it
|
||||||
|
if (last_child_node->is_text_volume())
|
||||||
|
return parent;
|
||||||
|
|
||||||
|
// delete this last volume
|
||||||
DeleteSettings(wxDataViewItem(last_child_node));
|
DeleteSettings(wxDataViewItem(last_child_node));
|
||||||
node_parent->GetChildren().Remove(last_child_node);
|
node_parent->GetChildren().Remove(last_child_node);
|
||||||
node_parent->m_volumes_cnt = 0;
|
node_parent->m_volumes_cnt = 0;
|
||||||
|
@ -1616,22 +1624,6 @@ void ObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item,
|
||||||
ItemChanged(item);
|
ItemChanged(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType volume_type)
|
|
||||||
{
|
|
||||||
if (!item.IsOk() || GetItemType(item) != itVolume)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
|
||||||
node->SetVolumeType(volume_type);
|
|
||||||
node->SetBitmap(*m_volume_bmps[int(volume_type)]);
|
|
||||||
if (volume_type != Slic3r::ModelVolumeType::MODEL_PART && volume_type != Slic3r::ModelVolumeType::PARAMETER_MODIFIER)
|
|
||||||
node->SetExtruder(""); // hide extruder
|
|
||||||
else if (node->GetExtruder().IsEmpty())
|
|
||||||
node->SetExtruder("default"); // show extruder ans set it to default
|
|
||||||
node->UpdateExtruderAndColorIcon();
|
|
||||||
ItemChanged(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelVolumeType ObjectDataViewModel::GetVolumeType(const wxDataViewItem& item)
|
ModelVolumeType ObjectDataViewModel::GetVolumeType(const wxDataViewItem& item)
|
||||||
{
|
{
|
||||||
if (!item.IsOk() || GetItemType(item) != itVolume)
|
if (!item.IsOk() || GetItemType(item) != itVolume)
|
||||||
|
@ -1684,6 +1676,7 @@ wxDataViewItem ObjectDataViewModel::SetObjectPrintableState(
|
||||||
void ObjectDataViewModel::UpdateBitmaps()
|
void ObjectDataViewModel::UpdateBitmaps()
|
||||||
{
|
{
|
||||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||||
|
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
|
||||||
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
||||||
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
|
||||||
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
m_lock_bmp = *get_bmp_bundle(LockIcon);
|
||||||
|
|
|
@ -84,6 +84,7 @@ class ObjectDataViewModelNode
|
||||||
|
|
||||||
std::string m_action_icon_name = "";
|
std::string m_action_icon_name = "";
|
||||||
ModelVolumeType m_volume_type{ -1 };
|
ModelVolumeType m_volume_type{ -1 };
|
||||||
|
bool m_is_text_volume{ false };
|
||||||
InfoItemType m_info_item_type {InfoItemType::Undef};
|
InfoItemType m_info_item_type {InfoItemType::Undef};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -101,6 +102,7 @@ public:
|
||||||
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||||
const wxString& sub_obj_name,
|
const wxString& sub_obj_name,
|
||||||
Slic3r::ModelVolumeType type,
|
Slic3r::ModelVolumeType type,
|
||||||
|
const bool is_text_volume,
|
||||||
const wxString& extruder,
|
const wxString& extruder,
|
||||||
const int idx = -1 );
|
const int idx = -1 );
|
||||||
|
|
||||||
|
@ -191,7 +193,7 @@ public:
|
||||||
InfoItemType GetInfoItemType() const { return m_info_item_type; }
|
InfoItemType GetInfoItemType() const { return m_info_item_type; }
|
||||||
void SetIdx(const int& idx);
|
void SetIdx(const int& idx);
|
||||||
int GetIdx() const { return m_idx; }
|
int GetIdx() const { return m_idx; }
|
||||||
ModelVolumeType GetVolumeType() { return m_volume_type; }
|
ModelVolumeType GetVolumeType() const { return m_volume_type; }
|
||||||
t_layer_height_range GetLayerRange() const { return m_layer_range; }
|
t_layer_height_range GetLayerRange() const { return m_layer_range; }
|
||||||
wxString GetExtruder() { return m_extruder; }
|
wxString GetExtruder() { return m_extruder; }
|
||||||
PrintIndicator IsPrintable() const { return m_printable; }
|
PrintIndicator IsPrintable() const { return m_printable; }
|
||||||
|
@ -235,6 +237,7 @@ public:
|
||||||
void update_settings_digest_bitmaps();
|
void update_settings_digest_bitmaps();
|
||||||
bool update_settings_digest(const std::vector<std::string>& categories);
|
bool update_settings_digest(const std::vector<std::string>& categories);
|
||||||
int volume_type() const { return int(m_volume_type); }
|
int volume_type() const { return int(m_volume_type); }
|
||||||
|
bool is_text_volume() const { return m_is_text_volume; }
|
||||||
void sys_color_changed();
|
void sys_color_changed();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -261,6 +264,7 @@ class ObjectDataViewModel :public wxDataViewModel
|
||||||
{
|
{
|
||||||
std::vector<ObjectDataViewModelNode*> m_objects;
|
std::vector<ObjectDataViewModelNode*> m_objects;
|
||||||
std::vector<wxBitmapBundle*> m_volume_bmps;
|
std::vector<wxBitmapBundle*> m_volume_bmps;
|
||||||
|
std::vector<wxBitmapBundle*> m_text_volume_bmps;
|
||||||
std::map<InfoItemType, wxBitmapBundle*> m_info_bmps;
|
std::map<InfoItemType, wxBitmapBundle*> m_info_bmps;
|
||||||
wxBitmapBundle m_empty_bmp;
|
wxBitmapBundle m_empty_bmp;
|
||||||
wxBitmapBundle m_warning_bmp;
|
wxBitmapBundle m_warning_bmp;
|
||||||
|
@ -281,6 +285,7 @@ public:
|
||||||
const wxString &name,
|
const wxString &name,
|
||||||
const int volume_idx,
|
const int volume_idx,
|
||||||
const Slic3r::ModelVolumeType volume_type,
|
const Slic3r::ModelVolumeType volume_type,
|
||||||
|
const bool is_text_volume,
|
||||||
const std::string& warning_icon_name,
|
const std::string& warning_icon_name,
|
||||||
const wxString& extruder);
|
const wxString& extruder);
|
||||||
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||||
|
@ -379,7 +384,6 @@ public:
|
||||||
void UpdateObjectPrintable(wxDataViewItem parent_item);
|
void UpdateObjectPrintable(wxDataViewItem parent_item);
|
||||||
void UpdateInstancesPrintable(wxDataViewItem parent_item);
|
void UpdateInstancesPrintable(wxDataViewItem parent_item);
|
||||||
|
|
||||||
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
|
|
||||||
ModelVolumeType GetVolumeType(const wxDataViewItem &item);
|
ModelVolumeType GetVolumeType(const wxDataViewItem &item);
|
||||||
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
|
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
|
||||||
int subobj_idx = -1,
|
int subobj_idx = -1,
|
||||||
|
@ -387,11 +391,9 @@ public:
|
||||||
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
|
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
|
||||||
|
|
||||||
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
|
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
|
||||||
// Rescale bitmaps for existing Items
|
// Rescale bitmaps for existing Items
|
||||||
void UpdateBitmaps();
|
void UpdateBitmaps();
|
||||||
|
|
||||||
wxBitmapBundle GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
|
|
||||||
const std::string& warning_icon_name = std::string());
|
|
||||||
void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
||||||
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
||||||
void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
||||||
|
|
|
@ -4505,19 +4505,29 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||||
#else
|
#else
|
||||||
const bool is_part = selection.is_single_volume() || selection.is_single_modifier();
|
const bool is_part = selection.is_single_volume() || selection.is_single_modifier();
|
||||||
#endif // ENABLE_WORLD_COORDINATE
|
#endif // ENABLE_WORLD_COORDINATE
|
||||||
menu = is_some_full_instances ? menus.object_menu() :
|
if (is_some_full_instances)
|
||||||
is_part ? menus.part_menu() : menus.multi_selection_menu();
|
menu = menus.object_menu();
|
||||||
|
else if (is_part)
|
||||||
|
menu = selection.is_single_text() ? menus.text_part_menu() : menus.part_menu();
|
||||||
|
else
|
||||||
|
menu = menus.multi_selection_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (q != nullptr && menu) {
|
if (q != nullptr && menu) {
|
||||||
|
Vec2d mouse_position = evt.data.first;
|
||||||
|
wxPoint position(static_cast<int>(mouse_position.x()),
|
||||||
|
static_cast<int>(mouse_position.y()));
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// For some reason on Linux the menu isn't displayed if position is specified
|
// For some reason on Linux the menu isn't displayed if position is
|
||||||
// (even though the position is sane).
|
// specified (even though the position is sane).
|
||||||
q->PopupMenu(menu);
|
position = wxDefaultPosition;
|
||||||
#else
|
|
||||||
q->PopupMenu(menu, (int)evt.data.first.x(), (int)evt.data.first.y());
|
|
||||||
#endif
|
#endif
|
||||||
|
GLCanvas3D &canvas = *q->canvas3D();
|
||||||
|
canvas.apply_retina_scale(mouse_position);
|
||||||
|
canvas.set_popup_menu_position(mouse_position);
|
||||||
|
q->PopupMenu(menu, position);
|
||||||
|
canvas.clear_popup_menu_position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4934,6 +4944,10 @@ bool Plater::priv::can_increase_instances() const
|
||||||
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Disallow arrange and add instance when emboss gizmo is opend
|
||||||
|
// Prevent strobo effect during editing emboss parameters.
|
||||||
|
if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false;
|
||||||
|
|
||||||
const int obj_idx = get_selected_object_idx();
|
const int obj_idx = get_selected_object_idx();
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) &&
|
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) &&
|
||||||
!sidebar->obj_list()->has_selected_cut_object();
|
!sidebar->obj_list()->has_selected_cut_object();
|
||||||
|
@ -4963,7 +4977,9 @@ bool Plater::priv::can_split_to_volumes() const
|
||||||
|
|
||||||
bool Plater::priv::can_arrange() const
|
bool Plater::priv::can_arrange() const
|
||||||
{
|
{
|
||||||
return !model.objects.empty() && m_worker.is_idle();
|
if (model.objects.empty() && m_worker.is_idle()) return false;
|
||||||
|
if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::priv::can_layers_editing() const
|
bool Plater::priv::can_layers_editing() const
|
||||||
|
@ -7244,7 +7260,7 @@ bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
|
||||||
SuppressBackgroundProcessingUpdate sbpu;
|
SuppressBackgroundProcessingUpdate sbpu;
|
||||||
// When tracking a pop-up menu, postpone error messages from the slicing result.
|
// When tracking a pop-up menu, postpone error messages from the slicing result.
|
||||||
m_tracking_popup_menu = true;
|
m_tracking_popup_menu = true;
|
||||||
bool out = this->wxPanel::PopupMenu(menu, pos);
|
bool out = this->wxPanel::PopupMenu(menu, pos);
|
||||||
m_tracking_popup_menu = false;
|
m_tracking_popup_menu = false;
|
||||||
if (! m_tracking_popup_menu_error_message.empty()) {
|
if (! m_tracking_popup_menu_error_message.empty()) {
|
||||||
// Don't know whether the CallAfter is necessary, but it should not hurt.
|
// Don't know whether the CallAfter is necessary, but it should not hurt.
|
||||||
|
@ -7262,6 +7278,7 @@ void Plater::bring_instance_forward()
|
||||||
|
|
||||||
wxMenu* Plater::object_menu() { return p->menus.object_menu(); }
|
wxMenu* Plater::object_menu() { return p->menus.object_menu(); }
|
||||||
wxMenu* Plater::part_menu() { return p->menus.part_menu(); }
|
wxMenu* Plater::part_menu() { return p->menus.part_menu(); }
|
||||||
|
wxMenu* Plater::text_part_menu() { return p->menus.text_part_menu(); }
|
||||||
wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); }
|
wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); }
|
||||||
wxMenu* Plater::default_menu() { return p->menus.default_menu(); }
|
wxMenu* Plater::default_menu() { return p->menus.default_menu(); }
|
||||||
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
||||||
|
|
|
@ -463,6 +463,7 @@ public:
|
||||||
// get same Plater/ObjectList menus
|
// get same Plater/ObjectList menus
|
||||||
wxMenu* object_menu();
|
wxMenu* object_menu();
|
||||||
wxMenu* part_menu();
|
wxMenu* part_menu();
|
||||||
|
wxMenu* text_part_menu();
|
||||||
wxMenu* sla_object_menu();
|
wxMenu* sla_object_menu();
|
||||||
wxMenu* default_menu();
|
wxMenu* default_menu();
|
||||||
wxMenu* instance_menu();
|
wxMenu* instance_menu();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue