Merge remote-tracking branch 'PRIVATE/master' into ys_emboss_with_master

This commit is contained in:
YuSanka 2022-10-27 13:09:55 +02:00
commit fbcdd4191b
65 changed files with 6783 additions and 2921 deletions

View File

@ -43,6 +43,14 @@ set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux")
set(IS_CROSS_COMPILE FALSE)
if (SLIC3R_STATIC)
# Prefer config scripts over find modules. This is helpful when building with
# the static dependencies. Many libraries have their own export scripts
# while having a Find<PkgName> module in standard cmake installation.
# (e.g. CURL)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
endif ()
if (APPLE)
set(CMAKE_FIND_FRAMEWORK LAST)
set(CMAKE_FIND_APPBUNDLE LAST)
@ -106,6 +114,8 @@ if (MSVC)
# C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data. An integer type is converted to a smaller integer type.
# C4267: The compiler detected a conversion from size_t to a smaller type.
add_compile_options(/wd4244 /wd4267)
# Enforce strict C++ conformance, so our code that compiles on MSVC also compiles on GCC and clang.
add_compile_options(/permissive-)
endif ()
if (MINGW)
@ -435,6 +445,14 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR})
# no matter what.
find_package(EXPAT REQUIRED)
add_library(libexpat INTERFACE)
if (TARGET EXPAT::EXPAT )
target_link_libraries(libexpat INTERFACE EXPAT::EXPAT)
elseif(TARGET expat::expat)
target_link_libraries(libexpat INTERFACE expat::expat)
endif ()
find_package(PNG REQUIRED)
set(OpenGL_GL_PREFERENCE "LEGACY")

View File

@ -13,8 +13,8 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for
endif()
prusaslicer_add_cmake_project(wxWidgets
URL https://github.com/prusa3d/wxWidgets/archive/2a0b365df947138c513a888d707d46248d78a341.zip
URL_HASH SHA256=9ab05cd5179196fad4ae702c78eaae9418e73a402cfd390f7438e469b13eb735
URL https://github.com/prusa3d/wxWidgets/archive/34b524f8d5134a40a90d93a16360d533af2676ae.zip
URL_HASH SHA256=e76ca0dd998905c4dbb86f41f264e6e0468504dc2398f7e7e3bba8dc37de2f45
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG
CMAKE_ARGS
-DwxBUILD_PRECOMP=ON

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="expand">
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="4" x2="12" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="12" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="12"/></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 919 B

28
resources/icons/cut_.svg Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="cut">
<g>
<path fill="#ED6B21" d="M118.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S118.95,65.5,118.12,65.5z M98.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S98.95,65.5,98.12,65.5z M78.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S78.95,65.5,78.12,65.5z M58.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S58.95,65.5,58.12,65.5z M38.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S38.95,65.5,38.12,65.5z M18.12,65.5h-10c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5h10c0.83,0,1.5,0.67,1.5,1.5
S18.95,65.5,18.12,65.5z"/>
</g>
<g>
<g>
<path fill="#808080" d="M108.79,51.6H19.21c-1.93,0-3.5-1.57-3.5-3.5V10.12c0-1.93,1.57-3.5,3.5-3.5h89.57
c1.93,0,3.5,1.57,3.5,3.5V48.1C112.29,50.03,110.71,51.6,108.79,51.6z M19.21,9.62c-0.27,0-0.5,0.23-0.5,0.5V48.1
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V10.12c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
</g>
<g>
<path fill="#808080" d="M108.79,121.38H19.21c-1.93,0-3.5-1.57-3.5-3.5V79.4c0-1.93,1.57-3.5,3.5-3.5h89.57
c1.93,0,3.5,1.57,3.5,3.5v38.49C112.29,119.81,110.71,121.38,108.79,121.38z M19.21,78.9c-0.27,0-0.5,0.23-0.5,0.5v38.49
c0,0.27,0.23,0.5,0.5,0.5h89.57c0.27,0,0.5-0.23,0.5-0.5V79.4c0-0.27-0.23-0.5-0.5-0.5H19.21z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="add_x5F_part">
<g>
<path fill="#ED6B21" d="M14.62,4.37c-0.01-0.14,0.06-0.34,0.15-0.44l0.13-0.15c0.09-0.11,0.12-0.3,0.07-0.43l-0.2-0.49
c-0.05-0.13-0.21-0.24-0.35-0.25l-0.2-0.01c-0.14-0.01-0.33-0.1-0.42-0.21c-0.09-0.1-0.37-0.46-0.38-0.6l-0.01-0.2
c-0.01-0.14-0.12-0.3-0.25-0.35l-0.49-0.2C12.52,0.97,12.33,1,12.22,1.1l-0.15,0.13c-0.11,0.09-0.31,0.16-0.44,0.15
c-0.14-0.01-0.59-0.06-0.69-0.15L10.78,1.1c-0.11-0.09-0.3-0.12-0.43-0.07l-0.49,0.2C9.73,1.28,9.61,1.44,9.6,1.58l-0.01,0.2
C9.58,1.92,9.49,2.11,9.38,2.2c-0.1,0.09-0.46,0.37-0.6,0.38L8.58,2.6c-0.14,0.01-0.3,0.12-0.35,0.25l-0.2,0.49
C7.97,3.48,8,3.67,8.1,3.78l0.13,0.15c0.09,0.11,0.16,0.31,0.15,0.44C8.37,4.52,8.32,4.96,8.23,5.07L8.1,5.22
C8,5.33,7.97,5.52,8.03,5.65l0.2,0.49C8.28,6.27,8.44,6.39,8.58,6.4l0.2,0.01c0.14,0.01,0.33,0.1,0.42,0.21
c0.09,0.1,0.37,0.46,0.38,0.6l0.01,0.2c0.01,0.14,0.12,0.3,0.25,0.35l0.49,0.2C10.48,8.03,10.67,8,10.78,7.9l0.15-0.13
c0.11-0.09,0.31-0.16,0.44-0.15c0.14,0.01,0.59,0.06,0.69,0.15l0.15,0.13c0.11,0.09,0.3,0.12,0.43,0.07l0.49-0.2
c0.13-0.05,0.24-0.21,0.25-0.35l0.01-0.2c0.01-0.14,0.1-0.33,0.21-0.42s0.46-0.37,0.6-0.38l0.2-0.01c0.14-0.01,0.3-0.12,0.35-0.25
l0.2-0.49C15.03,5.52,15,5.33,14.9,5.22l-0.13-0.15C14.68,4.96,14.63,4.51,14.62,4.37z M11.5,6.6c-1.16,0-2.1-0.94-2.1-2.1
s0.94-2.1,2.1-2.1s2.1,0.94,2.1,2.1S12.66,6.6,11.5,6.6z"/>
</g>
<path fill="#808080" d="M10.98,9.78c-0.29,0-0.52,0.23-0.52,0.52v2.09v1.04c0,0.29-0.23,0.52-0.52,0.52H2.62
c-0.29,0-0.53-0.24-0.53-0.53L2.04,6.12c0-0.14,0.05-0.27,0.15-0.37c0.1-0.1,0.23-0.15,0.37-0.15l3.19,0v0
c0.29,0,0.52-0.23,0.52-0.52S6.04,4.55,5.75,4.55H3.66c-0.01,0-0.01,0-0.02,0l-1.08,0c-0.42,0-0.81,0.16-1.11,0.46
C1.16,5.31,1,5.71,1,6.13l0.04,7.31C1.05,14.3,1.75,15,2.62,15h7.31c0.86,0,1.57-0.7,1.57-1.57v-1.04V10.3
C11.5,10.01,11.27,9.78,10.98,9.78z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="expand">
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="4" x2="8" y2="8"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="8" x2="12" y2="4"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="8" x2="8" y2="12"/></g>
<g><line fill="none" stroke="#FFFFFF" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="12" x2="12" y2="8"/></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 915 B

View File

@ -73,7 +73,7 @@ msgid ""
msgstr ""
"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, "
"Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and "
"numerous others. 한국어 번역 울산에테르, 밤송이직박구리"
"numerous others. 한국어 번역 울산에테르, 밤송이직박구리,brightstone song"
#: src/slic3r/GUI/AboutDialog.cpp:310
msgid "Copy Version Info"
@ -93,13 +93,13 @@ msgstr ""
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:84
#, boost-format
msgid "PrusaSlicer has encountered a fatal error: \"%1%\""
msgstr ""
msgstr "슬라이서에 치명적인 오류가 발생했습니다: \"%1%\""
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:85
msgid ""
"Please save your project and restart PrusaSlicer. We would be glad if you "
"reported the issue."
msgstr ""
msgstr "작업물을 저장하시고 슬라이서를 재시작하시기 바랍니다. 이 이슈를 보고해주시면 감사하겠습니다."
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:162
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:204
@ -109,27 +109,27 @@ msgstr "슬라이스 완료"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:199
#, boost-format
msgid "Masked SLA file exported to %1%"
msgstr "마스크 된 SLA 파일을 %1%로 내보냅니"
msgstr "마스크 된 SLA 파일을 %1%로 내보냅니다."
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:286
msgid "Access violation"
msgstr ""
msgstr "접근 위반"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:288
msgid "Illegal instruction"
msgstr ""
msgstr "잘못된 명령"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:290
msgid "Divide by zero"
msgstr ""
msgstr "0"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:292
msgid "Overflow"
msgstr ""
msgstr "오버플로우"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:294
msgid "Underflow"
msgstr ""
msgstr "언더플로우"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:297
msgid "Floating reserved operand"
@ -137,7 +137,7 @@ msgstr ""
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:300
msgid "Stack overflow"
msgstr ""
msgstr "스택 오버플로우"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:659
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:726
@ -147,7 +147,7 @@ msgstr "포스트 프로세싱 스크립트"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:690
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:710
msgid "Unknown error occured during exporting G-code."
msgstr ""
msgstr "G고드 제작중 알 수 없는 오류가 발생"
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:695
#, boost-format
@ -189,7 +189,7 @@ msgstr ""
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:715
#, boost-format
msgid "G-code file exported to %1%"
msgstr "%1%로 내보낸 G 코드 파일"
msgstr "%1%로 G코드 파일이 저장되었습니다."
#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:729
msgid "Copying of the temporary G-code to the output G-code failed"
@ -218,13 +218,13 @@ msgstr "노즐 직경"
#: src/slic3r/GUI/BedShapeDialog.cpp:49
msgid "Size in X and Y of the rectangular plate."
msgstr "사각 플레이트 X 및 Y 크기."
msgstr "사각형 베드의 X와 Y크기."
#: src/slic3r/GUI/BedShapeDialog.cpp:58
msgid ""
"Distance of the 0,0 G-code coordinate from the front left corner of the "
"rectangle."
msgstr "사각 전면 왼쪽 모서리에서 원저(0, 0) G-code 좌표 거리입니다."
msgstr "전면부 왼쪽 모서리부터 원점까지의(0, 0) G-code 좌표 거리입니다."
#: src/slic3r/GUI/BedShapeDialog.cpp:64 src/slic3r/GUI/ConfigWizard.cpp:262
#: src/slic3r/GUI/ConfigWizard.cpp:1476 src/slic3r/GUI/ConfigWizard.cpp:1490
@ -281,7 +281,7 @@ msgstr "mm"
msgid ""
"Diameter of the print bed. It is assumed that origin (0,0) is located in the "
"center."
msgstr "인쇄 배드의 직경. 원점 (0,0) 은 중재봉선에 있다고 가정합니다."
msgstr "인쇄 배드의 직경. 원점 (0,0) 은 중앙에 있다고 가정됩니다."
#: src/slic3r/GUI/BedShapeDialog.cpp:79
msgid "Rectangular"
@ -303,7 +303,7 @@ msgstr "모양"
#: src/slic3r/GUI/BedShapeDialog.cpp:203
msgid "Load shape from STL..."
msgstr "STL파일 로드."
msgstr "STL파일 로드하기."
#: src/slic3r/GUI/BedShapeDialog.cpp:249 src/slic3r/GUI/GCodeViewer.cpp:3709
#: src/slic3r/GUI/MainFrame.cpp:2147
@ -325,7 +325,7 @@ msgstr "제거"
#: src/slic3r/GUI/BedShapeDialog.cpp:317 src/slic3r/GUI/BedShapeDialog.cpp:388
msgid "Not found:"
msgstr ""
msgstr "찾지 못함"
#: src/slic3r/GUI/BedShapeDialog.cpp:344
msgid "Model"
@ -346,25 +346,25 @@ msgstr "오류! 잘못된 모델"
#: src/slic3r/GUI/BedShapeDialog.cpp:533
msgid "The selected file contains no geometry."
msgstr "선택한 파일에 없는 형상이 있습니다."
msgstr "선택한 파일에는 형상이 존재하지 않습니다."
#: src/slic3r/GUI/BedShapeDialog.cpp:537
msgid ""
"The selected file contains several disjoint areas. This is not supported."
msgstr ""
"선택한 파일은 여러개의 분리 된 영역을 포함 되어 있어 지원 되지 않습니다."
"이 파일은 몇몇 끊어진 부분이 있습니다. 지원이 불가능합니다."
#: src/slic3r/GUI/BedShapeDialog.cpp:552
msgid "Choose a file to import bed texture from (PNG/SVG):"
msgstr "(PNG /SVG)에서 배드 텍스처를 가져올 파일을 선택합니다."
msgstr "베드 텍스처를 가져올 (PNG /SVG)파일을 선택하십시오."
#: src/slic3r/GUI/BedShapeDialog.cpp:574
msgid "Choose an STL file to import bed model from:"
msgstr "다음에서 베드 모델을 가져올 STL 파일을 선택합니다:"
msgstr "베드 모델을 가져올 STL 파일을 선택하십시오:"
#: src/slic3r/GUI/BedShapeDialog.hpp:95 src/slic3r/GUI/ConfigWizard.cpp:1396
msgid "Bed Shape"
msgstr "드 모양"
msgstr "드 모양"
#: src/slic3r/GUI/BonjourDialog.cpp:55
msgid "Network lookup"
@ -376,7 +376,7 @@ msgstr "주소"
#: src/slic3r/GUI/BonjourDialog.cpp:73
msgid "Hostname"
msgstr "호스트이름"
msgstr "호스트 이름"
#: src/slic3r/GUI/BonjourDialog.cpp:74
msgid "Service name"
@ -388,7 +388,7 @@ msgstr "옥토프린트 버전"
#: src/slic3r/GUI/BonjourDialog.cpp:224
msgid "Searching for devices"
msgstr "디바이스 검색"
msgstr "디바이스 검색"
#: src/slic3r/GUI/BonjourDialog.cpp:231
msgid "Finished"
@ -406,7 +406,7 @@ msgstr "이 값은 시스템 값과 같습니다"
msgid ""
"Value was changed and is not equal to the system value or the last saved "
"preset"
msgstr "값이 변경 되었고, 시스템 값 또는 마지막으로 저장된 설정값과 다릅니다."
msgstr "수치가 변경 되었고, 시스템 값 또는 마지막으로 저장된 설정값과 다릅니다."
#: src/slic3r/GUI/ButtonsDescription.cpp:62
msgid "Buttons And Text Colors Description"
@ -418,6 +418,9 @@ msgid ""
"\n"
"The layer height will be reset to 0.01."
msgstr ""
"레이어 높이가 유호하지 않습니다.\n"
"\n"
"높이가 0.01로 재설정됩니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:50
#: src/slic3r/GUI/GUI_ObjectLayers.cpp:29 src/slic3r/GUI/Tab.cpp:1449
@ -431,6 +434,9 @@ msgid ""
"\n"
"The first layer height will be reset to 0.01."
msgstr ""
"첫 레이어 높이가 유호하지 않습니다.\n"
"\n"
"첫 레이어 높이가 0.01로 재설정됩니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:62 src/libslic3r/PrintConfig.cpp:1227
msgid "First layer height"
@ -446,14 +452,21 @@ msgid ""
"- Ensure vertical shell thickness enabled\n"
"- Detect thin walls disabled"
msgstr ""
"꽃병 모드는 다음과 같은 설정이 필요합니다:\n"
"- 외벽 1\n"
"- 상부 레이어 없음\n"
"- 내부 밀도 0%\n"
"- 서포트 없음\n"
"- 외벽 두께 보장 활성화\n"
"- 얇은 외벽 감지 비활성화"
#: src/slic3r/GUI/ConfigManipulation.cpp:90
msgid "Shall I adjust those settings in order to enable Spiral Vase?"
msgstr "나선형 꽃병을 활성화하기 위해 이러한 설정을 조정해야 합니까?"
msgstr "꽃병 모드를 활성화하기 위해 이 설정들을 변경하시겠습니까?"
#: src/slic3r/GUI/ConfigManipulation.cpp:91
msgid "Spiral Vase"
msgstr "스파이럴 바이스"
msgstr "꾳병 모드"
#: src/slic3r/GUI/ConfigManipulation.cpp:121
msgid ""
@ -463,14 +476,14 @@ msgid ""
"(both support_material_extruder and support_material_interface_extruder need "
"to be set to 0)."
msgstr ""
"와이프 타워(프라임 타워)는 현재 비수용성 지원만 지원합니다.\n"
"공구 교환을 트리거하지 않고 현재 압출기로 인쇄된 경우\n"
"(support_material_extruder support_material_interface_extruder 모두 0으로 설"
"와이프 타워(프라임 타워)는 현재 비수용성 서포트만 지원합니다.\n"
"툴체인지를 사용하지 않고 현재 노즐로 출력시\n"
"(support_material_extruder support_material_interface_extruder 모두 0으로 설"
"정해야 합니다)."
#: src/slic3r/GUI/ConfigManipulation.cpp:125
msgid "Shall I adjust those settings in order to enable the Wipe Tower?"
msgstr "와이프 타워를 활성화하기 위해 이러한 설정을 조정해야 합니까?"
msgstr "와이프 타워를 활성화하기 위해 이 설정들을 변경하시겠습니까?"
#: src/slic3r/GUI/ConfigManipulation.cpp:126
#: src/slic3r/GUI/ConfigManipulation.cpp:146
@ -482,37 +495,37 @@ msgid ""
"For the Wipe Tower to work with the soluble supports, the support layers\n"
"need to be synchronized with the object layers."
msgstr ""
"와이프 타워(프라임 타워)가 가용성 지지체와 함께 작동 하려면 서포트 레이어를 "
"와이프 타워(프라임 타워)를 수용성 서포트와 함께 사용하기 위해서는 서포트 레이어를 "
"객체(object) 레이어와 동기화 해야 합니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:145
msgid "Shall I synchronize support layers in order to enable the Wipe Tower?"
msgstr "와이프 타워를 활성화하기 위해 지원 레이어를 동기화해야 합니까?"
msgstr "와이프 타워를 사용하기 위해 서포트 레이어 설정을 변경하시겠습니까?"
#: src/slic3r/GUI/ConfigManipulation.cpp:164
msgid ""
"Supports work better, if the following feature is enabled:\n"
"- Detect bridging perimeters"
msgstr ""
"다음 기능이 활성화된 경우 더 나은 작업을 지원합니다.\n"
"- 브리징 경계를 감지"
"서포트는 다음 기능이 활성화되어 있으면 더 효율적으로 생성됩니다.\n"
"- 브릿징 동작 감지"
#: src/slic3r/GUI/ConfigManipulation.cpp:167
msgid "Shall I adjust those settings for supports?"
msgstr "지원에 대한 설정을 조정해야 합니까?"
msgstr "서포트 세팅을 변경하시겠습니까?"
#: src/slic3r/GUI/ConfigManipulation.cpp:168
msgid "Support Generator"
msgstr "서포트 생성"
msgstr "서포트 생성"
#: src/slic3r/GUI/ConfigManipulation.cpp:195
#, boost-format
msgid "The %1% infill pattern is not supposed to work at 100%% density."
msgstr "%1% 채우기 패턴은 100%% 밀도로 작동하도록 되어 있지 않습니다."
msgstr "%1% 패턴은 100%% 밀도로 작동하도록 되어 있지 않습니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:198
msgid "Shall I switch to rectilinear fill pattern?"
msgstr "직선 채우기 패턴으로 전환해야 합니까?"
msgstr "직선 패턴으로 전환하시겠습니까?"
#: src/slic3r/GUI/ConfigManipulation.cpp:199
#: src/slic3r/GUI/GUI_Factories.cpp:55 src/slic3r/GUI/GUI_Factories.cpp:128
@ -525,19 +538,19 @@ msgstr "직선 채우기 패턴으로 전환해야 합니까?"
#: src/libslic3r/PrintConfig.cpp:1493 src/libslic3r/PrintConfig.cpp:1512
#: src/libslic3r/PrintConfig.cpp:2333 src/libslic3r/PrintConfig.cpp:2350
msgid "Infill"
msgstr "인필(채움)"
msgstr "인필"
#: src/slic3r/GUI/ConfigManipulation.cpp:336
msgid "Head penetration should not be greater than the head width."
msgstr "헤드 관통은 헤드 폭 보다 크지 않아야 합니다."
msgstr "헤드 접촉길이는 헤드의 지름보다 클 수 없습니다."
#: src/slic3r/GUI/ConfigManipulation.cpp:338
msgid "Invalid Head penetration"
msgstr "잘못된 헤드 관통"
msgstr "헤드 관통 불가"
#: src/slic3r/GUI/ConfigManipulation.cpp:349
msgid "Pinhead diameter should be smaller than the pillar diameter."
msgstr "핀헤드 지름은 기둥 지름 보다 작아야 합니다."
msgstr "핀헤드 지름은 기둥 지름보다 클 수 ."
#: src/slic3r/GUI/ConfigManipulation.cpp:351
msgid "Invalid pinhead diameter"
@ -596,7 +609,7 @@ msgstr "프린터"
#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:75 src/slic3r/GUI/Tab.cpp:1366
msgid "vendor"
msgstr "제조사"
msgstr "제조사"
#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:75
msgid "version"
@ -641,7 +654,7 @@ msgstr "대체 노즐:"
#: src/slic3r/GUI/ConfigWizard.cpp:330
msgid "All standard"
msgstr "모두 표준설정"
msgstr "전부 표준설정으로"
#: src/slic3r/GUI/ConfigWizard.cpp:330
msgid "Standard"
@ -680,12 +693,12 @@ msgid ""
"Hello, welcome to %s! This %s helps you with the initial configuration; just "
"a few settings and you will be ready to print."
msgstr ""
"안녕하세요 ,%s에 오신 것을 환영 합니다! 이 %s는 초기 구성에 도움이 됩니다. "
"몇 가지 설정만으로 인쇄 준비가 될 것입니다."
"안녕하세요 ,%s에 오신 것을 환영 합니다! 이 %s는 초기 설정에 도움이 됩니다. "
"몇 가지 설정 후 프린팅할 준비가 될 것입니다."
#: src/slic3r/GUI/ConfigWizard.cpp:495
msgid "Remove user profiles (a snapshot will be taken beforehand)"
msgstr ""
msgstr "유저 프로필 제거(제거 전 스냅샷이 생성될 것입니다.)"
#: src/slic3r/GUI/ConfigWizard.cpp:498
msgid ""
@ -696,7 +709,7 @@ msgstr ""
#: src/slic3r/GUI/ConfigWizard.cpp:550
#, c-format, boost-format
msgid "%s Family"
msgstr "%s의 가족들"
msgstr "%s사 제품들"
#: src/slic3r/GUI/ConfigWizard.cpp:640
msgid "Printer:"
@ -729,7 +742,7 @@ msgstr "필라멘트"
#: src/slic3r/GUI/ConfigWizard.cpp:752
msgid "SLA materials"
msgstr ""
msgstr "SLA 레진"
#: src/slic3r/GUI/ConfigWizard.cpp:755
#, boost-format
@ -754,19 +767,19 @@ msgstr ""
#: src/slic3r/GUI/ConfigWizard.cpp:1175
msgid "Custom Printer Setup"
msgstr "사용자 지정 프린터 설정"
msgstr "커스텀 프린터 설정"
#: src/slic3r/GUI/ConfigWizard.cpp:1175
msgid "Custom Printer"
msgstr "사용자 정의 프린터"
msgstr "커스텀 프린터"
#: src/slic3r/GUI/ConfigWizard.cpp:1177
msgid "Define a custom printer profile"
msgstr "사용자 정의 프린터 프로필"
msgstr "커스텀 프린터 세팅 입력"
#: src/slic3r/GUI/ConfigWizard.cpp:1179
msgid "Custom profile name:"
msgstr "사용자 정의 프로필 명칭:"
msgstr "커스텀 프로필 명칭:"
#: src/slic3r/GUI/ConfigWizard.cpp:1206
msgid "Automatic updates"
@ -788,9 +801,9 @@ msgid ""
"application startup (never during program usage). This is only a "
"notification mechanisms, no automatic installation is done."
msgstr ""
"활성화 된 경우 %s은 온라인의 새 버전을 확인합니다. 새 버전을 사용할 수 있게 "
"되면, 다음 응용 프로그램 시작시 알림이 표시됩니다 (프로그램 사용 중에는 절대"
"로 사용하지 마십시오).이것은 단순한 알림 일뿐 자동으로 설치가 되지 않습니다."
"활성화시 %s는 최신버전을 온라인에서 확인합니다. 새 버전이 있을시 "
"프로그램이 켜질 때 알림이 표시됩니다 (켜져있을 때는 알림이"
"표시되지 않습니다).자동으로 설치는 되지 않습니다."
#: src/slic3r/GUI/ConfigWizard.cpp:1224 src/slic3r/GUI/Preferences.cpp:175
msgid "Update built-in Presets automatically"
@ -877,7 +890,7 @@ msgstr ""
#: src/slic3r/GUI/ConfigWizard.cpp:1279
msgid "Simple mode"
msgstr "단순 모드"
msgstr "초보자 모드"
#: src/slic3r/GUI/ConfigWizard.cpp:1280
msgid "Advanced mode"
@ -889,20 +902,20 @@ msgstr "전문가 모드"
#: src/slic3r/GUI/ConfigWizard.cpp:1287
msgid "The size of the object can be specified in inches"
msgstr ""
msgstr "물체의 크기를 인치로 표시 가능합니다"
#: src/slic3r/GUI/ConfigWizard.cpp:1288
msgid "Use inches"
msgstr ""
msgstr "인치 사용"
#: src/slic3r/GUI/ConfigWizard.cpp:1322
msgid "Other Vendors"
msgstr "다른 공급 업체"
msgstr "다른 벤더"
#: src/slic3r/GUI/ConfigWizard.cpp:1326
#, c-format, boost-format
msgid "Pick another vendor supported by %s"
msgstr "%s가 지원하는 다른 공급 업체를 선택하십시오:"
msgstr "%s가 지원하는 다른 벤더를 선택하십시오:"
#: src/slic3r/GUI/ConfigWizard.cpp:1357
msgid "Firmware Type"
@ -914,7 +927,7 @@ msgstr "펌웨어"
#: src/slic3r/GUI/ConfigWizard.cpp:1361
msgid "Choose the type of firmware used by your printer."
msgstr "프린터에 업로드 할 펌웨어를 선택하세요."
msgstr "프린터가 사용하는 펌웨어를 선택하세요."
#: src/slic3r/GUI/ConfigWizard.cpp:1396
msgid "Bed Shape and Size"
@ -922,13 +935,13 @@ msgstr "배드 모양과 크기"
#: src/slic3r/GUI/ConfigWizard.cpp:1399
msgid "Set the shape of your printer's bed."
msgstr "프린터 배드모양을 설정하세요."
msgstr "프린터 배드 모양을 설정하세요."
#: src/slic3r/GUI/ConfigWizard.cpp:1433 src/slic3r/GUI/Field.cpp:255
#: src/slic3r/GUI/Field.cpp:324 src/slic3r/GUI/Field.cpp:1561
#: src/slic3r/GUI/GUI_ObjectLayers.cpp:435
msgid "Invalid numeric input."
msgstr "숫자 입력이 잘못 되었습니다."
msgstr "잘못된 수치."
#: src/slic3r/GUI/ConfigWizard.cpp:1457
msgid "Filament and Nozzle Diameters"
@ -936,11 +949,11 @@ msgstr "필라멘트와 노즐 크기"
#: src/slic3r/GUI/ConfigWizard.cpp:1457
msgid "Print Diameters"
msgstr "인쇄 직경"
msgstr "노즐,필라멘트 직경"
#: src/slic3r/GUI/ConfigWizard.cpp:1472
msgid "Enter the diameter of your printer's hot end nozzle."
msgstr "핫 엔드 노즐 직경을 입력하십시오."
msgstr "노즐 직경을 입력하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:1475
msgid "Nozzle Diameter:"
@ -948,15 +961,15 @@ msgstr "노즐 직경:"
#: src/slic3r/GUI/ConfigWizard.cpp:1485
msgid "Enter the diameter of your filament."
msgstr "필라멘트 직경을 입력하십시오."
msgstr "필라멘트 직경을 입력하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:1486
msgid ""
"Good precision is required, so use a caliper and do multiple measurements "
"along the filament, then compute the average."
msgstr ""
"정밀도가 필요하므로 캘리퍼를 사용하여 필라멘트를 따라 여러 번 측정 한 다음 평"
"균을 계산하십시오."
"정확한 수치가 필요하므로 버니어 캘리퍼로 여러번 측정하여"
"평균값을 입력하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:1489
msgid "Filament Diameter:"
@ -964,7 +977,7 @@ msgstr "필라멘트 직경:"
#: src/slic3r/GUI/ConfigWizard.cpp:1547
msgid "Nozzle and Bed Temperatures"
msgstr ""
msgstr "노즐, 베드 온도"
#: src/slic3r/GUI/ConfigWizard.cpp:1547
msgid "Temperatures"
@ -992,14 +1005,14 @@ msgstr "°C"
msgid ""
"Enter the bed temperature needed for getting your filament to stick to your "
"heated bed."
msgstr "필라멘트가 핫배드에 접착하는데 필요한 온도를 입력하십시오."
msgstr "필라멘트가 온열배드에 안착하는데 필요한 온도를 입력하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:1578
msgid ""
"A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have "
"no heated bed."
msgstr ""
"보통은 PLA의 경우 60 ° C이고 ABS의 경우 110 ° C입니다. 핫배드가 없는 경우에"
"보통 PLA는 60 ° C이고 ABS는 110 ° C입니다. 온열배드가 없는 경우에"
"는 0으로 두십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:1581
@ -1008,7 +1021,7 @@ msgstr "배드 온도 :"
#: src/slic3r/GUI/ConfigWizard.cpp:2043 src/slic3r/GUI/ConfigWizard.cpp:2915
msgid "SLA Materials"
msgstr "SLA 재료"
msgstr "SLA 레진"
#: src/slic3r/GUI/ConfigWizard.cpp:2097
msgid "FFF Technology Printers"
@ -1024,6 +1037,8 @@ msgid ""
"Following printer profiles has no default filament: %1%Please select one "
"manually."
msgstr ""
"프린터 프로필에 기본 필라멘트가 없습니다: %1%하나를 "
"선택하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:2339
#, boost-format
@ -1031,6 +1046,8 @@ msgid ""
"Following printer profiles has no default material: %1%Please select one "
"manually."
msgstr ""
"프린터 프로필에 기본 레진이 없습니다: %1%하나를 "
"선택하십시오."
#: src/slic3r/GUI/ConfigWizard.cpp:2340 src/slic3r/GUI/ConfigWizard.cpp:2438
#: src/slic3r/GUI/DoubleSlider.cpp:2522 src/slic3r/GUI/DoubleSlider.cpp:2543

View File

@ -32,6 +32,7 @@ src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
src/slic3r/GUI/GUI.cpp

File diff suppressed because it is too large Load Diff

View File

@ -426,7 +426,9 @@ int CLI::run(int argc, char **argv)
o->cut(Z, m_config.opt_float("cut"), &out);
}
#else
model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
model.objects.front()->cut(0, Geometry::assemble_transform(m_config.opt_float("cut")* Vec3d::UnitZ()),
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
#endif
model.delete_object(size_t(0));
}

View File

@ -140,6 +140,7 @@ namespace ImGui
const wchar_t CancelButton = 0x14;
const wchar_t CancelHoverButton = 0x15;
// const wchar_t VarLayerHeightMarker = 0x16;
const wchar_t RevertButton = 0x16;
const wchar_t RightArrowButton = 0x18;
const wchar_t RightArrowHoverButton = 0x19;
@ -168,6 +169,10 @@ namespace ImGui
const wchar_t LegendCOG = 0x2615;
const wchar_t LegendShells = 0x2616;
const wchar_t LegendToolMarker = 0x2617;
const wchar_t WarningMarkerSmall = 0x2618;
const wchar_t ExpandBtn = 0x2619;
const wchar_t CollapseBtn = 0x2620;
const wchar_t InfoMarkerSmall = 0x2621;
// void MyFunction(const char* name, const MyMatrix44& v);
}

View File

@ -4,11 +4,14 @@
#include <cmath> // std::isinf() is here
#include <unordered_map>
#include "libslic3r/Point.hpp"
#include "libslic3r/MutablePriorityQueue.hpp"
namespace Slic3r { namespace astar {
// Borrowed from C++20
template<class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Input interface for the Astar algorithm. Specialize this struct for a
// particular type and implement all the 4 methods and specify the Node type
// to register the new type for the astar implementation.

View File

@ -19,6 +19,13 @@ void ExPolygon::scale(double factor)
hole.scale(factor);
}
void ExPolygon::scale(double factor_x, double factor_y)
{
contour.scale(factor_x, factor_y);
for (Polygon &hole : holes)
hole.scale(factor_x, factor_y);
}
void ExPolygon::translate(const Point &p)
{
contour.translate(p);

View File

@ -37,6 +37,7 @@ public:
void clear() { contour.points.clear(); holes.clear(); }
void scale(double factor);
void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
void translate(const Point &vector);
void rotate(double angle);

View File

@ -79,6 +79,7 @@ const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml";
static constexpr const char* MODEL_TAG = "model";
static constexpr const char* RESOURCES_TAG = "resources";
@ -434,6 +435,19 @@ namespace Slic3r {
VolumeMetadataList volumes;
};
struct CutObjectInfo
{
struct Connector
{
int volume_id;
int type;
float r_tolerance;
float h_tolerance;
};
CutObjectBase id;
std::vector<Connector> connectors;
};
// Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
typedef std::map<int, int> IdToModelObjectMap;
typedef std::map<int, ComponentsList> IdToAliasesMap;
@ -442,6 +456,7 @@ namespace Slic3r {
typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
@ -469,6 +484,7 @@ namespace Slic3r {
IdToGeometryMap m_geometries;
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
IdToCutObjectInfoMap m_cut_object_infos;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges;
IdToSlaSupportPointsMap m_sla_support_points;
@ -500,6 +516,7 @@ namespace Slic3r {
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
@ -704,6 +721,10 @@ namespace Slic3r {
// extract slic3r layer heights profile file
_extract_layer_heights_profile_config_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) {
// extract slic3r layer config ranges file
_extract_cut_information_from_archive(archive, stat, config_substitutions);
}
else if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) {
// extract slic3r layer config ranges file
_extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
@ -846,6 +867,19 @@ namespace Slic3r {
if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr, config_substitutions))
return false;
// Apply cut information for object if any was loaded
// m_cut_object_ids are indexed by a 1 based model object index.
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
if (cut_object_info != m_cut_object_infos.end()) {
model_object->cut_id = cut_object_info->second.id;
for (auto connector : cut_object_info->second.connectors) {
assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size()));
model_object->volumes[connector.volume_id]->cut_info =
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
}
}
}
// If instances contain a single volume, the volume offset should be 0,0,0
@ -972,6 +1006,65 @@ namespace Slic3r {
return true;
}
void _3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions)
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading cut information data to buffer");
return;
}
std::istringstream iss(buffer); // wrap returned xml to istringstream
pt::ptree objects_tree;
pt::read_xml(iss, objects_tree);
for (const auto& object : objects_tree.get_child("objects")) {
pt::ptree object_tree = object.second;
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
if (obj_idx <= 0) {
add_error("Found invalid object id");
continue;
}
IdToCutObjectInfoMap::iterator object_item = m_cut_object_infos.find(obj_idx);
if (object_item != m_cut_object_infos.end()) {
add_error("Found duplicated cut_object_id");
continue;
}
CutObjectBase cut_id;
std::vector<CutObjectInfo::Connector> connectors;
for (const auto& obj_cut_info : object_tree) {
if (obj_cut_info.first == "cut_id") {
pt::ptree cut_id_tree = obj_cut_info.second;
cut_id = CutObjectBase(ObjectID( cut_id_tree.get<size_t>("<xmlattr>.id")),
cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
}
if (obj_cut_info.first == "connectors") {
pt::ptree cut_connectors_tree = obj_cut_info.second;
for (const auto& cut_connector : cut_connectors_tree) {
if (cut_connector.first != "connector")
continue;
pt::ptree connector_tree = cut_connector.second;
CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"),
connector_tree.get<int>("<xmlattr>.type"),
connector_tree.get<float>("<xmlattr>.r_tolerance"),
connector_tree.get<float>("<xmlattr>.h_tolerance")};
connectors.emplace_back(connector);
}
}
}
CutObjectInfo cut_info {cut_id, connectors};
m_cut_object_infos.insert({ obj_idx, cut_info });
}
}
}
void _3MF_Importer::_extract_print_config_from_archive(
mz_zip_archive& archive, const mz_zip_archive_file_stat& stat,
DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions,
@ -2309,6 +2402,7 @@ namespace Slic3r {
bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
@ -2371,6 +2465,15 @@ namespace Slic3r {
return false;
}
// Adds file with information for object cut ("Metadata/Slic3r_PE_cut_information.txt").
// All information for object cut of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
if (!_add_cut_information_file_to_archive(archive, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
// Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
// All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
@ -2874,6 +2977,67 @@ namespace Slic3r {
return true;
}
bool _3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects) {
object_cnt++;
pt::ptree& obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store info for cut_id
pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
// store cut_id atributes
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
int volume_idx = -1;
for (const ModelVolume* volume : object->volumes) {
++volume_idx;
if (volume->is_cut_connector()) {
pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
}
}
}
if (!tree.empty()) {
std::ostringstream oss;
pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "><connectors", ">\n <connectors");
boost::replace_all(out, "></connectors>", ">\n </connectors>");
boost::replace_all(out, "><connector", ">\n <connector");
boost::replace_all(out, "></connector>", ">\n </connector>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add cut information file to archive");
return false;
}
}
return true;
}
bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
{
assert(is_decimal_separator_point());

View File

@ -464,12 +464,25 @@ static constexpr const double volume_threshold_inches = 9.0; // 9 = 3*3*3;
bool Model::looks_like_imperial_units() const
{
if (this->objects.size() == 0)
if (this->objects.empty())
return false;
for (ModelObject* obj : this->objects)
if (obj->get_object_stl_stats().volume < volume_threshold_inches)
return true;
if (obj->get_object_stl_stats().volume < volume_threshold_inches) {
if (!obj->is_cut())
return true;
bool all_cut_parts_look_like_imperial_units = true;
for (ModelObject* obj_other : this->objects) {
if (obj_other == obj)
continue;
if (obj_other->cut_id.is_equal(obj->cut_id) && obj_other->get_object_stl_stats().volume >= volume_threshold_inches) {
all_cut_parts_look_like_imperial_units = false;
break;
}
}
if (all_cut_parts_look_like_imperial_units)
return true;
}
return false;
}
@ -613,6 +626,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable;
this->origin_translation = rhs.origin_translation;
this->cut_id.copy(rhs.cut_id);
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
m_raw_bounding_box = rhs.m_raw_bounding_box;
@ -715,6 +729,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t
ModelVolume* v = new ModelVolume(this, other);
if (type != ModelVolumeType::INVALID && v->type() != type)
v->set_type(type);
v->cut_info = other.cut_info;
this->volumes.push_back(v);
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
@ -1189,34 +1204,355 @@ size_t ModelObject::parts_count() const
return num;
}
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
bool ModelObject::has_connectors() const
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
assert(is_cut());
for (const ModelVolume* v : this->volumes)
if (v->cut_info.is_connector)
return true;
return false;
}
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount {1};
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
default:
break;
}
if (connector_attributes.style == CutConnectorStyle::Prizm)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
void ModelObject::apply_cut_connectors(const std::string& new_name)
{
if (cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = cut_id.connectors_cnt();
for (const CutConnector& connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
// Mesh will be centered when loading.
ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m *
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
new_volume->name = new_name + "-" + std::to_string(++connector_id);
}
cut_id.increase_connectors_cnt(cut_connectors.size());
// delete all connectors
cut_connectors.clear();
}
void ModelObject::invalidate_cut()
{
this->cut_id.invalidate();
for (ModelVolume* volume : this->volumes)
volume->invalidate_cut_info();
}
void ModelObject::synchronize_model_after_cut()
{
for (ModelObject* obj : m_model->objects) {
if (obj == this || obj->cut_id.is_equal(this->cut_id))
continue;
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
obj->cut_id.copy(this->cut_id);
}
}
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
// we don't save cut information, if result will not contains all parts of initial object
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower))
return;
if (cut_id.id().invalid())
cut_id.init();
{
int cut_obj_cnt = -1;
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt++;
if (cut_obj_cnt > 0)
cut_id.increase_check_sum(size_t(cut_obj_cnt));
}
}
void ModelObject::clone_for_cut(ModelObject** obj)
{
(*obj) = ModelObject::new_clone(*this);
(*obj)->set_model(nullptr);
(*obj)->sla_support_points.clear();
(*obj)->sla_drain_holes.clear();
(*obj)->sla_points_status = sla::PointsStatus::NoPoints;
(*obj)->clear_volumes();
(*obj)->input_file.clear();
}
void ModelVolume::reset_extra_facets()
{
this->supported_facets.reset();
this->seam_facets.reset();
this->mmu_segmentation_facets.reset();
}
void ModelVolume::apply_tolerance()
{
assert(cut_info.is_connector);
if (cut_info.is_processed)
return;
Vec3d sf = get_scaling_factor();
/*
// correct Z offset in respect to the new size
Vec3d pos = vol->get_offset();
pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance;
vol->set_offset(pos);
*/
// make a "hole" wider
sf[X] *= 1. + double(cut_info.radius_tolerance);
sf[Y] *= 1. + double(cut_info.radius_tolerance);
// make a "hole" dipper
sf[Z] *= 1. + double(cut_info.height_tolerance);
set_scaling_factor(sf);
}
void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace)
{
assert(volume->cut_info.is_connector);
volume->cut_info.set_processed();
const auto volume_matrix = volume->get_matrix();
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume* vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
if (volume->cut_info.connector_type == CutConnectorType::Dowel)
vol->apply_tolerance();
else
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
if (volume->cut_info.connector_type == CutConnectorType::Dowel &&
attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject* dowel{ nullptr };
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume* vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
dowels.push_back(dowel);
}
}
void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
{
const auto volume_matrix = instance_matrix * volume->get_matrix();
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(volume_matrix));
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
lower->add_volume(*volume);
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->name = src_volume->name;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
vol->cut_info = src_volume->cut_info;
}
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace)
{
const auto volume_matrix = volume->get_matrix();
using namespace Geometry;
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1 * cut_transformation.get_offset());
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
}
}
static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance)
{
if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) {
object->center_around_origin();
object->translate_instances(-object->origin_translation);
object->origin_translation = Vec3d::Zero();
}
else {
object->invalidate_bounding_box();
object->center_around_origin();
}
}
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero())
{
using namespace Geometry;
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < object->instances.size(); ++i) {
auto& obj_instance = object->instances[i];
const Vec3d offset = obj_instance->get_offset();
const double rot_z = obj_instance->get_rotation().z();
obj_instance->set_transformation(Transformation());
const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() :
assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace;
obj_instance->set_offset(offset + displace);
Vec3d rotation = Vec3d::Zero();
if (!flip && !place_on_cut) {
if ( i != src_instance_idx)
rotation[Z] = rot_z;
}
else {
Transform3d rotation_matrix = Transform3d::Identity();
if (flip)
rotation_matrix = rotation_transform(PI * Vec3d::UnitX());
if (place_on_cut)
rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse();
if (i != src_instance_idx)
rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix;
rotation = Transformation(rotation_matrix).get_rotation();
}
obj_instance->set_rotation(rotation);
}
}
ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
{
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
return {};
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// apply cut attributes for object
apply_cut_attributes(attributes);
// Clone the object to duplicate instances, materials etc.
ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr;
ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr;
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_for_cut(&upper);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes();
upper->input_file.clear();
}
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower))
clone_for_cut(&lower);
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
lower->set_model(nullptr);
lower->sla_support_points.clear();
lower->sla_drain_holes.clear();
lower->sla_points_status = sla::PointsStatus::NoPoints;
lower->clear_volumes();
lower->input_file.clear();
}
std::vector<ModelObject*> dowels;
using namespace Geometry;
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
@ -1224,128 +1560,72 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = Geometry::assemble_transform(
const auto instance_matrix = assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 0.0)), // don't apply Z-rotation
instances[instance]->get_rotation(),
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
z -= instances[instance]->get_offset().z();
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1. * cut_transformation.get_offset());
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
for (ModelVolume* volume : volumes) {
volume->reset_extra_facets();
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (! volume->is_model_part()) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower->add_volume(*volume);
}
else if (! volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
}
if (!volume->is_model_part()) {
if (volume->cut_info.is_processed)
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
else
process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
}
else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace);
}
// Post-process cut parts
ModelObjectPtrs res;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
upper->center_around_origin();
upper->translate_instances(-upper->origin_translation);
upper->origin_translation = Vec3d::Zero();
}
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); ++i) {
auto &instance = upper->instances[i];
const Vec3d offset = instance->get_offset();
const double rot_z = instance->get_rotation().z();
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace;
instance->set_transformation(Geometry::Transformation());
instance->set_offset(offset + displace);
instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
invalidate_translations(upper, instances[instance]);
reset_instance_transformation(upper, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper),
local_displace);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
lower->center_around_origin();
lower->translate_instances(-lower->origin_translation);
lower->origin_translation = Vec3d::Zero();
}
// Reset instance transformation except offset and Z-rotation
for (auto *instance : lower->instances) {
const Vec3d offset = instance->get_offset();
const double rot_z = instance->get_rotation().z();
instance->set_transformation(Geometry::Transformation());
instance->set_offset(offset);
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
invalidate_translations(lower, instances[instance]);
reset_instance_transformation(lower, instance, cut_matrix,
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
invalidate_translations(dowel, instances[instance]);
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
synchronize_model_after_cut();
return res;
}
@ -2289,6 +2569,14 @@ bool model_has_multi_part_objects(const Model &model)
return false;
}
bool model_has_connectors(const Model &model)
{
for (const ModelObject *model_object : model.objects)
if (!model_object->cut_connectors.empty())
return true;
return false;
}
bool model_has_advanced_features(const Model &model)
{
auto config_is_advanced = [](const ModelConfig &config) {

View File

@ -222,7 +222,102 @@ private:
friend class ModelObject;
};
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower };
enum class CutConnectorType : int {
Plug
, Dowel
, Undef
};
enum class CutConnectorStyle : int {
Prizm
, Frustum
, Undef
//,Claw
};
enum class CutConnectorShape : int {
Triangle
, Square
, Hexagon
, Circle
, Undef
//,D-shape
};
struct CutConnectorAttributes
{
CutConnectorType type{ CutConnectorType::Plug };
CutConnectorStyle style{ CutConnectorStyle::Prizm };
CutConnectorShape shape{ CutConnectorShape::Circle };
CutConnectorAttributes() {}
CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh)
: type(t), style(st), shape(sh)
{}
CutConnectorAttributes(const CutConnectorAttributes& rhs) :
CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {}
bool operator==(const CutConnectorAttributes& other) const;
bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); }
bool operator<(const CutConnectorAttributes& other) const {
return this->type < other.type ||
(this->type == other.type && this->style < other.style) ||
(this->type == other.type && this->style == other.style && this->shape < other.shape);
}
template<class Archive> inline void serialize(Archive& ar) {
ar(type, style, shape);
}
};
struct CutConnector
{
Vec3d pos;
Transform3d rotation_m;
float radius;
float height;
float radius_tolerance;// [0.f : 1.f]
float height_tolerance;// [0.f : 1.f]
CutConnectorAttributes attribs;
CutConnector()
: pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f)
{}
CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes)
{}
CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {}
bool operator==(const CutConnector& other) const;
bool operator!=(const CutConnector& other) const { return !(other == (*this)); }
template<class Archive> inline void serialize(Archive& ar) {
ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs);
}
};
using CutConnectors = std::vector<CutConnector>;
// Declared outside of ModelVolume, so it could be forward declared.
enum class ModelVolumeType : int {
INVALID = -1,
MODEL_PART = 0,
NEGATIVE_VOLUME,
PARAMETER_MODIFIER,
SUPPORT_BLOCKER,
SUPPORT_ENFORCER,
};
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
@ -262,6 +357,10 @@ public:
// Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes;
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
CutConnectors cut_connectors;
CutObjectBase cut_id;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment
@ -346,8 +445,21 @@ public:
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(const std::string& name);
// invalidate cut state for this object and its connectors/volumes
void invalidate_cut();
void synchronize_model_after_cut();
void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj);
void process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace);
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace);
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs*new_objects);
void merge();
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
@ -370,6 +482,9 @@ public:
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_repaired_errors_count(const int vol_idx = -1) const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
private:
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
@ -490,7 +605,8 @@ private:
Internal::StaticSerializationWrapper<LayerHeightProfile> layer_heigth_profile_wrapper(layer_height_profile);
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
cut_connectors, cut_id);
}
// Called by Print::validate() from the UI thread.
@ -609,6 +725,37 @@ public:
};
Source source;
// struct used by cut command
// It contains information about connetors
struct CutInfo
{
bool is_connector{ false };
bool is_processed{ true };
CutConnectorType connector_type{ CutConnectorType::Plug };
float radius_tolerance{ 0.f };// [0.f : 1.f]
float height_tolerance{ 0.f };// [0.f : 1.f]
CutInfo() = default;
CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) :
is_connector(true),
is_processed(processed),
connector_type(type),
radius_tolerance(rad_tolerance),
height_tolerance(h_tolerance)
{}
void set_processed() { is_processed = true; }
void invalidate() { is_connector = false; }
template<class Archive> inline void serialize(Archive& ar) {
ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance);
}
};
CutInfo cut_info;
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
void invalidate_cut_info() { cut_info.invalidate(); }
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
#if ENABLE_RAYCAST_PICKING
@ -620,7 +767,7 @@ public:
void set_mesh(indexed_triangle_set &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(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 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,
// overriding the global Slic3r settings and the ModelObject settings.
@ -650,6 +797,8 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; }
void reset_extra_facets();
void apply_tolerance();
void set_material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
@ -819,8 +968,9 @@ private:
ObjectBase(other),
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),
supported_facets(other.supported_facets), seam_facets(other.seam_facets),
mmu_segmentation_facets(other.mmu_segmentation_facets), text_configuration(other.text_configuration)
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
cut_info(other.cut_info),
text_configuration(other.text_configuration)
{
assert(this->id().valid());
assert(this->config.id().valid());
@ -841,6 +991,7 @@ private:
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
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),
cut_info(other.cut_info),
text_configuration(other.text_configuration)
{
assert(this->id().valid());
@ -883,7 +1034,7 @@ private:
}
template<class Archive> void load(Archive &ar) {
bool has_convex_hull;
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info);
cereal::load_by_value(ar, supported_facets);
cereal::load_by_value(ar, seam_facets);
cereal::load_by_value(ar, mmu_segmentation_facets);
@ -900,7 +1051,7 @@ private:
}
template<class Archive> void save(Archive &ar) const {
bool has_convex_hull = m_convex_hull.get() != nullptr;
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info);
cereal::save_by_value(ar, supported_facets);
cereal::save_by_value(ar, seam_facets);
cereal::save_by_value(ar, mmu_segmentation_facets);
@ -1235,6 +1386,8 @@ extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const Mod
// If the model has multi-part objects, then it is currently not supported by the SLA mode.
// Either the model cannot be loaded, or a SLA printer has to be activated.
bool model_has_multi_part_objects(const Model &model);
// If the model has objects with cut connectrs, then it is currently not supported by the SLA mode.
bool model_has_connectors(const Model& model);
// If the model has advanced features, then it cannot be processed in simple mode.
bool model_has_advanced_features(const Model &model);

View File

@ -2,6 +2,7 @@
#define slic3r_ObjectID_hpp_
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
namespace Slic3r {
@ -89,7 +90,9 @@ private:
friend class cereal::access;
friend class Slic3r::UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) { ar(m_id); }
protected: // #vbCHECKME && #ysFIXME
ObjectBase(const ObjectID id) : m_id(id) {}
private:
template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
};
@ -128,6 +131,64 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(m_timestamp); }
};
class CutObjectBase : public ObjectBase
{
// check sum of CutParts in initial Object
size_t m_check_sum{ 1 };
// connectors count
size_t m_connectors_cnt{ 0 };
public:
// Default Constructor to assign an invalid ID
CutObjectBase() : ObjectBase(-1) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
CutObjectBase(int) : ObjectBase(-1) {}
// Constructor to initialize full information from 3mf
CutObjectBase(ObjectID id, size_t check_sum, size_t connectors_cnt) : ObjectBase(id), m_check_sum(check_sum), m_connectors_cnt(connectors_cnt) {}
// The class tree will have virtual tables and type information.
virtual ~CutObjectBase() = default;
bool operator<(const CutObjectBase& other) const { return other.id() > this->id(); }
bool operator==(const CutObjectBase& other) const { return other.id() == this->id(); }
void copy(const CutObjectBase& rhs) {
this->copy_id(rhs);
this->m_check_sum = rhs.check_sum();
this->m_connectors_cnt = rhs.connectors_cnt() ;
}
CutObjectBase& operator=(const CutObjectBase& other) {
this->copy(other);
return *this;
}
void invalidate() {
set_invalid_id();
m_check_sum = 1;
m_connectors_cnt = 0;
}
void init() { this->set_new_unique_id(); }
bool has_same_id(const CutObjectBase& rhs) { return this->id() == rhs.id(); }
bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() &&
this->check_sum() == rhs.check_sum() &&
this->connectors_cnt() == rhs.connectors_cnt() ; }
size_t check_sum() const { return m_check_sum; }
void set_check_sum(size_t cs) { m_check_sum = cs; }
void increase_check_sum(size_t cnt) { m_check_sum += cnt; }
size_t connectors_cnt() const { return m_connectors_cnt; }
void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) {
ar(cereal::base_class<ObjectBase>(this));
ar(m_check_sum, m_connectors_cnt);
}
};
// Unique object / instance ID for the wipe tower.
extern ObjectID wipe_tower_object_id();
extern ObjectID wipe_tower_instance_id();

View File

@ -1061,6 +1061,61 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh;
}
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
{
std::vector<Vec3f> dst_vertices;

View File

@ -309,6 +309,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);

View File

@ -270,9 +270,9 @@ constexpr inline T lerp(const T& a, const T& b, Number t)
}
template <typename Number>
constexpr inline bool is_approx(Number value, Number test_value)
constexpr inline bool is_approx(Number value, Number test_value, Number precision = EPSILON)
{
return std::fabs(double(value) - double(test_value)) < double(EPSILON);
return std::fabs(double(value) - double(test_value)) < double(precision);
}
template<typename Number>

View File

@ -276,6 +276,8 @@ set(SLIC3R_GUI_SOURCES
Utils/WxFontUtils.hpp
)
find_package(NanoSVG REQUIRED)
if (APPLE)
list(APPEND SLIC3R_GUI_SOURCES
Utils/RetinaHelperImpl.mm

View File

@ -33,7 +33,7 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/)
// Style_NoTrailingZeroes does not work on OSX. It also does not work correctly with some locales on Windows.
// return wxNumberFormatter::ToString(value, max_precision, wxNumberFormatter::Style_NoTrailingZeroes);
wxString s = wxNumberFormatter::ToString(value, value < 0.0001 ? 10 : max_precision, wxNumberFormatter::Style_None);
wxString s = wxNumberFormatter::ToString(value, std::abs(value) < 0.0001 ? 10 : max_precision, wxNumberFormatter::Style_None);
// The following code comes from wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
// with the exception that here one sets the decimal separator explicitely to dot.

View File

@ -1367,6 +1367,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)
&& (mv == nullptr || m_model->objects[vol->composite_id.object_id]->volumes[vol->composite_id.volume_id] == mv)) {
vol->is_active = visible;
if (!vol->is_modifier)
vol->color.a(1.f);
if (instance_idx == -1) {
vol->force_native_color = false;
@ -1375,9 +1377,13 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
const GLGizmosManager& gm = get_gizmos_manager();
auto gizmo_type = gm.get_current_type();
if ( (gizmo_type == GLGizmosManager::FdmSupports
|| gizmo_type == GLGizmosManager::Seam)
&& ! vol->is_modifier)
|| gizmo_type == GLGizmosManager::Seam
|| gizmo_type == GLGizmosManager::Cut)
&& ! vol->is_modifier) {
vol->force_neutral_color = true;
if (gizmo_type == GLGizmosManager::Cut)
vol->color.a(0.95f);
}
else if (gizmo_type == GLGizmosManager::MmuSegmentation)
vol->is_active = false;
else
@ -3371,6 +3377,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
show_sinking_contours();
}
}
else if (evt.LeftUp() &&
m_gizmos.get_current_type() == GLGizmosManager::EType::Scale &&
m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) {
wxGetApp().obj_list()->selection_changed();
}
return;
}
@ -3446,6 +3457,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports &&
m_gizmos.get_current_type() != GLGizmosManager::FdmSupports &&
m_gizmos.get_current_type() != GLGizmosManager::Seam &&
m_gizmos.get_current_type() != GLGizmosManager::Cut &&
m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) {
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
m_dirty = true;
@ -3495,7 +3507,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
const int volume_idx = get_first_hover_volume_idx();
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
volume_bbox.offset(1.0);
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) {
const bool is_cut_connector_selected = m_selection.is_any_connector();
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position) && !is_cut_connector_selected) {
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
// The dragging operation is initiated.
m_mouse.drag.move_volume_idx = volume_idx;

View File

@ -2891,6 +2891,13 @@ bool GUI_App::may_switch_to_SLA_preset(const wxString& caption)
caption);
return false;
}
if (model_has_connectors(model())) {
show_info(nullptr,
_L("SLA technology doesn't support cut with connectors") + "\n\n" +
_L("Please check your object list before preset changing."),
caption);
return false;
}
return true;
}

View File

@ -572,7 +572,10 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
for (auto& item : ADD_VOLUME_MENU_ITEMS) {
wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type++));
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second,
[]() { return obj_list()->is_instance_or_object_selected(); }, m_parent);
[type]() {
bool can_add = type < size_t(ModelVolumeType::PARAMETER_MODIFIER) ? !obj_list()->is_selected_object_cut() : true;
return can_add && obj_list()->is_instance_or_object_selected();
}, m_parent);
}
append_menu_item_layers_editing(menu);
@ -738,6 +741,21 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
return menu_item_printable;
}
void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu* menu)
{
const wxString menu_name = _L("Invalidate cut info");
auto menu_item_id = menu->FindItem(menu_name);
if (menu_item_id != wxNOT_FOUND)
// Delete old menu item if selected object isn't cut
menu->Destroy(menu_item_id);
if (obj_list()->has_selected_cut_object())
append_menu_item(menu, wxID_ANY, menu_name, "",
[](wxCommandEvent&) { obj_list()->invalidate_cut_info_for_selection(); }, "", menu,
[]() { return true; }, m_parent);
}
void MenuFactory::append_menu_items_osx(wxMenu* menu)
{
append_menu_item(menu, wxID_ANY, _L("Rename"), "",
@ -874,6 +892,8 @@ void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/*
ModelObjectPtrs objects;
for (int obj_idx : obj_idxs) {
ModelObject* object = obj_list()->object(obj_idx);
if (object->is_cut())
return false;
if (vol_idxs.empty()) {
for (ModelVolume* volume : object->volumes)
if (volume_respects_conversion(volume, conver_type))
@ -1118,6 +1138,7 @@ wxMenu* MenuFactory::object_menu()
append_menu_item_settings(&m_object_menu);
append_menu_item_change_extruder(&m_object_menu);
update_menu_items_instance_manipulation(mtObjectFFF);
append_menu_item_invalidate_cut_info(&m_object_menu);
return &m_object_menu;
}
@ -1127,6 +1148,7 @@ wxMenu* MenuFactory::sla_object_menu()
append_menu_items_convert_unit(&m_sla_object_menu, 11);
append_menu_item_settings(&m_sla_object_menu);
update_menu_items_instance_manipulation(mtObjectSLA);
append_menu_item_invalidate_cut_info(&m_sla_object_menu);
return &m_sla_object_menu;
}
@ -1165,6 +1187,9 @@ wxMenu* MenuFactory::multi_selection_menu()
wxDataViewItemArray sels;
obj_list()->GetSelections(sels);
if (sels.IsEmpty())
return nullptr;
for (const wxDataViewItem& item : sels)
if (!(list_model()->GetItemType(item) & (itVolume | itObject | itInstance)))
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection

View File

@ -97,6 +97,7 @@ private:
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
wxMenuItem* append_menu_item_printable(wxMenu* menu);
void append_menu_item_invalidate_cut_info(wxMenu *menu);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
wxMenuItem* append_menu_item_simplify(wxMenu* menu);

View File

@ -12,6 +12,8 @@
#include "GalleryDialog.hpp"
#include "MainFrame.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "Gizmos/GLGizmoCut.hpp"
#include "Gizmos/GLGizmoScale.hpp"
#include "OptionsGroup.hpp"
#include "Tab.hpp"
@ -402,6 +404,13 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol
if (obj_idx < 0)
return { {}, {} }; // hide tooltip
const ModelObject* object = (*m_objects)[obj_idx];
if (vol_idx != -1 && vol_idx >= int(object->volumes.size())) {
if (sidebar_info)
*sidebar_info = _L("Wrong volume index ");
return { {}, {} }; // hide tooltip
}
const TriangleMeshStats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
@ -682,6 +691,8 @@ void ObjectList::selection_changed()
fix_multiselection_conflicts();
fix_cut_selection();
// update object selection on Plater
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
@ -1404,6 +1415,15 @@ bool ObjectList::is_instance_or_object_selected()
return selection.is_single_full_instance() || selection.is_single_full_object();
}
bool ObjectList::is_selected_object_cut()
{
const Selection& selection = scene_selection();
int obj_idx = selection.get_object_idx();
if (obj_idx < 0)
return false;
return object(obj_idx)->is_cut();
}
void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false*/)
{
if (type == ModelVolumeType::INVALID && from_galery) {
@ -1723,6 +1743,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
if (model_object.is_cut())
update_info_items(obj_idx);
selection_changed();
}
@ -1831,22 +1854,22 @@ void ObjectList::load_mesh_object(
#endif /* _DEBUG */
}
void ObjectList::del_object(const int obj_idx)
bool ObjectList::del_object(const int obj_idx)
{
wxGetApp().plater()->delete_object_from_model(obj_idx);
return wxGetApp().plater()->delete_object_from_model(obj_idx);
}
// Delete subobject
void ObjectList::del_subobject_item(wxDataViewItem& item)
bool ObjectList::del_subobject_item(wxDataViewItem& item)
{
if (!item) return;
if (!item) return false;
int obj_idx, idx;
ItemType type;
m_objects_model->GetItemInfo(item, type, obj_idx, idx);
if (type == itUndef)
return;
return false;
wxDataViewItem parent = m_objects_model->GetParent(item);
@ -1860,19 +1883,21 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (type & itInfo && obj_idx != -1)
del_info_item(obj_idx, m_objects_model->GetInfoItemType(item));
else if (idx == -1)
return;
else if (!del_subobject_from_object(obj_idx, idx, type))
return;
else if (idx == -1 || !del_subobject_from_object(obj_idx, idx, type))
return false;
// If last volume item with warning was deleted, unmark object item
if (type & itVolume) {
add_volumes_to_object_in_list(obj_idx);
const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats());
m_objects_model->UpdateWarningIcon(parent, icon_name);
}
else
m_objects_model->Delete(item);
m_objects_model->Delete(item);
update_info_items(obj_idx);
return true;
}
void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
@ -1895,6 +1920,10 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
mv->seam_facets.reset();
break;
case InfoItemType::CutConnectors:
show_error(nullptr, _L("Connectors cannot be deleted from cut object."));
break;
case InfoItemType::MmuSegmentation:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove Multi Material painting"));
@ -2005,6 +2034,16 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object."));
return false;
}
if (object->is_cut()) {
if (volume->is_model_part()) {
Slic3r::GUI::show_error(nullptr, _L("Solid part cannot be deleted from cut object."));
return false;
}
if (volume->is_negative_volume()) {
Slic3r::GUI::show_error(nullptr, _L("Negative volume cannot be deleted from cut object."));
return false;
}
}
take_snapshot(_L("Delete Subobject"));
@ -2032,6 +2071,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted."));
return false;
}
if (object->is_cut()) {
Slic3r::GUI::show_error(nullptr, _L("Instance cannot be deleted from cut object."));
return false;
}
take_snapshot(_L("Delete Instance"));
object->delete_instance(idx);
@ -2065,31 +2108,11 @@ void ObjectList::split()
volume->split(nozzle_dmrs_cnt);
(*m_objects)[obj_idx]->input_file.clear();
wxBusyCursor wait;
auto model_object = (*m_objects)[obj_idx];
auto parent = m_objects_model->GetTopParent(item);
if (parent)
m_objects_model->DeleteVolumeChildren(parent);
else
parent = item;
for (const ModelVolume* volume : model_object->volumes) {
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
volume->text_configuration.has_value(),
get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
// add settings to the part, if it has those
add_settings_item(vol_item, &volume->config.get());
}
model_object->input_file.clear();
if (parent == item)
Expand(parent);
add_volumes_to_object_in_list(obj_idx);
changed_object(obj_idx);
// update printable state for new volumes on canvas3D
@ -2408,9 +2431,12 @@ bool ObjectList::is_splittable(bool to_objects)
auto obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
if ((*m_objects)[obj_idx]->volumes.size() > 1)
const ModelObject* object = (*m_objects)[obj_idx];
if (object->is_cut())
return false;
if (object->volumes.size() > 1)
return true;
return (*m_objects)[obj_idx]->volumes[0]->is_splittable();
return object->volumes[0]->is_splittable();
}
return false;
}
@ -2443,9 +2469,59 @@ bool ObjectList::can_split_instances()
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
}
bool ObjectList::has_selected_cut_object() const
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0 && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
invalidate_cut_info_for_object(size_t(obj_idx));
}
}
void ObjectList::invalidate_cut_info_for_object(size_t obj_idx)
{
ModelObject* init_obj = object(int(obj_idx));
if (!init_obj->is_cut())
return;
take_snapshot(_L("Invalidate cut info"));
auto invalidate_cut = [this](size_t obj_idx) {
object(int(obj_idx))->invalidate_cut();
update_info_items(obj_idx);
add_volumes_to_object_in_list(obj_idx);
};
// invalidate cut for related objects (which have the same cut_id)
for (size_t idx = 0; idx < m_objects->size(); idx++)
if (ModelObject* obj = object(idx); obj != init_obj && obj->cut_id.is_equal(init_obj->cut_id))
invalidate_cut(idx);
// invalidate own cut information
invalidate_cut(size_t(obj_idx));
update_lock_icons_for_model();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (printer_technology() == ptSLA)
if (printer_technology() == ptSLA || has_selected_cut_object())
return false;
wxDataViewItemArray sels;
@ -2473,17 +2549,7 @@ bool ObjectList::can_merge_to_single_object() const
wxPoint ObjectList::get_mouse_position_in_control() const
{
wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
#ifdef __APPLE__
// Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl
if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) {
auto rect = this->GetItemRect(top_item, this->GetColumn(0));
pt.y -= rect.y;
}
#endif // __APPLE__
return pt;
return wxGetMousePosition() - this->GetScreenPosition();
}
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
@ -2504,14 +2570,65 @@ void ObjectList::part_selection_changed()
bool update_and_show_settings = false;
bool update_and_show_layers = false;
bool enable_manipulation {true};
bool disable_ss_manipulation {false};
bool disable_ununiform_scale {false};
const auto item = GetSelection();
if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (item && m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) {
og_name = _L("Cut Connectors information");
update_and_show_manipulations = true;
enable_manipulation = false;
disable_ununiform_scale = true;
}
else if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
og_name = _L("Group manipulation");
const Selection& selection = scene_selection();
// don't show manipulation panel for case of all Object's parts selection
update_and_show_manipulations = !selection.is_single_full_instance();
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) {
if (selection.is_any_volume() || selection.is_any_modifier())
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot)
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else {
wxDataViewItemArray sels;
GetSelections(sels);
if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) {
int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else if (selection.is_mixed() || selection.is_multiple_full_object()) {
std::map<CutObjectBase, std::set<int>> cut_objects;
// find cut objects
for (auto item : sels) {
int obj_idx = m_objects_model->GetObjectIdByItem(item);
const ModelObject* obj = object(obj_idx);
if (obj->is_cut()) {
if (cut_objects.find(obj->cut_id) == cut_objects.end())
cut_objects[obj->cut_id] = std::set<int>{ obj_idx };
else
cut_objects.at(obj->cut_id).insert(obj_idx);
}
}
// check if selected cut objects are "full selected"
for (auto cut_object : cut_objects)
if (cut_object.first.check_sum() != cut_object.second.size()) {
disable_ss_manipulation = true;
break;
}
disable_ununiform_scale = !cut_objects.empty();
}
}
}
else {
if (item) {
@ -2519,11 +2636,12 @@ void ObjectList::part_selection_changed()
const wxDataViewItem parent = m_objects_model->GetParent(item);
const ItemType parent_type = m_objects_model->GetItemType(parent);
obj_idx = m_objects_model->GetObjectIdByItem(item);
ModelObject* object = (*m_objects)[obj_idx];
if (parent == wxDataViewItem(nullptr)
|| type == itInfo) {
og_name = _L("Object manipulation");
m_config = &(*m_objects)[obj_idx]->config;
m_config = &object->config;
update_and_show_manipulations = true;
if (type == itInfo) {
@ -2539,29 +2657,30 @@ void ObjectList::part_selection_changed()
case InfoItemType::CustomSeam:
case InfoItemType::MmuSegmentation:
{
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
GLGizmosManager::EType::MmuSegmentation;
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos_mgr.get_current_type() != gizmo_type)
gizmos_mgr.open_gizmo(gizmo_type);
break;
}
case InfoItemType::Sinking: { break; }
case InfoItemType::Sinking:
default: { break; }
}
}
else
disable_ss_manipulation = object->is_cut();
}
else {
if (type & itSettings) {
if (parent_type & itObject) {
og_name = _L("Object Settings to modify");
m_config = &(*m_objects)[obj_idx]->config;
m_config = &object->config;
}
else if (parent_type & itVolume) {
og_name = _L("Part Settings to modify");
volume_id = m_objects_model->GetVolumeIdByItem(parent);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
m_config = &object->volumes[volume_id]->config;
}
else if (parent_type & itLayer) {
og_name = _L("Layer range Settings to modify");
@ -2572,15 +2691,18 @@ void ObjectList::part_selection_changed()
else if (type & itVolume) {
og_name = _L("Part manipulation");
volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
m_config = &object->volumes[volume_id]->config;
update_and_show_manipulations = true;
const ModelVolume* volume = object->volumes[volume_id];
enable_manipulation = !(object->is_cut() && (volume->is_cut_connector() || volume->is_model_part()));
}
else if (type & itInstance) {
og_name = _L("Instance manipulation");
update_and_show_manipulations = true;
// fill m_config by object's values
m_config = &(*m_objects)[obj_idx]->config;
m_config = &object->config;
disable_ss_manipulation = object->is_cut();
}
else if (type & (itLayerRoot|itLayer)) {
og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range");
@ -2599,10 +2721,20 @@ void ObjectList::part_selection_changed()
wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " ");
if (item) {
// wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item));
wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item));
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id));
}
if (disable_ss_manipulation)
wxGetApp().obj_manipul()->DisableScale();
else {
wxGetApp().obj_manipul()->Enable(enable_manipulation);
if (disable_ununiform_scale)
wxGetApp().obj_manipul()->DisableUnuniformScale();
}
if (GLGizmoScale3D* scale = dynamic_cast<GLGizmoScale3D*>(gizmos_mgr.get_gizmo(GLGizmosManager::Scale)))
scale->enable_ununiversal_scale(!disable_ununiform_scale);
}
if (update_and_show_settings)
@ -2625,6 +2757,7 @@ void ObjectList::part_selection_changed()
#else
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
#endif // ENABLE_WORLD_COORDINATE
wxGetApp().plater()->canvas3D()->enable_moving(enable_manipulation); // ysFIXME
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
@ -2683,6 +2816,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
for (InfoItemType type : {InfoItemType::CustomSupports,
InfoItemType::CustomSeam,
InfoItemType::CutConnectors,
InfoItemType::MmuSegmentation,
InfoItemType::Sinking,
InfoItemType::VariableLayerHeight}) {
@ -2703,6 +2837,9 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
});
break;
case InfoItemType::CutConnectors:
should_show = model_object->is_cut() && model_object->has_connectors() && model_object->volumes.size() > 1;
break;
case InfoItemType::VariableLayerHeight :
should_show = printer_technology() == ptFFF
&& ! model_object->layer_height_profile.empty();
@ -2742,33 +2879,74 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
}
}
static wxString extruder2str(int extruder)
{
return extruder == 0 ? _L("default") : wxString::Format("%d", extruder);
}
static bool can_add_volumes_to_object(const ModelObject* object)
{
bool can = object->volumes.size() > 1;
if (can && object->is_cut()) {
int no_connectors_cnt = 0;
for (const ModelVolume* v : object->volumes)
if (!v->is_cut_connector()) {
if (!v->is_model_part())
return true;
no_connectors_cnt++;
}
can = no_connectors_cnt > 1;
}
return can;
}
wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
{
wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx));
m_objects_model->DeleteVolumeChildren(object_item);
wxDataViewItemArray items;
const ModelObject* object = (*m_objects)[obj_idx];
// add volumes to the object
if (can_add_volumes_to_object(object)) {
int volume_idx{ -1 };
for (const ModelVolume* volume : object->volumes) {
++volume_idx;
if (object->is_cut() && volume->is_cut_connector())
continue;
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item,
from_u8(volume->name),
volume_idx,
volume->type(),
volume->text_configuration.has_value(),
get_warning_icon_name(volume->mesh().stats()),
extruder2str(volume->config.has("extruder") ? volume->config.extruder() : 0));
add_settings_item(vol_item, &volume->config.get());
if (add_to_selection && add_to_selection(volume))
items.Add(vol_item);
}
Expand(object_item);
}
return items;
}
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
{
auto model_object = (*m_objects)[obj_idx];
const wxString& item_name = from_u8(model_object->name);
const auto item = m_objects_model->Add(item_name,
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
get_warning_icon_name(model_object->mesh().stats()));
const auto item = m_objects_model->AddObject(item_name,
extruder2str(model_object->config.has("extruder") ? model_object->config.extruder() : 0),
get_warning_icon_name(model_object->mesh().stats()),
model_object->is_cut());
update_info_items(obj_idx, nullptr, call_selection_changed);
// add volumes to the object
if (model_object->volumes.size() > 1 ||
model_object->volumes[0]->text_configuration.has_value()) {
for (const ModelVolume* volume : model_object->volumes) {
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
from_u8(volume->name),
volume->type(),
volume->text_configuration.has_value(),
get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
add_settings_item(vol_item, &volume->config.get());
}
Expand(item);
}
add_volumes_to_object_in_list(obj_idx);
// add instances to the object, if it has those
if (model_object->instances.size()>1)
@ -2822,29 +3000,39 @@ void ObjectList::delete_instance_from_list(const size_t obj_idx, const size_t in
select_item([this, obj_idx, inst_idx]() { return m_objects_model->Delete(m_objects_model->GetItemByInstanceId(obj_idx, inst_idx)); });
}
void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx)
void ObjectList::update_lock_icons_for_model()
{
if ( !(type&(itObject|itVolume|itInstance)) )
return;
take_snapshot(_(L("Delete Selected Item")));
if (type&itObject) {
del_object(obj_idx);
delete_object_from_list(obj_idx);
}
else {
del_subobject_from_object(obj_idx, sub_obj_idx, type);
type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) :
delete_instance_from_list(obj_idx, sub_obj_idx);
}
for (size_t obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx)
if (!(*m_objects)[obj_idx]->is_cut())
m_objects_model->UpdateLockIcon(m_objects_model->GetItemById(int(obj_idx)), false);
}
void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete)
bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx)
{
if (type & (itObject | itVolume | itInstance)) {
if (type & itObject) {
bool was_cut = object(obj_idx)->is_cut();
if (del_object(obj_idx)) {
delete_object_from_list(obj_idx);
if (was_cut)
update_lock_icons_for_model();
return true;
}
return false;
}
if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) {
type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) :
delete_instance_from_list(obj_idx, sub_obj_idx);
return true;
}
}
return false;
}
bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete)
{
if (items_for_delete.empty())
return;
return false;
m_prevent_list_events = true;
@ -2853,14 +3041,18 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
if (!(item->type&(itObject | itVolume | itInstance)))
continue;
if (item->type&itObject) {
del_object(item->obj_idx);
bool was_cut = object(item->obj_idx)->is_cut();
if (!del_object(item->obj_idx))
return false;// continue;
m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx));
if (was_cut)
update_lock_icons_for_model();
}
else {
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue;
if (item->type&itVolume) {
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
add_volumes_to_object_in_list(item->obj_idx);
ModelObject* obj = object(item->obj_idx);
if (obj->volumes.size() == 1) {
wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx);
@ -2884,8 +3076,12 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
update_info_items(id);
}
m_prevent_list_events = true;
m_prevent_list_events = false;
if (modified_objects_ids.empty())
return false;
part_selection_changed();
return true;
}
void ObjectList::delete_all_objects_from_list()
@ -2990,8 +3186,10 @@ void ObjectList::remove()
{
wxDataViewItem parent = m_objects_model->GetParent(item);
ItemType type = m_objects_model->GetItemType(item);
if (type & itObject)
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
if (type & itObject) {
if (!delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1))
return item;
}
else {
if (type & (itLayer | itInstance)) {
// In case there is just one layer or two instances and we delete it, del_subobject_item will
@ -3001,7 +3199,8 @@ void ObjectList::remove()
parent = m_objects_model->GetTopParent(item);
}
del_subobject_item(item);
if (!del_subobject_item(item))
return item;
}
return parent;
@ -3027,6 +3226,8 @@ void ObjectList::remove()
if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes)
continue;
parent = delete_item(item);
if (parent == item && m_objects_model->GetItemType(item) & itObject) // Object wasn't deleted
break;
}
}
@ -3219,7 +3420,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range,
const auto layer_item = m_objects_model->AddLayersChild(layers_item,
range,
config.opt_int("extruder"),
extruder2str(config.opt_int("extruder")),
layer_idx);
add_settings_item(layer_item, &config);
}
@ -3313,6 +3514,24 @@ bool ObjectList::is_selected(const ItemType type) const
return false;
}
bool ObjectList::is_connectors_item_selected() const
{
const wxDataViewItem& item = GetSelection();
if (item)
return m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors;
return false;
}
bool ObjectList::is_connectors_item_selected(const wxDataViewItemArray& sels) const
{
for (auto item : sels)
if (m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors)
return true;
return false;
}
int ObjectList::get_selected_layers_range_idx() const
{
const wxDataViewItem& item = GetSelection();
@ -3439,11 +3658,18 @@ void ObjectList::update_selections()
else {
for (auto idx : selection.get_volume_idxs()) {
const auto gl_vol = selection.get_volume(idx);
if (gl_vol->volume_idx() >= 0)
if (gl_vol->volume_idx() >= 0) {
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
// are not associated with ModelVolumes, but they are temporarily generated by the backend
// (for example, SLA supports or SLA pad).
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
int obj_idx = gl_vol->object_idx();
int vol_idx = gl_vol->volume_idx();
assert(obj_idx >= 0 && vol_idx >= 0);
if (object(obj_idx)->volumes[vol_idx]->is_cut_connector())
sels.Add(m_objects_model->GetInfoItemByType(m_objects_model->GetItemById(obj_idx), InfoItemType::CutConnectors));
else
sels.Add(m_objects_model->GetItemByVolumeId(obj_idx, vol_idx));
}
}
m_selection_mode = smVolume; }
}
@ -3493,11 +3719,34 @@ void ObjectList::update_selections()
if (sels.size() == 0 || m_selection_mode & smSettings)
m_selection_mode = smUndef;
select_items(sels);
// Scroll selected Item in the middle of an object list
ensure_current_item_visible();
if (fix_cut_selection(sels) || is_connectors_item_selected(sels)) {
m_prevent_list_events = true;
// If some part is selected, unselect all items except of selected parts of the current object
UnselectAll();
SetSelections(sels);
m_prevent_list_events = false;
// update object selection on Plater
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
// to update the toolbar and info sizer
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject || is_connectors_item_selected()) {
auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT);
event.SetEventObject(this);
wxPostEvent(this, event);
}
part_selection_changed();
}
else {
select_items(sels);
// Scroll selected Item in the middle of an object list
ensure_current_item_visible();
}
}
void ObjectList::update_selections_on_canvas()
@ -3531,16 +3780,29 @@ void ObjectList::update_selections_on_canvas()
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
}
else if (type == itInfo) {
// When selecting an info item, select one instance of the
// respective object - a gizmo may want to be opened.
int inst_idx = selection.get_instance_idx();
int scene_obj_idx = selection.get_object_idx();
mode = Selection::Instance;
// select first instance, unless an instance of the object is already selected
if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx)
inst_idx = 0;
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) {
mode = Selection::Volume;
// When selecting CutConnectors info item, select all object volumes, which are marked as a connector
const ModelObject* obj = object(obj_idx);
for (unsigned int vol_idx = 0; vol_idx < obj->volumes.size(); vol_idx++)
if (obj->volumes[vol_idx]->is_cut_connector()) {
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_volume(obj_idx, std::max(instance_idx, 0), vol_idx);
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
}
}
else {
// When selecting an info item, select one instance of the
// respective object - a gizmo may want to be opened.
int inst_idx = selection.get_instance_idx();
int scene_obj_idx = selection.get_object_idx();
mode = Selection::Instance;
// select first instance, unless an instance of the object is already selected
if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx)
inst_idx = 0;
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
}
}
else
{
@ -3556,6 +3818,8 @@ void ObjectList::update_selections_on_canvas()
if (sel_cnt == 1) {
wxDataViewItem item = GetSelection();
if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors)
selection.remove_all();
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode);
else
@ -3826,6 +4090,53 @@ void ObjectList::fix_multiselection_conflicts()
m_prevent_list_events = false;
}
bool ObjectList::fix_cut_selection(wxDataViewItemArray& sels)
{
if (wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Scale) {
for (const auto& item : sels) {
if (m_objects_model->GetItemType(item) & (itInstance | itObject) ||
(m_objects_model->GetItemType(item) & itSettings &&
m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject)) {
bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance;
int object_idx = m_objects_model->GetObjectIdByItem(item);
int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0;
if (auto obj = object(object_idx); obj->is_cut()) {
sels.Clear();
auto cut_id = obj->cut_id;
int objects_cnt = int((*m_objects).size());
for (int obj_idx = 0; obj_idx < objects_cnt; ++obj_idx) {
auto object = (*m_objects)[obj_idx];
if (object->is_cut() && object->cut_id.has_same_id(cut_id))
sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx));
}
return true;
}
}
}
}
return false;
}
void ObjectList::fix_cut_selection()
{
wxDataViewItemArray sels;
GetSelections(sels);
if (fix_cut_selection(sels)) {
m_prevent_list_events = true;
// If some part is selected, unselect all items except of selected parts of the current object
UnselectAll();
SetSelections(sels);
m_prevent_list_events = false;
}
}
ModelVolume* ObjectList::get_selected_model_volume()
{
wxDataViewItem item = GetSelection();
@ -3856,10 +4167,11 @@ void ObjectList::change_part_type()
if (obj_idx < 0) return;
const ModelVolumeType type = volume->type();
const ModelObject* obj = object(obj_idx);
if (type == ModelVolumeType::MODEL_PART)
{
int model_part_cnt = 0;
for (auto vol : (*m_objects)[obj_idx]->volumes) {
for (auto vol : obj->volumes) {
if (vol->type() == ModelVolumeType::MODEL_PART)
++model_part_cnt;
}
@ -3870,14 +4182,19 @@ void ObjectList::change_part_type()
}
}
wxArrayString names;
for (const wxString& name : { _L("Part"), _L("Negative Volume"), _L("Modifier") })
names.Add(name);
// names.Alloc(is_cut_object ? 3 : 5);
if (!is_cut_object)
for (const wxString& type : { _L("Part"), _L("Negative Volume") })
names.Add(type);
names.Add(_L("Modifier"))
if (!volume->text_configuration.has_value())
for (const wxString& name : { _L("Support Blocker"), _L("Support Enforcer") })
names.Add(name);
auto new_type = ModelVolumeType(wxGetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type)));
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));
if (new_type == type || new_type == ModelVolumeType::INVALID)
return;
@ -4397,34 +4714,14 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
wxGetApp().plater()->update();
}
wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
{
wxDataViewItemArray items;
(*m_objects)[obj_idx]->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
ModelObject* object = (*m_objects)[obj_idx];
if (object->volumes.size() <= 1)
return items;
wxDataViewItemArray items = add_volumes_to_object_in_list(obj_idx, std::move(add_to_selection));
object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
changed_object(int(obj_idx));
wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
m_objects_model->DeleteVolumeChildren(object_item);
for (const ModelVolume* volume : object->volumes) {
wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name),
volume->type(),
volume->text_configuration.has_value(),
get_warning_icon_name(volume->mesh().stats()),
volume->config.has("extruder") ? volume->config.extruder() : 0,
false);
// add settings to the part, if it has those
add_settings_item(vol_item, &volume->config.get());
if (add_to_selection && add_to_selection(volume))
items.Add(vol_item);
}
changed_object(obj_idx);
return items;
}

View File

@ -248,7 +248,7 @@ public:
void add_category_to_settings_from_frequent(const std::vector<std::string>& category_options, wxDataViewItem item);
void show_settings(const wxDataViewItem settings_item);
bool is_instance_or_object_selected();
bool is_selected_object_cut();
void load_subobject(ModelVolumeType type, bool from_galery = false);
// ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common
//void load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
@ -259,8 +259,8 @@ public:
void load_shape_object_from_gallery(const wxArrayString& input_files);
void load_mesh_object(const TriangleMesh &mesh, const std::string &name, bool center = true,
const TextConfiguration* text_config = nullptr, const Transform3d* transformation = nullptr);
void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item);
bool del_object(const int obj_idx);
bool del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item);
void del_instances_from_object(const int obj_idx);
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
@ -279,6 +279,9 @@ public:
bool is_splittable(bool to_objects);
bool selected_instances_of_same_object();
bool can_split_instances();
bool has_selected_cut_object() const;
void invalidate_cut_info_for_selection();
void invalidate_cut_info_for_object(size_t obj_idx);
bool can_merge_to_multipart_object() const;
bool can_merge_to_single_object() const;
@ -290,6 +293,9 @@ public:
void changed_object(const int obj_idx = -1) const;
void part_selection_changed();
// Add object's volumes to the list
// Return selected items, if add_to_selection is defined
wxDataViewItemArray add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection = nullptr);
// Add object to the list
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true);
// Delete object from the list
@ -297,8 +303,9 @@ public:
void delete_object_from_list(const size_t obj_idx);
void delete_volume_from_list(const size_t obj_idx, const size_t vol_idx);
void delete_instance_from_list(const size_t obj_idx, const size_t inst_idx);
void delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx);
void delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete);
void update_lock_icons_for_model();
bool delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx);
bool delete_from_model_and_list(const std::vector<ItemForDelete>& items_for_delete);
// Delete all objects from the list
void delete_all_objects_from_list();
// Increase instances count
@ -341,6 +348,8 @@ public:
void init_objects();
bool multiple_selection() const ;
bool is_selected(const ItemType type) const;
bool is_connectors_item_selected() const;
bool is_connectors_item_selected(const wxDataViewItemArray& sels) const;
int get_selected_layers_range_idx() const;
void set_selected_layers_range_idx(const int range_idx) { m_selected_layers_range_idx = range_idx; }
void set_selection_mode(SELECTION_MODE mode) { m_selection_mode = mode; }
@ -355,6 +364,9 @@ public:
bool check_last_selection(wxString& msg_str);
// correct current selections to avoid of the possible conflicts
void fix_multiselection_conflicts();
// correct selection in respect to the cut_id if any exists
void fix_cut_selection();
bool fix_cut_selection(wxDataViewItemArray& sels);
ModelVolume* get_selected_model_volume();
void change_part_type();
@ -390,7 +402,7 @@ public:
void toggle_printable_state();
void set_extruder_for_selected_items(const int extruder) const ;
wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function<bool(const ModelVolume*)> add_to_selection = nullptr);
wxDataViewItemArray reorder_volumes_and_get_selection(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection = nullptr);
void apply_volumes_order();
bool has_paint_on_segmentation();

View File

@ -576,6 +576,35 @@ void ObjectManipulation::UpdateAndShow(const bool show)
OG_Settings::UpdateAndShow(show);
}
void ObjectManipulation::Enable(const bool enadle)
{
for (auto editor : m_editors)
editor->Enable(enadle);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt
#if ENABLE_WORLD_COORDINATE
,m_reset_skew_button
#endif // ENABLE_WORLD_COORDINATE
})
win->Enable(enadle);
}
void ObjectManipulation::DisableScale()
{
for (auto editor : m_editors)
editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_lock_bnt
#if ENABLE_WORLD_COORDINATE
,m_reset_skew_button
#endif // ENABLE_WORLD_COORDINATE
})
win->Enable(false);
}
void ObjectManipulation::DisableUnuniformScale()
{
m_lock_bnt->Enable(false);
}
void ObjectManipulation::update_ui_from_settings()
{
if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) {
@ -734,7 +763,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
#endif // ENABLE_WORLD_COORDINATE
m_new_enabled = true;
}
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) {
else if (obj_list->is_connectors_item_selected() || obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) {
reset_settings_value();
m_new_move_label_string = L("Translate");
m_new_rotate_label_string = L("Rotate");

View File

@ -64,6 +64,8 @@ public:
const std::string& get_full_opt_name() const { return m_full_opt_name; }
#endif // ENABLE_WORLD_COORDINATE
bool has_opt_key(const std::string& key) { return m_opt_key == key; }
private:
double get_value();
};
@ -197,6 +199,10 @@ public:
void Show(const bool show) override;
bool IsShown() override;
void UpdateAndShow(const bool show) override;
void Enable(const bool enadle = true);
void Disable() { Enable(false); }
void DisableScale();
void DisableUnuniformScale();
void update_ui_from_settings();
bool use_colors() { return m_use_colors; }

View File

@ -313,8 +313,8 @@ void GLGizmoBase::set_hover_id(int id)
assert(!m_dragging);
// allow empty grabbers when not using grabbers but use hover_id - flatten, rotate
if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
return;
// if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
// return;
m_hover_id = id;
on_set_hover_id();
@ -406,15 +406,15 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
}
if (mouse_event.LeftDown()) {
Selection &selection = m_parent.get_selection();
if (!selection.is_empty() && m_hover_id != -1 &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))) {
Selection &selection = m_parent.get_selection();
if (!selection.is_empty() && m_hover_id != -1 /* &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))*/) {
selection.setup_cache();
m_dragging = true;
for (auto &grabber : m_grabbers) grabber.dragging = false;
if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
m_grabbers[m_hover_id].dragging = true;
// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
// m_grabbers[m_hover_id].dragging = true;
on_start_dragging();

View File

@ -215,6 +215,9 @@ public:
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
#endif // ENABLE_RAYCAST_PICKING
virtual bool is_in_editing_mode() const { return false; }
virtual bool is_selection_rectangle_dragging() const { return false; }
protected:
virtual bool on_init() = 0;
virtual void on_load(cereal::BinaryInputArchive& ar) {}

File diff suppressed because it is too large Load Diff

View File

@ -2,55 +2,161 @@
#define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLSelectionRectangle.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/ObjectID.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
enum class CutConnectorType : int;
class ModelVolume;
struct CutConnectorAttributes;
namespace GUI {
class Selection;
class GLGizmoCut : public GLGizmoBase
{
static const double Offset;
static const double Margin;
enum class SLAGizmoEventType : unsigned char;
class GLGizmoCut3D : public GLGizmoBase
{
Transform3d m_rotation_m{ Transform3d::Identity() };
double m_snap_step{ 1.0 };
int m_connectors_group_id;
// archived values
Vec3d m_ar_plane_center { Vec3d::Zero() };
Vec3d m_plane_center{ Vec3d::Zero() };
// data to check position of the cut palne center on gizmo activation
Vec3d m_min_pos{ Vec3d::Zero() };
Vec3d m_max_pos{ Vec3d::Zero() };
Vec3d m_bb_center{ Vec3d::Zero() };
Vec3d m_center_offset{ Vec3d::Zero() };
// values from RotationGizmo
double m_radius{ 0.0 };
double m_grabber_radius{ 0.0 };
double m_grabber_connection_len{ 0.0 };
double m_snap_coarse_in_radius{ 0.0 };
double m_snap_coarse_out_radius{ 0.0 };
double m_snap_fine_in_radius{ 0.0 };
double m_snap_fine_out_radius{ 0.0 };
// dragging angel in hovered axes
Transform3d m_start_dragging_m{ Transform3d::Identity() };
double m_angle{ 0.0 };
TriangleMesh m_connector_mesh;
// workaround for using of the clipping plane normal
Vec3d m_clp_normal{ Vec3d::Ones() };
Vec3d m_line_beg{ Vec3d::Zero() };
Vec3d m_line_end{ Vec3d::Zero() };
Vec2d m_ldown_mouse_position{ Vec2d::Zero() };
double m_cut_z{ 0.0 };
double m_max_z{ 0.0 };
double m_start_z{ 0.0 };
Vec3d m_drag_pos;
Vec3d m_drag_center;
bool m_keep_upper{ true };
bool m_keep_lower{ true };
bool m_rotate_lower{ false };
#if ENABLE_LEGACY_OPENGL_REMOVAL
GLModel m_plane;
GLModel m_grabber_connection;
Vec3d m_old_center;
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
GLModel m_cut_line;
struct CutContours
PickingModel m_sphere;
PickingModel m_cone;
std::map<CutConnectorAttributes, PickingModel> m_shapes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
GLModel m_circle;
GLModel m_scale;
GLModel m_snap_radii;
GLModel m_reference_radius;
GLModel m_angle_arc;
Vec3d m_old_center;
struct InvalidConnectorsStatistics
{
TriangleMesh mesh;
GLModel contours;
double cut_z{ 0.0 };
Vec3d position{ Vec3d::Zero() };
Vec3d shift{ Vec3d::Zero() };
ObjectID object_id;
int instance_idx{ -1 };
std::vector<ObjectID> volumes_idxs;
std::vector<Transform3d> volumes_trafos;
unsigned int outside_cut_contour;
unsigned int outside_bb;
bool is_overlap;
void invalidate() {
outside_cut_contour = 0;
outside_bb = 0;
is_overlap = false;
}
} m_info_stats;
bool m_keep_upper{ true };
bool m_keep_lower{ true };
bool m_place_on_cut_upper{ true };
bool m_place_on_cut_lower{ false };
bool m_rotate_upper{ false };
bool m_rotate_lower{ false };
bool m_hide_cut_plane{ false };
bool m_connectors_editing{ false };
bool m_cut_plane_as_circle{ false };
float m_connector_depth_ratio{ 3.f };
float m_connector_size{ 2.5f };
float m_connector_depth_ratio_tolerance{ 0.1f };
float m_connector_size_tolerance{ 0.f };
float m_label_width{ 150.0 };
float m_control_width{ 200.0 };
bool m_imperial_units{ false };
bool force_update_clipper_on_render{false};
mutable std::vector<bool> m_selected; // which pins are currently selected
int m_selected_count{ 0 };
GLSelectionRectangle m_selection_rectangle;
bool m_has_invalid_connector{ false };
bool m_show_shortcuts{ false };
std::vector<std::pair<wxString, wxString>> m_shortcuts;
enum class CutMode {
cutPlanar
, cutGrig
//,cutRadial
//,cutModular
};
CutContours m_cut_contours;
enum class CutConnectorMode {
Auto
, Manual
};
std::vector<std::string> m_modes;
size_t m_mode{ size_t(CutMode::cutPlanar) };
std::vector<std::string> m_connector_modes;
CutConnectorMode m_connector_mode{ CutConnectorMode::Manual };
std::vector<std::string> m_connector_types;
CutConnectorType m_connector_type;
std::vector<std::string> m_connector_styles;
size_t m_connector_style;
std::vector<std::string> m_connector_shapes;
size_t m_connector_shape_id;
std::vector<std::string> m_axis_names;
public:
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
double get_cut_z() const { return m_cut_z; }
void set_cut_z(double cut_z);
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
std::string get_tooltip() const override;
bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair<Vec3d, Vec3d>& pos_and_normal, Vec3d& pos_world);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool is_in_editing_mode() const override { return m_connectors_editing; }
bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); }
/// <summary>
/// Drag of plane
@ -59,29 +165,105 @@ public:
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void shift_cut_z(double delta);
void rotate_vec3d_around_plane_center(Vec3d&vec);
void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset);
void update_clipper();
void update_clipper_on_render();
void set_connectors_editing() { m_connectors_editing = true; }
void invalidate_cut_plane();
BoundingBoxf3 bounding_box() const;
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const;
protected:
virtual bool on_init() override;
virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual std::string on_get_name() const override;
virtual void on_set_state() override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_dragging(const UpdateData& data) override;
virtual void on_render() override;
#if ENABLE_RAYCAST_PICKING
bool on_init() override;
void on_load(cereal::BinaryInputArchive&ar) override;
void on_save(cereal::BinaryOutputArchive&ar) const override;
std::string on_get_name() const override;
void on_set_state() override;
CommonGizmosDataID on_get_requirements() const override;
void on_set_hover_id() override;
bool on_is_activable() const override;
bool on_is_selectable() const override;
Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const;
void dragging_grabber_z(const GLGizmoBase::UpdateData &data);
void dragging_grabber_xy(const GLGizmoBase::UpdateData &data);
void dragging_connector(const GLGizmoBase::UpdateData &data);
void on_dragging(const UpdateData&data) override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_render() override;
void render_debug_input_window();
void adjust_window_position(float x, float y, float bottom_limit);
void unselect_all_connectors();
void select_all_connectors();
void render_shortcuts();
void apply_selected_connectors(std::function<void(size_t idx)> apply_fn);
void render_connectors_input_window(CutConnectors &connectors);
void render_build_size();
void reset_cut_plane();
void set_connectors_editing(bool connectors_editing);
void render_cut_plane_input_window(CutConnectors &connectors);
void init_input_window_data(CutConnectors &connectors);
void render_input_window_warning() const;
bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position);
bool delete_selected_connectors(CutConnectors&connectors);
void select_connector(int idx, bool select);
bool is_selection_changed(bool alt_down, bool shift_down);
void process_selection_rectangle(CutConnectors &connectors);
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
#else
virtual void on_render_for_picking() override;
#endif // ENABLE_RAYCAST_PICKING
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
void update_raycasters_for_picking();
void set_volumes_picking_state(bool state);
void update_raycasters_for_picking_transform();
void on_render_input_window(float x, float y, float bottom_limit) override;
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); }
std::string get_action_snapshot_name() override { return _u8L("Cut gizmo editing"); }
private:
void perform_cut(const Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
BoundingBoxf3 bounding_box() const;
void update_contours();
void set_center(const Vec3d& center);
bool render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in);
void render_move_center_input(int axis);
void render_connect_mode_radio_button(CutConnectorMode mode);
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
bool render_connect_type_radio_button(CutConnectorType type);
Transform3d get_volume_transformation(const ModelVolume* volume) const;
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
void render_connectors();
bool can_perform_cut() const;
void apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object);
bool cut_line_processing() const;
void discard_cut_line_processing();
void render_cut_plane();
void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
void render_rotation_snapping(Axis axis, const ColorRGBA& color);
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix);
void render_cut_plane_grabbers();
void render_cut_line();
void perform_cut(const Selection&selection);
void set_center_pos(const Vec3d&center_pos, bool force = false);
bool update_bb();
void init_picking_models();
void init_rendering_items();
void render_clipper_cut();
void clear_selection();
void reset_connectors();
void init_connector_shapes();
void update_connector_shape();
void validate_connector_settings();
bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position);
};
} // namespace GUI

File diff suppressed because it is too large Load Diff

View File

@ -478,19 +478,19 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::min(1., pos + 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}
@ -885,7 +885,7 @@ RENDER_AGAIN:
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -894,7 +894,7 @@ RENDER_AGAIN:
ImGui::PushItemWidth(window_width - settings_sliders_left);
float clp_dist = m_c->object_clipper()->get_position();
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
// make sure supports are shown/hidden as appropriate
bool show_sups = m_c->instances_hider()->are_supports_shown();

View File

@ -31,7 +31,7 @@ public:
void data_changed() override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points();
bool is_selection_rectangle_dragging() const {
bool is_selection_rectangle_dragging() const override {
return m_selection_rectangle.is_dragging();
}

View File

@ -476,7 +476,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_imgui->text(m_desc.at("clipping_of_view"));
} else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); });
wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); });
}
}
@ -484,7 +484,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
m_c->object_clipper()->set_position(clp_dist, true);
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {

View File

@ -8,6 +8,7 @@
#if ENABLE_LEGACY_OPENGL_REMOVAL
#include "slic3r/GUI/Plater.hpp"
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#include "libslic3r/Model.hpp"
#include <GL/glew.h>
@ -79,7 +80,8 @@ std::string GLGizmoMove3D::on_get_name() const
bool GLGizmoMove3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
const Selection& selection = m_parent.get_selection();
return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty();
}
void GLGizmoMove3D::on_start_dragging()

View File

@ -537,7 +537,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
pos = action == SLAGizmoEventType::MouseWheelDown
? std::max(0., pos - 0.01)
: std::min(1., pos + 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
else if (alt_down) {
@ -573,7 +573,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}

View File

@ -12,6 +12,7 @@
#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Model.hpp"
#include <GL/glew.h>
@ -868,7 +869,8 @@ std::string GLGizmoRotate3D::on_get_name() const
bool GLGizmoRotate3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
const Selection& selection = m_parent.get_selection();
return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty();
}
void GLGizmoRotate3D::on_start_dragging()

View File

@ -8,6 +8,7 @@
#if ENABLE_LEGACY_OPENGL_REMOVAL
#include "slic3r/GUI/Plater.hpp"
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#include "libslic3r/Model.hpp"
#include <GL/glew.h>
@ -103,6 +104,12 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
return use_grabbers(mouse_event);
}
void GLGizmoScale3D::enable_ununiversal_scale(bool enable)
{
for (unsigned int i = 0; i < 6; ++i)
m_grabbers[i].enabled = enable;
}
void GLGizmoScale3D::data_changed()
{
#if ENABLE_WORLD_COORDINATE
@ -161,7 +168,7 @@ std::string GLGizmoScale3D::on_get_name() const
bool GLGizmoScale3D::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
return !selection.is_empty() && !selection.is_wipe_tower();
return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty() && !selection.is_wipe_tower();
}
void GLGizmoScale3D::on_start_dragging()
@ -448,7 +455,7 @@ void GLGizmoScale3D::on_render()
// draw grabbers
render_grabbers(grabber_mean_size);
}
else if (m_hover_id == 0 || m_hover_id == 1) {
else if ((m_hover_id == 0 || m_hover_id == 1) && m_grabbers[0].enabled && m_grabbers[1].enabled) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
// draw connections
#if ENABLE_GL_CORE_PROFILE
@ -493,7 +500,7 @@ void GLGizmoScale3D::on_render()
shader->stop_using();
}
}
else if (m_hover_id == 2 || m_hover_id == 3) {
else if ((m_hover_id == 2 || m_hover_id == 3) && m_grabbers[2].enabled && m_grabbers[3].enabled) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
// draw connections
#if ENABLE_GL_CORE_PROFILE
@ -538,7 +545,7 @@ void GLGizmoScale3D::on_render()
shader->stop_using();
}
}
else if (m_hover_id == 4 || m_hover_id == 5) {
else if ((m_hover_id == 4 || m_hover_id == 5) && m_grabbers[4].enabled && m_grabbers[5].enabled) {
#if ENABLE_LEGACY_OPENGL_REMOVAL
// draw connections
#if ENABLE_GL_CORE_PROFILE

View File

@ -85,6 +85,7 @@ public:
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
void enable_ununiversal_scale(bool enable);
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;

View File

@ -156,7 +156,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -165,7 +165,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel")))
m_c->object_clipper()->set_position(clp_dist, true);
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
ImGui::Separator();
if (m_imgui->button(m_desc.at("remove_all"))) {

View File

@ -673,19 +673,19 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::min(1., pos + 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}
@ -972,7 +972,7 @@ RENDER_AGAIN:
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -981,7 +981,7 @@ RENDER_AGAIN:
ImGui::PushItemWidth(window_width - clipping_slider_left);
float clp_dist = m_c->object_clipper()->get_position();
if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
if (m_imgui->button("?")) {

View File

@ -62,8 +62,8 @@ public:
void delete_selected_points(bool force = false);
//ClippingPlane get_sla_clipping_plane() const;
bool is_in_editing_mode() const { return m_editing_mode; }
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
bool is_in_editing_mode() const override { return m_editing_mode; }
bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); }
bool has_backend_supports() const;
void reslice_SLA_supports(bool postpone_error_messages = false) const;

View File

@ -425,8 +425,11 @@ void ObjectClipper::render_cut() const
if (m_clp_ratio == 0.)
return;
const SelectionInfo* sel_info = get_pool()->selection_info();
int sel_instance_idx = sel_info->get_active_instance();
if (sel_instance_idx < 0)
return;
const ModelObject* mo = sel_info->model_object();
const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation();
size_t clipper_id = 0;
for (const ModelVolume* mv : mo->volumes) {
@ -440,19 +443,30 @@ void ObjectClipper::render_cut() const
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
#if ENABLE_LEGACY_OPENGL_REMOVAL
clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f });
clipper->render_contour({ 1.f, 1.f, 1.f, 1.f});
#else
glsafe(::glPushMatrix());
glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
clipper->render_cut();
glsafe(::glPopMatrix());
glsafe(::glColor3f(1.f, 1.f, 1.f));
clipper->render_contour();
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
++clipper_id;
}
}
bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const std::unique_ptr<MeshClipper>& cl) { return cl->is_projection_inside_cut(point); });
}
void ObjectClipper::set_position(double pos, bool keep_normal)
bool ObjectClipper::has_valid_contour() const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const std::unique_ptr<MeshClipper>& cl) { return cl->has_valid_contour(); });
}
void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal)
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
int active_inst = get_pool()->selection_info()->get_active_instance();
@ -470,7 +484,36 @@ void ObjectClipper::set_position(double pos, bool keep_normal)
get_pool()->get_canvas()->set_as_dirty();
}
void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos)
{
m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset));
m_clp_ratio = pos;
get_pool()->get_canvas()->set_as_dirty();
}
const ClippingPlane* ObjectClipper::get_clipping_plane(bool ignore_hide_clipped) const
{
static const ClippingPlane no_clip = ClippingPlane::ClipsNothing();
return (ignore_hide_clipped || m_hide_clipped) ? m_clp.get() : &no_clip;
}
void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width)
{
m_hide_clipped = hide_clipped;
for (auto& clipper : m_clippers)
clipper->set_behaviour(fill_cut, contour_width);
}
void ObjectClipper::pass_mouse_click(const Vec3d& pt)
{
for (auto& clipper : m_clippers)
clipper->pass_mouse_click(pt);
}
std::vector<Vec3d> ObjectClipper::get_disabled_contours() const
{
return std::vector<Vec3d>();
}
void SupportsClipper::on_update()
{
@ -557,11 +600,13 @@ void SupportsClipper::render_cut() const
#if ENABLE_LEGACY_OPENGL_REMOVAL
m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f });
#else
glsafe(::glPushMatrix());
glsafe(::glColor3f(1.0f, 0.f, 0.37f));
m_clipper->render_cut();
glsafe(::glPopMatrix());
glsafe(::glColor3f(1.0f, 1.f, 1.f));
m_clipper->render_contour();
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
}

View File

@ -255,10 +255,19 @@ public:
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
void set_position(double pos, bool keep_normal);
void set_normal(const Vec3d& dir);
double get_position() const { return m_clp_ratio; }
ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const;
void render_cut() const;
void set_position_by_ratio(double pos, bool keep_normal);
void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos);
void set_behavior(bool hide_clipped, bool fill_cut, double contour_width);
void pass_mouse_click(const Vec3d& pt);
std::vector<Vec3d> get_disabled_contours() const;
bool is_projection_inside_cut(const Vec3d& point_in) const;
bool has_valid_contour() const;
protected:
@ -271,6 +280,7 @@ private:
std::unique_ptr<ClippingPlane> m_clp;
double m_clp_ratio = 0.;
double m_active_inst_bb_radius = 0.;
bool m_hide_clipped = true;
};

View File

@ -100,7 +100,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1));
m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2));
m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3));
m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4));
m_gizmos.emplace_back(new GLGizmoCut3D(m_parent, "cut.svg", 4));
m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5));
m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6));
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
@ -290,6 +290,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MmuSegmentation)
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Cut)
return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else
return false;
}
@ -507,7 +509,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
{
// Sla gizmo selects all support points
if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::SelectAll))
if ((m_current == SlaSupports || m_current == Hollow || m_current == Cut) && gizmo_event(SLAGizmoEventType::SelectAll))
processed = true;
break;
@ -551,7 +553,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case WXK_BACK:
case WXK_DELETE:
{
if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete))
if ((m_current == SlaSupports || m_current == Hollow || m_current == Cut) && gizmo_event(SLAGizmoEventType::Delete))
processed = true;
break;
@ -610,20 +612,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
if (evt.GetEventType() == wxEVT_KEY_UP)
{
if (m_current == SlaSupports || m_current == Hollow)
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut)
{
bool is_editing = true;
bool is_rectangle_dragging = false;
if (m_current == SlaSupports) {
GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current());
is_editing = gizmo->is_in_editing_mode();
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
}
else {
GLGizmoHollow* gizmo = dynamic_cast<GLGizmoHollow*>(get_current());
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
}
GLGizmoBase* gizmo = get_current();
const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode();
const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
if (keyCode == WXK_SHIFT)
{
@ -645,7 +638,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
else if (evt.GetEventType() == wxEVT_KEY_DOWN)
{
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT))
&& dynamic_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode())
&& get_current()->is_in_editing_mode())
{
// m_parent.set_cursor(GLCanvas3D::Cross);
processed = true;
@ -653,8 +646,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
else if (m_current == Cut)
{
auto do_move = [this, &processed](double delta_z) {
GLGizmoCut* cut = dynamic_cast<GLGizmoCut*>(get_current());
cut->set_cut_z(delta_z + cut->get_cut_z());
GLGizmoCut3D* cut = dynamic_cast<GLGizmoCut3D*>(get_current());
cut->shift_cut_z(delta_z);
processed = true;
};
@ -662,6 +655,9 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
{
case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; }
case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; }
case WXK_SHIFT : case WXK_ALT: {
processed = get_current()->is_in_editing_mode();
}
default: { break; }
}
} else if (m_current == Simplify && keyCode == WXK_ESCAPE) {
@ -1044,6 +1040,11 @@ GLGizmoBase* GLGizmosManager::get_current() const
return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get();
}
GLGizmoBase* GLGizmosManager::get_gizmo(GLGizmosManager::EType type) const
{
return ((type == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[type].get();
}
GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const
{
std::vector<size_t> selectable_idxs = get_selectable_idxs();

View File

@ -200,6 +200,7 @@ public:
EType get_current_type() const { return m_current; }
GLGizmoBase* get_current() const;
GLGizmoBase* get_gizmo(GLGizmosManager::EType type) const;
EType get_gizmo_from_name(const std::string& gizmo_name) const;
bool is_running() const;

View File

@ -59,6 +59,11 @@ static const std::map<const wchar_t, std::string> font_icons = {
{ImGui::PreferencesHoverButton, "notification_preferences_hover"},
{ImGui::SliderFloatEditBtnIcon, "edit_button" },
{ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" },
{ImGui::ExpandBtn , "expand_btn" },
{ImGui::CollapseBtn , "collapse_btn" },
{ImGui::RevertButton , "undo" },
{ImGui::WarningMarkerSmall , "notification_warning" },
{ImGui::InfoMarkerSmall , "notification_info" },
};
static const std::map<const wchar_t, std::string> font_icons_large = {

View File

@ -16,15 +16,27 @@
#include <igl/unproject.h>
#include <cstdint>
namespace Slic3r {
namespace GUI {
void MeshClipper::set_behaviour(bool fill_cut, double contour_width)
{
if (fill_cut != m_fill_cut || contour_width != m_contour_width)
m_result.reset();
m_fill_cut = fill_cut;
m_contour_width = contour_width;
}
void MeshClipper::set_plane(const ClippingPlane& plane)
{
if (m_plane != plane) {
m_plane = plane;
m_triangles_valid = false;
m_result.reset();
}
}
@ -33,7 +45,7 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane)
{
if (m_limiting_plane != plane) {
m_limiting_plane = plane;
m_triangles_valid = false;
m_result.reset();
}
}
@ -43,8 +55,7 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
{
if (m_mesh != &mesh) {
m_mesh = &mesh;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
@ -52,8 +63,7 @@ void MeshClipper::set_negative_mesh(const TriangleMesh& mesh)
{
if (m_negative_mesh != &mesh) {
m_negative_mesh = &mesh;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
@ -63,8 +73,7 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
{
if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
m_trafo = trafo;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
@ -74,13 +83,9 @@ void MeshClipper::render_cut(const ColorRGBA& color)
void MeshClipper::render_cut()
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
{
if (! m_triangles_valid)
if (! m_result)
recalculate_triangles();
#if ENABLE_LEGACY_OPENGL_REMOVAL
if (m_model.vertices_count() == 0 || m_model.indices_count() == 0)
return;
GLShaderProgram* curr_shader = wxGetApp().get_current_shader();
if (curr_shader != nullptr)
curr_shader->stop_using();
@ -91,8 +96,10 @@ void MeshClipper::render_cut()
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
m_model.set_color(color);
m_model.render();
for (CutIsland& isl : m_result->cut_islands) {
isl.model.set_color(isl.disabled ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : color);
isl.model.render();
}
shader->stop_using();
}
@ -105,19 +112,80 @@ void MeshClipper::render_cut()
}
#if ENABLE_LEGACY_OPENGL_REMOVAL
void MeshClipper::render_contour(const ColorRGBA& color)
#else
void MeshClipper::render_contour()
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
{
if (! m_result)
recalculate_triangles();
#if ENABLE_LEGACY_OPENGL_REMOVAL
GLShaderProgram* curr_shader = wxGetApp().get_current_shader();
if (curr_shader != nullptr)
curr_shader->stop_using();
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
for (CutIsland& isl : m_result->cut_islands) {
isl.model_expanded.set_color(color);
isl.model_expanded.render();
}
shader->stop_using();
}
if (curr_shader != nullptr)
curr_shader->start_using();
#else
if (m_vertex_array_expanded.has_VBOs())
m_vertex_array_expanded.render();
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
}
bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const
{
if (!m_result || m_result->cut_islands.empty())
return false;
Vec3d point = m_result->trafo.inverse() * point_in;
Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y()));
for (const CutIsland& isl : m_result->cut_islands) {
if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d))
return true;
}
return false;
}
bool MeshClipper::has_valid_contour() const
{
return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); });
}
void MeshClipper::pass_mouse_click(const Vec3d& point_in)
{
if (! m_result || m_result->cut_islands.empty())
return;
Vec3d point = m_result->trafo.inverse() * point_in;
Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y()));
for (CutIsland& isl : m_result->cut_islands) {
if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d))
isl.disabled = ! isl.disabled;
}
}
void MeshClipper::recalculate_triangles()
{
#if ENABLE_WORLD_COORDINATE
const Transform3f instance_matrix_no_translation_no_scaling = m_trafo.get_rotation_matrix().cast<float>();
#else
const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
#endif // ENABLE_WORLD_COORDINATE
// Calculate clipping plane normal in mesh coordinates.
const Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
const Vec3d up = up_noscale.cast<double>().cwiseProduct(m_trafo.get_scaling_factor());
// Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
const float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
m_result = ClipResult();
auto plane_mesh = Eigen::Hyperplane<double, 3>(m_plane.get_normal(), -m_plane.distance(Vec3d::Zero())).transform(m_trafo.get_matrix().inverse());
const Vec3d up = plane_mesh.normal();
const float height_mesh = -plane_mesh.offset();
// Now do the cutting
MeshSlicingParams slicing_params;
@ -137,6 +205,8 @@ void MeshClipper::recalculate_triangles()
tr.rotate(q);
tr = m_trafo.get_matrix() * tr;
m_result->trafo = tr;
if (m_limiting_plane != ClippingPlane::ClipsNothing())
{
// Now remove whatever ended up below the limiting plane (e.g. sinking objects).
@ -190,42 +260,108 @@ void MeshClipper::recalculate_triangles()
}
}
m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.);
tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting
Transform3d tr2 = tr;
tr2.pretranslate(0.002 * m_plane.get_normal().normalized());
#if ENABLE_LEGACY_OPENGL_REMOVAL
m_model.reset();
std::vector<Vec2f> triangles2d;
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_vertices(m_triangles2d.size());
init_data.reserve_indices(m_triangles2d.size());
for (const ExPolygon& exp : expolys) {
triangles2d.clear();
// vertices + indices
for (auto it = m_triangles2d.cbegin(); it != m_triangles2d.cend(); it = it + 3) {
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - m_triangles2d.cbegin();
init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);
m_result->cut_islands.push_back(CutIsland());
CutIsland& isl = m_result->cut_islands.back();
if (m_fill_cut) {
triangles2d = triangulate_expolygon_2f(exp, m_trafo.get_matrix().matrix().determinant() < 0.);
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_vertices(triangles2d.size());
init_data.reserve_indices(triangles2d.size());
// vertices + indices
for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) {
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - triangles2d.cbegin();
init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);
}
if (!init_data.is_empty())
isl.model.init_from(std::move(init_data));
}
if (m_contour_width != 0. && ! exp.contour.empty()) {
triangles2d.clear();
// The contours must not scale with the object. Check the scale factor
// in the respective directions, create a scaled copy of the ExPolygon
// offset it and then unscale the result again.
Transform3d t = tr;
t.translation() = Vec3d::Zero();
double scale_x = (t * Vec3d::UnitX()).norm();
double scale_y = (t * Vec3d::UnitY()).norm();
// To prevent overflow after scaling, downscale the input if needed:
double extra_scale = 1.;
int32_t limit = int32_t(std::min(std::numeric_limits<coord_t>::max() / (2. * scale_x), std::numeric_limits<coord_t>::max() / (2. * scale_y)));
int32_t max_coord = 0;
for (const Point& pt : exp.contour)
max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y())));
if (max_coord + m_contour_width >= limit)
extra_scale = 0.9 * double(limit) / max_coord;
ExPolygon exp_copy = exp;
if (extra_scale != 1.)
exp_copy.scale(extra_scale);
exp_copy.scale(scale_x, scale_y);
ExPolygons expolys_exp = offset_ex(exp_copy, scale_(m_contour_width));
expolys_exp = diff_ex(expolys_exp, ExPolygons({exp_copy}));
for (ExPolygon& e : expolys_exp) {
e.scale(1./scale_x, 1./scale_y);
if (extra_scale != 1.)
e.scale(1./extra_scale);
}
triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.);
GLModel::Geometry init_data = GLModel::Geometry();
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_vertices(triangles2d.size());
init_data.reserve_indices(triangles2d.size());
// vertices + indices
for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) {
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - triangles2d.cbegin();
init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2);
}
if (!init_data.is_empty())
isl.model_expanded.init_from(std::move(init_data));
}
isl.expoly = std::move(exp);
isl.expoly_bb = get_extents(exp);
}
if (!init_data.is_empty())
m_model.init_from(std::move(init_data));
#else
m_vertex_array.release_geometry();
for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) {
m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up);
m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up);
m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up);
const size_t idx = it - m_triangles2d.cbegin();
m_vertex_array.push_triangle(idx, idx+1, idx+2);
}
m_vertex_array.finalize_geometry(true);
#error NOT IMPLEMENTED
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
m_triangles_valid = true;
#if ENABLE_LEGACY_OPENGL_REMOVAL
#else
#error NOT IMPLEMENTED
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
}
@ -239,7 +375,7 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3
Vec3d& point, Vec3d& direction)
#else
void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const
Vec3d& point, Vec3d& direction)
#endif // ENABLE_RAYCAST_PICKING
{
Matrix4d modelview = camera.get_view_matrix().matrix();
@ -264,8 +400,11 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
size_t* facet_idx) const
size_t* facet_idx, bool* was_clipping_plane_hit) const
{
if (was_clipping_plane_hit)
*was_clipping_plane_hit = false;
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
@ -286,9 +425,26 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
break;
}
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
// All hits are either clipped, or there is an odd number of unclipped
// hits - meaning the nearest must be from inside the mesh.
if (i==hits.size()) {
// All hits are clipped.
return false;
}
if ((hits.size()-i) % 2 != 0) {
// There is an odd number of unclipped hits - meaning the nearest must be from inside the mesh.
// In that case, calculate intersection with the clipping place.
if (clipping_plane && was_clipping_plane_hit) {
direction = direction + point;
point = trafo * point; // transform to world coords
direction = trafo * direction - point;
Vec3d normal = -clipping_plane->get_normal().cast<double>();
double den = normal.dot(direction);
if (den != 0.) {
double t = (-clipping_plane->get_offset() - normal.dot(point))/den;
position = (point + t * direction).cast<float>();
*was_clipping_plane_hit = true;
}
}
return false;
}
@ -302,6 +458,35 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
return true;
}
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& position, Vec3d& normal) const
{
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
if (hits.empty())
return false; // no intersection found
// Now stuff the points in the provided vector and calculate normals if asked about them:
position = hits[0].position();
normal = hits[0].normal();
return true;
}
bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const
{
point = trafo.inverse() * point;
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
std::vector<AABBMesh::hit_result> neg_hits = m_emesh.query_ray_hits(point, -direction);
return !hits.empty() && !neg_hits.empty();
}
std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
const ClippingPlane* clipping_plane) const

View File

@ -14,6 +14,7 @@
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
#include <cfloat>
#include <optional>
#if ENABLE_RAYCAST_PICKING
#include <memory>
#endif // ENABLE_RAYCAST_PICKING
@ -80,6 +81,10 @@ public:
class MeshClipper
{
public:
// Set whether the cut should be triangulated and whether a cut
// contour should be calculated and shown.
void set_behaviour(bool fill_cut, double contour_width);
// Inform MeshClipper about which plane we want to use to cut the mesh
// This is supposed to be in world coordinates.
void set_plane(const ClippingPlane& plane);
@ -103,10 +108,16 @@ public:
// be set in world coords.
#if ENABLE_LEGACY_OPENGL_REMOVAL
void render_cut(const ColorRGBA& color);
void render_contour(const ColorRGBA& color);
#else
void render_cut();
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
void pass_mouse_click(const Vec3d& pt);
bool is_projection_inside_cut(const Vec3d& point) const;
bool has_valid_contour() const;
private:
void recalculate_triangles();
@ -115,13 +126,27 @@ private:
const TriangleMesh* m_negative_mesh = nullptr;
ClippingPlane m_plane;
ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing();
std::vector<Vec2f> m_triangles2d;
#if ENABLE_LEGACY_OPENGL_REMOVAL
GLModel m_model;
struct CutIsland {
GLModel model;
GLModel model_expanded;
ExPolygon expoly;
BoundingBox expoly_bb;
bool disabled = false;
};
struct ClipResult {
std::vector<CutIsland> cut_islands;
Transform3d trafo; // this rotates the cut into world coords
};
std::optional<ClipResult> m_result;
#else
#error NOT IMLEMENTED
GLIndexedVertexArray m_vertex_array;
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
bool m_triangles_valid = false;
bool m_fill_cut = true;
double m_contour_width = 0.;
};
@ -150,8 +175,10 @@ public:
{
}
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const;
static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction);
// void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
// Vec3d& point, Vec3d& direction) const;
#endif // ENABLE_RAYCAST_PICKING
// Given a mouse position, this returns true in case it is on the mesh.
@ -159,12 +186,18 @@ public:
const Vec2d& mouse_pos,
const Transform3d& trafo, // how to get the mesh into world coords
const Camera& camera, // current camera position
Vec3f& position, // where to save the positibon of the hit (mesh coords)
Vec3f& position, // where to save the positibon of the hit (mesh coords if mesh, world coords if clipping plane)
Vec3f& normal, // normal of the triangle that was hit
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
size_t* facet_idx = nullptr // index of the facet hit
size_t* facet_idx = nullptr, // index of the facet hit
bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section?
) const;
// Given a mouse position, this returns true in case it is on the mesh.
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& position, Vec3d& normal) const;
bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const;
// Given a vector of points in woorld coordinates, this returns vector
// of indices of points that are visible (i.e. not cut by clipping plane
// or obscured by part of the mesh.

View File

@ -1234,6 +1234,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty
case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break;
case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break;
case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break;
case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break;
default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break;
}
}

View File

@ -34,10 +34,19 @@ void ObjectDataViewModelNode::init_container()
#endif //__WXGTK__
}
void ObjectDataViewModelNode::invalidate_container()
{
#ifndef __WXGTK__
if (this->GetChildCount() == 0)
this->m_container = false;
#endif //__WXGTK__
}
static constexpr char LayerRootIcon[] = "edit_layers_all";
static constexpr char LayerIcon[] = "edit_layers_some";
static constexpr char WarningIcon[] = "exclamation";
static constexpr char WarningManifoldIcon[] = "exclamation_manifold";
static constexpr char LockIcon[] = "cut_";
struct InfoItemAtributes {
std::string name;
@ -48,6 +57,7 @@ const std::map<InfoItemType, InfoItemAtributes> INFO_ITEMS{
// info_item Type info_item Name info_item BitmapName
{ InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, },
{ InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, },
{ InfoItemType::CutConnectors, {L("Cut connectors"), "cut_connectors" }, },
{ InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation_"}, },
{ InfoItemType::Sinking, {L("Sinking"), "sinking"}, },
{ InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, },
@ -56,19 +66,16 @@ const std::map<InfoItemType, InfoItemAtributes> INFO_ITEMS{
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
Slic3r::ModelVolumeType type,
const wxBitmapBundle& bmp,
const bool is_text_volume,
const wxString& extruder,
const int idx/* = -1*/,
const std::string& warning_icon_name /*= std::string*/) :
const int idx/* = -1*/) :
m_parent(parent),
m_name(sub_obj_name),
m_type(itVolume),
m_volume_type(type),
m_is_text_volume(is_text_volume),
m_idx(idx),
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""),
m_warning_icon_name(warning_icon_name)
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
{
set_action_and_extruder_icons();
init_container();
@ -174,13 +181,6 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
*get_bmp_bundle(m_printable == piPrintable ? "eye_open" : "eye_closed");
}
void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name)
{
m_warning_icon_name = warning_icon_name;
if (warning_icon_name.empty())
m_bmp = m_empty_bmp;
}
void ObjectDataViewModelNode::update_settings_digest_bitmaps()
{
m_bmp = m_empty_bmp;
@ -329,6 +329,7 @@ ObjectDataViewModel::ObjectDataViewModel()
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
m_warning_bmp = *get_bmp_bundle(WarningIcon);
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
m_lock_bmp = *get_bmp_bundle(LockIcon);
for (auto item : INFO_ITEMS)
m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name);
@ -342,19 +343,55 @@ ObjectDataViewModel::~ObjectDataViewModel()
m_bitmap_cache = nullptr;
}
wxBitmapBundle& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name)
void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node)
{
return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp;
int vol_type = static_cast<int>(node->GetVolumeType());
bool is_volume_node = vol_type >= 0;
if (!node->has_warning_icon() && !node->has_lock()) {
node->SetBitmap(is_volume_node ? *m_volume_bmps.at(vol_type) : m_empty_bmp);
return;
}
std::string scaled_bitmap_name = std::string();
if (node->has_warning_icon())
scaled_bitmap_name += node->warning_icon_name();
if (node->has_lock())
scaled_bitmap_name += LockIcon;
if (is_volume_node)
scaled_bitmap_name += std::to_string(vol_type);
scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "-lm");
wxBitmapBundle* bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
if (!bmp) {
std::vector<wxBitmapBundle*> bmps;
if (node->has_warning_icon())
bmps.emplace_back(node->warning_icon_name() == WarningIcon ? &m_warning_bmp : &m_warning_manifold_bmp);
if (node->has_lock())
bmps.emplace_back(&m_lock_bmp);
if (is_volume_node)
bmps.emplace_back(m_volume_bmps[vol_type]);
bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps);
}
node->SetBitmap(*bmp);
}
wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
const int extruder,
const std::string& warning_icon_name/* = std::string()*/ )
void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock)
{
const wxString extruder_str = extruder == 0 ? _L("default") : wxString::Format("%d", extruder);
auto root = new ObjectDataViewModelNode(name, extruder_str);
node->SetWarningIconName(warning_icon_name);
node->SetLock(has_lock);
UpdateBitmapForNode(node);
}
wxDataViewItem ObjectDataViewModel::AddObject(const wxString &name,
const wxString& extruder,
const std::string& warning_icon_name,
const bool has_lock)
{
auto root = new ObjectDataViewModelNode(name, extruder);
// Add warning icon if detected auto-repaire
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
UpdateBitmapForNode(root, warning_icon_name, has_lock);
m_objects.push_back(root);
// notify control
@ -367,44 +404,30 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const int volume_idx,
const Slic3r::ModelVolumeType volume_type,
const bool is_text_volume /*- false*/,
const std::string& warning_icon_name/* = std::string()*/,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
const std::string& warning_icon_name,
const bool is_text_volume = false,
const wxString& extruder)
{
ObjectDataViewModelNode *root = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!root) return wxDataViewItem(0);
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
// get insertion position according to the existed Layers and/or Instances Items
int insert_position = get_root_idx(root, itLayerRoot);
if (insert_position < 0)
insert_position = get_root_idx(root, itInstanceRoot);
if (create_frst_child && root->m_volumes_cnt == 0)
{
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, nullptr, root->is_text_volume(), extruder_str, 0, root->m_warning_icon_name);
SetVolumeBitmap(node);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
root->m_volumes_cnt++;
if (insert_position >= 0) insert_position++;
}
const auto node = new ObjectDataViewModelNode(root, name, volume_type, nullptr, is_text_volume, extruder_str, root->m_volumes_cnt, warning_icon_name);
SetVolumeBitmap(node);
const auto node = new ObjectDataViewModelNode(root, name, volume_type, extruder, volume_idx);
UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// if part with errors is added, but object wasn't marked, then mark it
if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name &&
(root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) )
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
if (!warning_icon_name.empty() && warning_icon_name != root->warning_icon_name() &&
(!root->has_warning_icon() || root->warning_icon_name() == WarningManifoldIcon)) {
root->SetWarningIconName(warning_icon_name);
UpdateBitmapForNode(root);
}
// notify control
const wxDataViewItem child((void*)node);
@ -602,14 +625,12 @@ wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_i
wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder/* = 0*/,
const wxString& extruder,
const int index /* = -1*/)
{
ObjectDataViewModelNode *parent_node = static_cast<ObjectDataViewModelNode*>(parent_item.GetID());
if (!parent_node) return wxDataViewItem(0);
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
// get LayerRoot node
ObjectDataViewModelNode *layer_root_node;
wxDataViewItem layer_root_item;
@ -626,7 +647,7 @@ wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_
}
// Add layer node
ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str);
ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder);
if (index < 0)
layer_root_node->Append(layer_node);
else
@ -715,10 +736,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
delete node_parent;
ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__
if (obj_node->GetChildCount() == 0)
obj_node->m_container = false;
#endif //__WXGTK__
obj_node->invalidate_container();
ItemDeleted(ret_item, wxDataViewItem(node_parent));
return ret_item;
}
@ -734,10 +752,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
delete node_parent;
ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__
if (obj_node->GetChildCount() == 0)
obj_node->m_container = false;
#endif //__WXGTK__
obj_node->invalidate_container();
ItemDeleted(ret_item, wxDataViewItem(node_parent));
return ret_item;
}
@ -763,10 +778,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
node_parent->m_volumes_cnt = 0;
delete last_child_node;
#ifndef __WXGTK__
if (node_parent->GetChildCount() == 0)
node_parent->m_container = false;
#endif //__WXGTK__
node_parent->invalidate_container();
ItemDeleted(parent, wxDataViewItem(last_child_node));
wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED);
@ -801,10 +813,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
// set m_containet to FALSE if parent has no child
if (node_parent) {
#ifndef __WXGTK__
if (node_parent->GetChildCount() == 0)
node_parent->m_container = false;
#endif //__WXGTK__
node_parent->invalidate_container();
ret_item = parent;
}
@ -846,10 +855,7 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par
parent_node->set_printable_icon(last_inst_printable);
ItemDeleted(parent_item, inst_root_item);
ItemChanged(parent_item);
#ifndef __WXGTK__
if (parent_node->GetChildCount() == 0)
parent_node->m_container = false;
#endif //__WXGTK__
parent_node->invalidate_container();
}
// update object_node printable property
@ -894,10 +900,7 @@ void ObjectDataViewModel::DeleteChildren(wxDataViewItem& parent)
ItemDeleted(parent, item);
}
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root->m_container = false;
#endif //__WXGTK__
root->invalidate_container();
}
void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent)
@ -927,11 +930,7 @@ void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent)
ItemDeleted(parent, item);
}
root->m_volumes_cnt = 0;
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root->m_container = false;
#endif //__WXGTK__
root->invalidate_container();
}
void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent)
@ -1679,6 +1678,7 @@ void ObjectDataViewModel::UpdateBitmaps()
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
m_warning_bmp = *get_bmp_bundle(WarningIcon);
m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon);
m_lock_bmp = *get_bmp_bundle(LockIcon);
for (auto item : INFO_ITEMS)
m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name);
@ -1697,10 +1697,8 @@ void ObjectDataViewModel::UpdateBitmaps()
switch (node->m_type)
{
case itObject:
if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name);
break;
case itVolume:
SetVolumeBitmap(node);
UpdateBitmapForNode(node);
break;
case itLayerRoot:
node->m_bmp = *get_bmp_bundle(LayerRootIcon);
@ -1749,13 +1747,14 @@ void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->GetType() & itObject) {
node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
UpdateBitmapForNode(node, warning_icon_name, node->has_lock());
return;
}
if (node->GetType() & itVolume) {
node->SetWarningBitmap(GetVolumeIcon(*node, warning_icon_name), warning_icon_name);
node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
UpdateBitmapForNode(node, warning_icon_name, node->has_lock());
if (ObjectDataViewModelNode* parent = node->GetParent())
UpdateBitmapForNode(parent, warning_icon_name, parent->has_lock());
return;
}
}
@ -1770,12 +1769,9 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo
if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject)))
return;
if (node->GetType() & itVolume) {
node->SetWarningBitmap(*m_volume_bmps[static_cast<int>(node->volume_type())], "");
return;
}
node->SetWarningIconName(std::string());
UpdateBitmapForNode(node);
node->SetWarningBitmap(wxNullBitmap, "");
if (unmark_object)
{
wxDataViewItemArray children;
@ -1802,6 +1798,26 @@ void ObjectDataViewModel::UpdateWarningIcon(const wxDataViewItem& item, const st
AddWarningIcon(item, warning_icon_name);
}
void ObjectDataViewModel::UpdateLockIcon(const wxDataViewItem& item, bool has_lock)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->has_lock() == has_lock)
return;
node->SetLock(has_lock);
UpdateBitmapForNode(node);
if (node->GetType() & itObject) {
wxDataViewItemArray children;
GetChildren(item, children);
for (const wxDataViewItem& child : children)
UpdateLockIcon(child, has_lock);
}
ItemChanged(item);
}
} // namespace GUI
} // namespace Slic3r

View File

@ -51,6 +51,7 @@ enum class InfoItemType
Undef,
CustomSupports,
CustomSeam,
CutConnectors,
MmuSegmentation,
Sinking,
VariableLayerHeight
@ -79,9 +80,10 @@ class ObjectDataViewModelNode
PrintIndicator m_printable {piUndef};
wxBitmapBundle m_printable_icon;
std::string m_warning_icon_name{ "" };
bool m_has_lock{false};
std::string m_action_icon_name = "";
ModelVolumeType m_volume_type;
ModelVolumeType m_volume_type{ -1 };
bool m_is_text_volume{ false };
InfoItemType m_info_item_type {InfoItemType::Undef};
@ -100,11 +102,9 @@ public:
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
Slic3r::ModelVolumeType type,
const wxBitmapBundle& bmp,
const bool is_text_volume,
const wxString& extruder,
const int idx = -1,
const std::string& warning_icon_name = std::string());
const int idx = -1 );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
@ -130,7 +130,9 @@ public:
}
void init_container();
bool IsContainer() const
void invalidate_container();
bool IsContainer() const
{
return m_container;
}
@ -183,7 +185,8 @@ public:
void SetVolumeType(ModelVolumeType type) { m_volume_type = type; }
void SetBitmap(const wxBitmapBundle &icon) { m_bmp = icon; }
void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
void SetWarningBitmap(const wxBitmapBundle& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; }
void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; }
void SetLock(bool has_lock) { m_has_lock = has_lock; }
const wxBitmapBundle& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
@ -230,8 +233,6 @@ public:
void set_extruder_icon();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
// Set warning icon for node
void set_warning_icon(const std::string& warning_icon);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
@ -243,7 +244,9 @@ public:
bool valid();
#endif /* NDEBUG */
bool invalid() const { return m_idx < -1; }
bool has_warning_icon() const { return !m_warning_icon_name.empty(); }
bool has_warning_icon() const { return !m_warning_icon_name.empty(); }
bool has_lock() const { return m_has_lock; }
const std::string& warning_icon_name() const { return m_warning_icon_name; }
private:
friend class ObjectDataViewModel;
@ -266,6 +269,7 @@ class ObjectDataViewModel :public wxDataViewModel
wxBitmapBundle m_empty_bmp;
wxBitmapBundle m_warning_bmp;
wxBitmapBundle m_warning_manifold_bmp;
wxBitmapBundle m_lock_bmp;
wxDataViewCtrl* m_ctrl { nullptr };
@ -273,16 +277,17 @@ public:
ObjectDataViewModel();
~ObjectDataViewModel();
wxDataViewItem Add( const wxString &name,
const int extruder,
const std::string& warning_icon_name = std::string());
wxDataViewItem AddObject( const wxString &name,
const wxString& extruder,
const std::string& warning_icon_name,
const bool has_lock);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const int volume_idx,
const Slic3r::ModelVolumeType volume_type,
const std::string& warning_icon_name,
const bool is_text_volume = false,
const std::string& warning_icon_name = std::string(),
const int extruder = 0,
const bool create_frst_child = true);
const wxString& extruder);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
@ -290,7 +295,7 @@ public:
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder = 0,
const wxString& extruder,
const int index = -1);
size_t GetItemIndexForFirstVolume(ObjectDataViewModelNode* node_parent);
wxDataViewItem Delete(const wxDataViewItem &item);
@ -395,6 +400,7 @@ public:
void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
void UpdateLockIcon(const wxDataViewItem& item, bool has_lock);
bool HasWarningIcon(const wxDataViewItem& item) const;
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
@ -408,7 +414,8 @@ private:
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
void AddAllChildren(const wxDataViewItem& parent);
wxBitmapBundle& GetWarningBitmap(const std::string& warning_icon_name);
void UpdateBitmapForNode(ObjectDataViewModelNode* node);
void UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock);
};

View File

@ -97,6 +97,7 @@
#include "MsgDialog.hpp"
#include "ProjectDirtyStateManager.hpp"
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
#include "Gizmos/GLGizmoCut.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@ -1792,7 +1793,7 @@ struct Plater::priv
std::string get_config(const std::string &key) const;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool call_selection_changed = true);
fs::path get_export_file_path(GUI::FileType file_type);
wxString get_export_file(GUI::FileType file_type);
@ -1807,7 +1808,7 @@ struct Plater::priv
void select_all();
void deselect_all();
void remove(size_t obj_idx);
void delete_object_from_model(size_t obj_idx);
bool delete_object_from_model(size_t obj_idx);
void delete_all_objects_from_model();
void reset();
void mirror(Axis axis);
@ -2718,7 +2719,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// #define AUTOPLACEMENT_ON_LOAD
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool call_selection_changed /*= true*/)
{
const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones();
@ -2801,17 +2802,18 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo);
for (const size_t idx : obj_idxs) {
wxGetApp().obj_list()->add_object_to_list(idx);
wxGetApp().obj_list()->add_object_to_list(idx, call_selection_changed);
}
update();
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
for (const size_t idx : obj_idxs)
wxGetApp().obj_list()->update_info_items(idx);
object_list_changed();
if (call_selection_changed) {
update();
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
for (const size_t idx : obj_idxs)
wxGetApp().obj_list()->update_info_items(idx);
object_list_changed();
}
this->schedule_background_process();
return obj_idxs;
@ -2990,16 +2992,35 @@ void Plater::priv::remove(size_t obj_idx)
}
void Plater::priv::delete_object_from_model(size_t obj_idx)
bool Plater::priv::delete_object_from_model(size_t obj_idx)
{
// check if object isn't cut
// show warning message that "cut consistancy" will not be supported any more
ModelObject* obj = model.objects[obj_idx];
if (obj->is_cut()) {
InfoDialog dialog(q, _L("Delete object which is a part of cut object"),
_L("You try to delete an object which is a part of a cut object.\n"
"This action will break a cut correspondence.\n"
"After that PrusaSlicer can't garantie model consistency"),
false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING);
dialog.SetButtonLabel(wxID_YES, _L("Delete object"));
if (dialog.ShowModal() == wxID_CANCEL)
return false;
}
wxString snapshot_label = _L("Delete Object");
if (! model.objects[obj_idx]->name.empty())
snapshot_label += ": " + wxString::FromUTF8(model.objects[obj_idx]->name.c_str());
if (!obj->name.empty())
snapshot_label += ": " + wxString::FromUTF8(obj->name.c_str());
Plater::TakeSnapshot snapshot(q, snapshot_label);
m_worker.cancel_all();
if (obj->is_cut())
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
model.delete_object(obj_idx);
update();
object_list_changed();
return true;
}
void Plater::priv::delete_all_objects_from_model()
@ -4438,7 +4459,10 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&)
void Plater::priv::on_action_layersediting(SimpleEvent&)
{
view3D->enable_layers_editing(!view3D->is_layers_editing_enabled());
const bool enable_layersediting = !view3D->is_layers_editing_enabled();
view3D->enable_layers_editing(enable_layersediting);
if (enable_layersediting)
view3D->get_canvas3d()->reset_all_gizmos();
notification_manager->set_move_from_overlay(view3D->is_layers_editing_enabled());
}
@ -4481,7 +4505,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
selection.is_single_full_object() ||
selection.is_multiple_full_instance();
#if ENABLE_WORLD_COORDINATE
const bool is_part = selection.is_single_volume_or_modifier();
const bool is_part = selection.is_single_volume_or_modifier() && ! selection.is_any_connector();
#else
const bool is_part = selection.is_single_volume() || selection.is_single_modifier();
#endif // ENABLE_WORLD_COORDINATE
@ -4763,7 +4787,8 @@ bool Plater::priv::can_split(bool to_objects) const
bool Plater::priv::can_scale_to_print_volume() const
{
const BuildVolume::Type type = this->bed.build_volume().type();
return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
return !sidebar->obj_list()->has_selected_cut_object() &&
!view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
}
bool Plater::priv::layers_height_allowed() const
@ -4778,16 +4803,19 @@ bool Plater::priv::layers_height_allowed() const
bool Plater::priv::can_mirror() const
{
return get_selection().is_from_single_instance();
return !sidebar->obj_list()->has_selected_cut_object() && get_selection().is_from_single_instance();
}
bool Plater::priv::can_replace_with_stl() const
{
return get_selection().get_volume_idxs().size() == 1;
return !sidebar->obj_list()->has_selected_cut_object() && get_selection().get_volume_idxs().size() == 1;
}
bool Plater::priv::can_reload_from_disk() const
{
if (sidebar->obj_list()->has_selected_cut_object())
return false;
#if ENABLE_RELOAD_FROM_DISK_REWORK
// collect selected reloadable ModelVolumes
std::vector<std::pair<int, int>> selected_volumes = reloadable_volumes(model, get_selection());
@ -4901,8 +4929,12 @@ bool Plater::priv::can_fix_through_netfabb() const
bool Plater::priv::can_simplify() const
{
const int obj_idx = get_selected_object_idx();
// is object for simplification selected
if (get_selected_object_idx() < 0) return false;
// cut object can't be simplify
if (obj_idx < 0 || model.objects[obj_idx]->is_cut())
return false;
// is already opened?
if (q->canvas3D()->get_gizmos_manager().get_current_type() ==
GLGizmosManager::EType::Simplify)
@ -4918,8 +4950,7 @@ bool Plater::priv::can_increase_instances() const
if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false;
int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
return !sidebar->obj_list()->has_selected_cut_object();
}
bool Plater::priv::can_decrease_instances() const
@ -4928,8 +4959,7 @@ bool Plater::priv::can_decrease_instances() const
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
return false;
int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
return !sidebar->obj_list()->has_selected_cut_object();
}
bool Plater::priv::can_split_to_objects() const
@ -5735,7 +5765,7 @@ void Plater::reset_with_confirm()
reset();
}
void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_model(obj_idx); }
bool Plater::delete_object_from_model(size_t obj_idx) { return p->delete_object_from_model(obj_idx); }
void Plater::remove_selected()
{
@ -5912,23 +5942,29 @@ void Plater::toggle_layers_editing(bool enable)
canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting"));
}
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes)
void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
{
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
auto *object = p->model.objects[obj_idx];
auto* object = p->model.objects[obj_idx];
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
return;
Plater::TakeSnapshot snapshot(this, _L("Cut by Plane"));
wxBusyCursor wait;
const auto new_objects = object->cut(instance_idx, z, attributes);
remove(obj_idx);
p->load_model_objects(new_objects);
const auto new_objects = object->cut(instance_idx, cut_matrix, attributes);
model().delete_object(obj_idx);
sidebar().obj_list()->delete_object_from_list(obj_idx);
// suppress to call selection update for Object List to avoid call of early Gizmos on/off update
p->load_model_objects(new_objects, false, false);
// now process all updates of the 3d scene
update();
// Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),
// which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call
for (size_t idx = 0; idx < p->model.objects.size(); idx++)
wxGetApp().obj_list()->update_info_items(idx);
Selection& selection = p->get_selection();
size_t last_id = p->model.objects.size() - 1;

View File

@ -242,7 +242,7 @@ public:
void remove(size_t obj_idx);
void reset();
void reset_with_confirm();
void delete_object_from_model(size_t obj_idx);
bool delete_object_from_model(size_t obj_idx);
void remove_selected();
void increase_instances(size_t num = 1);
void decrease_instances(size_t num = 1);
@ -253,7 +253,7 @@ public:
void convert_unit(ConversionType conv_type);
void toggle_layers_editing(bool enable);
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes);
void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes);
void export_gcode(bool prefer_removable);
void export_stl_obj(bool extended = false, bool selection_only = false);

View File

@ -163,7 +163,7 @@ void SavePresetDialog::Item::update()
if (m_valid_type == ValidationType::Valid && existing)
{
if (m_preset_name == m_presets->get_selected_preset_name()) {
if (!rename && m_presets->get_edited_preset().is_dirty ||
if ((!rename && m_presets->get_edited_preset().is_dirty) ||
m_parent->get_preset_bundle()) // means that we save modifications from the DiffDialog
info_line = _L("Save preset modifications to existing user profile");
else

View File

@ -510,6 +510,28 @@ void Selection::volumes_changed(const std::vector<size_t> &map_volume_old_to_new
this->set_bounding_boxes_dirty();
}
bool Selection::is_any_connector() const
{
const int obj_idx = get_object_idx();
if ((is_any_volume() || is_any_modifier() || is_mixed()) && // some solid_part AND/OR modifier is selected
obj_idx >= 0 && m_model->objects[obj_idx]->is_cut()) {
const ModelVolumePtrs& obj_volumes = m_model->objects[obj_idx]->volumes;
for (size_t vol_idx = 0; vol_idx < obj_volumes.size(); vol_idx++)
if (obj_volumes[vol_idx]->is_cut_connector())
for (const GLVolume* v : *m_volumes)
if (v->object_idx() == obj_idx && v->volume_idx() == (int)vol_idx && v->selected)
return true;
}
return false;
}
bool Selection::is_any_cut_volume() const
{
const int obj_idx = get_object_idx();
return is_any_volume() && obj_idx >= 0 && m_model->objects[obj_idx]->is_cut();
}
bool Selection::is_single_full_instance() const
{
if (m_type == SingleFullInstance)
@ -1524,7 +1546,7 @@ int Selection::bake_transform_if_needed() const
if (needs_baking) {
MessageDialog dlg((wxWindow*)wxGetApp().mainframe,
_L("The currently manipulated object is tilted or contains tilted parts (rotation angles are not multiples of 90<EFBFBD>). "
_L("The currently manipulated object is tilted or contains tilted parts (rotation angles are not multiples of 90°). "
"Non-uniform scaling of tilted objects is only possible in non-local coordinate systems, "
"once the rotation is embedded into the object coordinates.") + "\n" +
_L("This operation is irreversible.") + "\n" +

View File

@ -320,6 +320,8 @@ public:
bool is_single_volume() const { return m_type == SingleVolume; }
bool is_multiple_volume() const { return m_type == MultipleVolume; }
bool is_any_volume() const { return is_single_volume() || is_multiple_volume(); }
bool is_any_connector() const;
bool is_any_cut_volume() const;
bool is_mixed() const { return m_type == Mixed; }
bool is_from_single_instance() const { return get_instance_idx() != -1; }
bool is_from_single_object() const;

View File

@ -414,7 +414,7 @@ public:
std::string get_left_preset_name(Preset::Type type);
std::string get_right_preset_name(Preset::Type type);
std::vector<std::string> get_selected_options(Preset::Type type) const { return std::move(m_tree->options(type, true)); }
std::vector<std::string> get_selected_options(Preset::Type type) const { return m_tree->options(type, true); }
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;