Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f608698439 | ||
|
|
432d6be518 | ||
|
|
3e7f0c027a | ||
|
|
504a143b98 | ||
|
|
a87639ac41 | ||
|
|
ecbd03fed7 | ||
|
|
ccbb73baaf | ||
|
|
1d577ea4e2 | ||
|
|
2bd868ecd0 | ||
|
|
9e5bfc272c | ||
|
|
fefedb66c4 | ||
|
|
1dcbb2c02a | ||
|
|
b4577dbdc4 | ||
|
|
eb5b9a77b9 | ||
|
|
c1e6de7e4e | ||
|
|
1496906939 | ||
|
|
6b8df05d40 | ||
|
|
47f1f2de5f | ||
|
|
5dcf89d094 | ||
|
|
318f6178ab | ||
|
|
37dbad5752 | ||
|
|
029bc7928f | ||
|
|
0a8fce0913 | ||
|
|
3942ba7005 | ||
|
|
0110572a77 | ||
|
|
2c2869ab07 | ||
|
|
bf63eb1226 | ||
|
|
38e57888a5 | ||
|
|
fc079b2e5b | ||
|
|
a88843c6f8 |
@@ -61,6 +61,7 @@ CheckOptions:
|
||||
termios\.h;
|
||||
[/\\](um|shared)[/\\].*;
|
||||
sal\.h;
|
||||
tchar\.h;
|
||||
clipper[/\\]clipper\.hpp;
|
||||
png(lib)?conf\.h;
|
||||
mcut[/\\]platform\.h;
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: Look for changed C++ files
|
||||
id: changes
|
||||
run: |
|
||||
if git diff --name-only HEAD^1 -- src tests | grep -qE '\.(cpp|cc|cxx|hpp|h|hxx)$'; then
|
||||
if git -c core.quotePath=false diff --name-only HEAD^1 -- src tests | grep -qE '\.(cpp|cc|cxx|hpp|h|hxx)$'; then
|
||||
echo "cpp=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No C++ changes under src/ or tests/."
|
||||
|
||||
@@ -90,6 +90,7 @@ See the [Localization guide](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/
|
||||
- Plural entries: read `nplurals` from the catalog's `Plural-Forms` header (it is **not** always 2 — ja/ko/zh/th/vi use 1, ru/cs/pl/lt use 3, uk uses 4). Each form must be genuinely inflected for its quantity; repeating one sentence across all forms is a bug in Slavic/Baltic languages, though it is correct for Turkish and Hungarian.
|
||||
- An entry whose `msgstr` equals its `msgid` is untranslated even though it is not empty; a plural entry with any empty form is likewise incomplete.
|
||||
- Mark machine-produced translations with an `# AI Translated` translator comment. Don't add it to a human translation you didn't actually rewrite.
|
||||
- When you can't be sure of a machine translation's meaning or UI wording, also add `# Needs human review: <what to check>`. Never use `fuzzy` for this — fuzzy entries are hidden from users.
|
||||
- Don't reflow or re-wrap unrelated entries — keep the diff limited to the strings you changed.
|
||||
|
||||
### Verifying
|
||||
|
||||
@@ -131,6 +131,13 @@
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>OrcaSlicer uses your local network to discover and connect to printers.</string>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_octoprint._tcp</string>
|
||||
<string>_http._tcp</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!-- Disable App Transport Security. Resolves https://github.com/OrcaSlicer/OrcaSlicer/issues/791 -->
|
||||
|
||||
@@ -52,8 +52,6 @@ src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.h
|
||||
src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.h
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.h
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.cpp
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp
|
||||
src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.h
|
||||
@@ -149,6 +147,7 @@ src/slic3r/GUI/HMSPanel.cpp
|
||||
src/slic3r/GUI/MainFrame.cpp
|
||||
src/slic3r/GUI/MediaPlayCtrl.cpp
|
||||
src/slic3r/GUI/MediaFilePanel.cpp
|
||||
src/slic3r/GUI/OrcaFilesPanel.cpp
|
||||
src/slic3r/GUI/ImageGrid.cpp
|
||||
src/slic3r/GUI/Widgets/ImageSwitchButton.cpp
|
||||
src/slic3r/GUI/Printer/PrinterFileSystem.cpp
|
||||
@@ -301,3 +300,64 @@ src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
||||
src/slic3r/GUI/KeyChord.cpp
|
||||
src/slic3r/GUI/Shortcuts.cpp
|
||||
src/libslic3r/CAD/CadDocument.cpp
|
||||
src/libslic3r/Support/TreeSupport3D.cpp
|
||||
src/slic3r/Config/Snapshot.cpp
|
||||
src/slic3r/GUI/AmsMappingPopupUpdate.cpp
|
||||
src/slic3r/GUI/AuxiliaryDataViewModel.cpp
|
||||
src/slic3r/GUI/AuxiliaryDialog.cpp
|
||||
src/slic3r/GUI/BBLStatusBarPrint.cpp
|
||||
src/slic3r/GUI/BBLStatusBarSend.cpp
|
||||
src/slic3r/GUI/BaseTransparentDPIFrame.cpp
|
||||
src/slic3r/GUI/CloneDialog.cpp
|
||||
src/slic3r/GUI/ColorDecomposeDialog.cpp
|
||||
src/slic3r/GUI/ColorDecomposeSupport.cpp
|
||||
src/slic3r/GUI/ConfigWizard.cpp
|
||||
src/slic3r/GUI/DeviceCore/DevCalib.cpp
|
||||
src/slic3r/GUI/DeviceCore/DevUpgrade.cpp
|
||||
src/slic3r/GUI/DeviceTab/uiAMSBestPositionPopup.cpp
|
||||
src/slic3r/GUI/DeviceTab/wgtMsgPanel.cpp
|
||||
src/slic3r/GUI/DragDropPanel.cpp
|
||||
src/slic3r/GUI/GLCanvas3D.hpp
|
||||
src/slic3r/GUI/GUI_Utils.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp
|
||||
src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp
|
||||
src/slic3r/GUI/Jobs/SLAImportDialog.hpp
|
||||
src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.cpp
|
||||
src/slic3r/GUI/Jobs/TextureDisplacementDebugJob.cpp
|
||||
src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp
|
||||
src/slic3r/GUI/MixedFilamentDialog.cpp
|
||||
src/slic3r/GUI/ModelMall.cpp
|
||||
src/slic3r/GUI/OAuthDialog.cpp
|
||||
src/slic3r/GUI/PluginPickerDialog.cpp
|
||||
src/slic3r/GUI/PluginsConfigDialog.cpp
|
||||
src/slic3r/GUI/PluginsDialog.cpp
|
||||
src/slic3r/GUI/PublishSettingsDialog.hpp
|
||||
src/slic3r/GUI/PurgeModeDialog.cpp
|
||||
src/slic3r/GUI/TerminalDialog.cpp
|
||||
src/slic3r/GUI/TextureImportDialog.cpp
|
||||
src/slic3r/GUI/TextureLibrary.cpp
|
||||
src/slic3r/GUI/TextureProjectorFrame.cpp
|
||||
src/slic3r/GUI/UVEditorCanvas.cpp
|
||||
src/slic3r/GUI/UserManager.cpp
|
||||
src/slic3r/GUI/WebDownPluginDlg.cpp
|
||||
src/slic3r/GUI/WebViewDialog.cpp
|
||||
src/slic3r/GUI/Widgets/AMSItem.cpp
|
||||
src/slic3r/GUI/Widgets/DialogButtons.cpp
|
||||
src/slic3r/Utils/NetworkAgentFactory.cpp
|
||||
src/slic3r/plugin/PluginAuditManager.cpp
|
||||
src/slic3r/plugin/PluginConfig.cpp
|
||||
src/slic3r/plugin/PluginResolver.cpp
|
||||
src/libslic3r/CAD/SketchEngine.cpp
|
||||
src/libslic3r/CAD/GeometryEngine.cpp
|
||||
src/slic3r/GUI/CAD/DesignOffer.hpp
|
||||
src/slic3r/GUI/CAD/DesignSketchTool.cpp
|
||||
src/slic3r/GUI/EncodedFilament.cpp
|
||||
src/slic3r/GUI/HttpServer.cpp
|
||||
src/slic3r/GUI/Plater.hpp
|
||||
src/slic3r/GUI/PostProcessor.cpp
|
||||
src/slic3r/GUI/TaskManager.cpp
|
||||
src/slic3r/GUI/Widgets/ProgressDialog.hpp
|
||||
src/slic3r/GUI/Widgets/StepCtrl.cpp
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="20" height="20" rx="10" fill="#00AF42" fill-opacity="0.2"/>
|
||||
<path d="M14.0828 8.14395L9.08284 13.144C9.0393 13.1877 8.98755 13.2223 8.93057 13.246C8.87359 13.2696 8.81251 13.2818 8.75081 13.2818C8.68912 13.2818 8.62803 13.2696 8.57105 13.246C8.51408 13.2223 8.46233 13.1877 8.41878 13.144L6.23128 10.9565C6.18768 10.9129 6.15309 10.8611 6.12949 10.8041C6.1059 10.7471 6.09375 10.6861 6.09375 10.6244C6.09375 10.5628 6.1059 10.5017 6.12949 10.4447C6.15309 10.3878 6.18768 10.336 6.23128 10.2924C6.27488 10.2488 6.32665 10.2142 6.38362 10.1906C6.44059 10.167 6.50165 10.1549 6.56331 10.1549C6.62498 10.1549 6.68604 10.167 6.74301 10.1906C6.79998 10.2142 6.85174 10.2488 6.89534 10.2924L8.7512 12.1482L13.4196 7.48067C13.5076 7.39261 13.6271 7.34314 13.7516 7.34314C13.8761 7.34314 13.9956 7.39261 14.0836 7.48067C14.1717 7.56873 14.2212 7.68817 14.2212 7.8127C14.2212 7.93724 14.1717 8.05667 14.0836 8.14473L14.0828 8.14395Z" fill="#00AF42" stroke="#00AF42" stroke-width="0.833333"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10,0h0c5.52,0,10,4.48,10,10h0c0,5.52-4.48,10-10,10h0C4.48,20,0,15.52,0,10h0C0,4.48,4.48,0,10,0Z" style="fill:#009688; isolation:isolate; opacity:.5;"/><polyline points="5.5 10.5 8.62 13.38 14.5 7.5" style="fill:none; stroke:#fff; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 398 B |
@@ -1,3 +1 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M28.0424 15.4264C28.6059 15.3044 28.7112 14.8962 28.2789 14.5156L16.5815 4.28496C16.1473 3.90625 15.438 3.9044 15.0038 4.28311L3.26759 14.5156C2.83346 14.8943 2.93876 15.3026 3.5022 15.4245L4.90989 15.7275C5.47333 15.8494 5.93332 16.4202 5.93332 16.9966V26.5216C5.93332 27.0979 6.4044 27.569 6.98077 27.569H11.9354C12.5117 27.569 12.9828 27.0979 12.9828 26.5216V21.9512C12.9828 21.3748 13.4539 20.9038 14.0303 20.9038H17.3721C17.9485 20.9038 18.4196 21.3748 18.4196 21.9512V26.5216C18.4196 27.0979 18.8907 27.569 19.467 27.569H24.5657C25.1421 27.569 25.6132 27.0979 25.6132 26.5216V16.9966C25.6132 16.4202 26.0732 15.8494 26.6366 15.7293L28.0424 15.4264Z" fill="#00AE42" fill-opacity="0.2" stroke="#00AE42" stroke-width="0.784708" stroke-miterlimit="10" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M4.86,13.27L14.86,5.02c.37-.3.9-.3,1.27,0l10,8.24c.23.19.36.47.36.77v12.53c0,.55-.45,1-1,1h-5c-.55,0-1-.45-1-1v-7c0-.55-.45-1-1-1h-6c-.55,0-1,.45-1,1v7c0,.55-.45,1-1,1h-5c-.55,0-1-.45-1-1v-12.53c0-.3.13-.58.36-.77Z" style="fill:#009688; isolation:isolate; opacity:.5;"/><path d="M4.86,13.27L14.86,5.02c.37-.3.9-.3,1.27,0l10,8.24c.23.19.36.47.36.77v12.53c0,.55-.45,1-1,1h-5c-.55,0-1-.45-1-1v-7c0-.55-.45-1-1-1h-6c-.55,0-1,.45-1,1v7c0,.55-.45,1-1,1h-5c-.55,0-1-.45-1-1v-12.53c0-.3.13-.58.36-.77Z" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 696 B |
@@ -1,4 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.2" d="M5.98183 18.0178C1.49214 10.5356 7.48183 3.05343 20.1981 3.80156C20.9462 16.5216 13.464 22.5075 5.98183 18.0178Z" fill="#00AF42"/>
|
||||
<path d="M20.9489 3.75655C20.9381 3.57307 20.8604 3.39991 20.7305 3.26994C20.6005 3.13998 20.4273 3.06226 20.2439 3.05155C13.1067 2.63249 7.38979 4.78124 4.95229 8.81249C4.10738 10.1915 3.69103 11.7903 3.75604 13.4062C3.80948 14.8987 4.24448 16.4062 5.04885 17.8922L3.21979 19.7203C3.07906 19.861 3 20.0519 3 20.2509C3 20.4499 3.07906 20.6408 3.21979 20.7815C3.36052 20.9223 3.55139 21.0013 3.75042 21.0013C3.94944 21.0013 4.14031 20.9223 4.28104 20.7815L6.10917 18.9525C7.59417 19.7559 9.1026 20.1909 10.5942 20.2444C10.6985 20.2481 10.8026 20.25 10.9064 20.25C12.4174 20.254 13.8997 19.8379 15.1879 19.0481C19.2192 16.6106 21.3689 10.8947 20.9489 3.75655ZM14.4145 17.7656C12.2817 19.0575 9.75698 19.0781 7.22292 17.8378L15.532 9.52967C15.6017 9.45999 15.6569 9.37727 15.6947 9.28622C15.7324 9.19518 15.7518 9.0976 15.7518 8.99905C15.7518 8.9005 15.7324 8.80292 15.6947 8.71188C15.6569 8.62083 15.6017 8.53811 15.532 8.46842C15.4623 8.39874 15.3796 8.34347 15.2885 8.30575C15.1975 8.26804 15.0999 8.24863 15.0014 8.24863C14.9028 8.24863 14.8052 8.26804 14.7142 8.30575C14.6231 8.34347 14.5404 8.39874 14.4707 8.46842L6.1626 16.7812C4.92604 14.25 4.94385 11.7187 6.23479 9.58967C8.30573 6.17061 13.2285 4.29842 19.4826 4.52155C19.7067 10.7709 17.8335 15.6947 14.4145 17.7656Z" fill="#00AF42"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M3.39,15.61C-2,6.97,4.47.5,18.5.5c0,14.03-6.47,20.5-15.11,15.11Z" style="fill:#009688; isolation:isolate; opacity:.5;"/><path d="M3.39,15.61C-2,6.97,4.47.5,18.5.5c0,14.03-6.47,20.5-15.11,15.11Z" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round; stroke-width:1.08px;"/><line x1=".5" y1="18.5" x2="15.5" y2="3.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="5.5" y1="13.5" x2="10.5" y2="14.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="4.5" y1="8.5" x2="5.5" y2="13.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="7.5" y1="5.5" x2="8.5" y2="10.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="11.5" y1="3.5" x2="12.5" y2="6.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="8.5" y1="10.5" x2="13.5" y2="11.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><line x1="12.5" y1="6.5" x2="15.5" y2="7.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -2,8 +2,8 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 9.51613C0 9.78336 0.237026 10 0.529412 10C3.55073 10 6 7.76142 6 5C6 2.23858 3.55073 0 0.529412 0C0.237026 0 0 0.216637 0 0.483871C0 0.751105 0.237026 0.967742 0.529412 0.967742C2.96596 0.967742 4.94118 2.77305 4.94118 5C4.94118 7.22695 2.96596 9.03226 0.529412 9.03226C0.237026 9.03226 0 9.24889 0 9.51613Z" fill="url(#paint0_linear_46765_16184)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_46765_16184" x1="0.622561" y1="9.52006" x2="5.48816e-08" y2="0.52972" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00AE42"/>
|
||||
<stop offset="1" stop-color="#00AE42" stop-opacity="0"/>
|
||||
<stop stop-color="#009688"/>
|
||||
<stop offset="1" stop-color="#009688" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 758 B After Width: | Height: | Size: 758 B |
@@ -2,8 +2,8 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 9.51613C0 9.78336 0.237026 10 0.529412 10C3.55073 10 6 7.76142 6 5C6 2.23858 3.55073 0 0.529412 0C0.237026 0 0 0.216637 0 0.483871C0 0.751105 0.237026 0.967742 0.529412 0.967742C2.96596 0.967742 4.94118 2.77305 4.94118 5C4.94118 7.22695 2.96596 9.03226 0.529412 9.03226C0.237026 9.03226 0 9.24889 0 9.51613Z" fill="url(#paint0_linear_46765_16184)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_46765_16184" x1="0.622561" y1="9.52006" x2="0" y2="0.52972" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00AE42"/>
|
||||
<stop offset="1" stop-color="#00AE42" stop-opacity="0"/>
|
||||
<stop stop-color="#009688"/>
|
||||
<stop offset="1" stop-color="#009688" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 748 B After Width: | Height: | Size: 748 B |
@@ -2,8 +2,8 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.483871 0C0.216637 0 0 0.237026 0 0.529412C0 3.55073 2.23858 6 5 6C7.76142 6 10 3.55073 10 0.529412C10 0.237026 9.78336 0 9.51613 0C9.24889 0 9.03226 0.237026 9.03226 0.529412C9.03226 2.96596 7.22695 4.94118 5 4.94118C2.77305 4.94118 0.967742 2.96596 0.967742 0.529412C0.967742 0.237026 0.751105 0 0.483871 0Z" transform="translate(-2, 2)" fill="url(#paint0_linear_46765_16184)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_46765_16184" x1="0.52972" y1="0.622561" x2="9.52006" y2="0.622561" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00AE42"/>
|
||||
<stop offset="1" stop-color="#00AE42" stop-opacity="0"/>
|
||||
<stop stop-color="#009688"/>
|
||||
<stop offset="1" stop-color="#009688" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 785 B After Width: | Height: | Size: 785 B |
@@ -2,8 +2,8 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 0.483871C6 0.216637 5.76297 0 5.47059 0C2.44927 0 0 2.23858 0 5C0 7.76142 2.44927 10 5.47059 10C5.76297 10 6 9.78336 6 9.51613C6 9.24889 5.76297 9.03226 5.47059 9.03226C3.03404 9.03226 1.05882 7.22695 1.05882 5C1.05882 2.77305 3.03404 0.967742 5.47059 0.967742C5.76297 0.967742 6 0.751105 6 0.483871Z" fill="url(#paint0_linear_46765_16184)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_46765_16184" x1="5.37744" y1="0.47994" x2="6" y2="9.47028" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00AE42"/>
|
||||
<stop offset="1" stop-color="#00AE42" stop-opacity="0"/>
|
||||
<stop stop-color="#009688"/>
|
||||
<stop offset="1" stop-color="#009688" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
@@ -2,8 +2,8 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.51613 6C9.78336 6 10 5.76297 10 5.47059C10 2.44927 7.76142 0 5 0C2.23858 0 0 2.44927 0 5.47059C0 5.76297 0.216637 6 0.483871 6C0.751105 6 0.967742 5.76297 0.967742 5.47059C0.967742 3.03404 2.77305 1.05882 5 1.05882C7.22695 1.05882 9.03226 3.03404 9.03226 5.47059C9.03226 5.76297 9.24889 6 9.51613 6Z" transform="translate(-4, -2)" fill="url(#paint0_linear_46765_16184)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_46765_16184" x1="9.47028" y1="5.37744" x2="0.47994" y2="5.37744" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00AE42"/>
|
||||
<stop offset="1" stop-color="#00AE42" stop-opacity="0"/>
|
||||
<stop stop-color="#009688"/>
|
||||
<stop offset="1" stop-color="#009688" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 775 B After Width: | Height: | Size: 775 B |
@@ -1,4 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.2" d="M20.25 5.25V10.5C20.25 19.5 12 21.75 12 21.75C12 21.75 3.75 19.5 3.75 10.5V5.25C3.75 5.05109 3.82902 4.86032 3.96967 4.71967C4.11032 4.57902 4.30109 4.5 4.5 4.5H19.5C19.6989 4.5 19.8897 4.57902 20.0303 4.71967C20.171 4.86032 20.25 5.05109 20.25 5.25Z" fill="#00AF42"/>
|
||||
<path d="M19.5 3.75H4.5C4.10218 3.75 3.72064 3.90804 3.43934 4.18934C3.15804 4.47064 3 4.85218 3 5.25V10.5C3 15.4425 5.3925 18.4378 7.39969 20.0803C9.56156 21.8484 11.7122 22.4494 11.8059 22.4738C11.9348 22.5088 12.0708 22.5088 12.1997 22.4738C12.2934 22.4494 14.4413 21.8484 16.6059 20.0803C18.6075 18.4378 21 15.4425 21 10.5V5.25C21 4.85218 20.842 4.47064 20.5607 4.18934C20.2794 3.90804 19.8978 3.75 19.5 3.75ZM19.5 10.5C19.5 13.9753 18.2194 16.7962 15.6937 18.8831C14.5943 19.7885 13.344 20.493 12 20.9644C10.6736 20.5012 9.4387 19.8092 8.35125 18.9197C5.79563 16.8291 4.5 13.9969 4.5 10.5V5.25H19.5V10.5Z" fill="#00AF42"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.5.5c-.55,0-1,.45-1,1v5c0,9,8.5,12,8.5,12,0,0,8.5-3,8.5-12V1.5c0-.55-.45-1-1-1H2.5Z" style="fill:#009688; isolation:isolate; opacity:.5;"/><path d="M2.5.5c-.55,0-1,.45-1,1v5c0,9,8.5,12,8.5,12,0,0,8.5-3,8.5-12V1.5c0-.55-.45-1-1-1H2.5Z" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
Before Width: | Height: | Size: 1023 B After Width: | Height: | Size: 438 B |
@@ -416,8 +416,8 @@ function favDigitFromEvent(e) {
|
||||
|
||||
function resultCountText(total, shown, query) {
|
||||
return (query || "").trim() ?
|
||||
T("sd_result_count", "Showing %s actions", shown) :
|
||||
T("sd_result_count_all", "%s actions", total);
|
||||
T("sd_result_count", "Matching actions: %s", shown) :
|
||||
T("sd_result_count_all", "Actions: %s", total);
|
||||
}
|
||||
|
||||
// Display label for a notebook tab. Trim any stray whitespace; pages added with an empty title
|
||||
@@ -758,7 +758,7 @@ window.HandleStudio = function (payload) {
|
||||
var fid = payload.id;
|
||||
if (fid && FAVS.indexOf(fid) !== -1) FAVS.splice(FAVS.indexOf(fid), 1);
|
||||
render({ resize: true, keepScroll: true });
|
||||
flashHint(T("sd_favs_full", "Favourites are full (%s max)", (payload.limit || K_FAV_LIMIT)));
|
||||
flashHint(T("sd_favs_full", "Favorites are full (%s max)", (payload.limit || K_FAV_LIMIT)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -821,8 +821,8 @@ function pinSvg(on) {
|
||||
function setPinState(pin, on) {
|
||||
pin.classList.toggle("on", on);
|
||||
pin.innerHTML = pinSvg(on);
|
||||
pin.title = on ? T("sd_unpin_fav", "Unpin from favourites (%s)", shortcutCtrl() + "B") :
|
||||
T("sd_pin_fav", "Pin to favourites (%s)", shortcutCtrl() + "B");
|
||||
pin.title = on ? T("sd_unpin_fav", "Unpin from favorites (%s)", shortcutCtrl() + "B") :
|
||||
T("sd_pin_fav", "Pin to favorites (%s)", shortcutCtrl() + "B");
|
||||
}
|
||||
|
||||
// ---- render ------------------------------------------------------------------
|
||||
@@ -858,15 +858,15 @@ function renderFav() {
|
||||
badge.textContent = slot;
|
||||
// Slot "0" is the 10th favourite (Alt/Option+0).
|
||||
var slot_num = slot === "0" ? "10" : slot;
|
||||
badge.title = T("sd_fav_slot", "Favourite %s (%s)", slot_num, shortcutAlt() + slot);
|
||||
badge.title = T("sd_fav_slot", "Favorite %s (%s)", slot_num, shortcutAlt() + slot);
|
||||
tile.appendChild(badge);
|
||||
}
|
||||
// Direct removal: a hover-revealed ✕ in the tile's corner. click() stops propagation so it
|
||||
// unpins without activating the action.
|
||||
var unpin = document.createElement("button");
|
||||
unpin.className = "fav-unpin";
|
||||
unpin.title = T("sd_remove_fav", "Remove from favourites");
|
||||
unpin.setAttribute("aria-label", T("sd_remove_fav", "Remove from favourites"));
|
||||
unpin.title = T("sd_remove_fav", "Remove from favorites");
|
||||
unpin.setAttribute("aria-label", T("sd_remove_fav", "Remove from favorites"));
|
||||
unpin.innerHTML = '<svg width="9" height="9" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>';
|
||||
unpin.onclick = function (ev) { ev.stopPropagation(); toggleFav(id); };
|
||||
tile.appendChild(unpin);
|
||||
@@ -1170,7 +1170,7 @@ function renderTabList() {
|
||||
listEl.className = "dial-list";
|
||||
if (countEl) {
|
||||
countEl.hidden = false;
|
||||
countEl.textContent = q ? T("sd_tab_match_count", "%s matches", list.length) : T("sd_tab_count", "%s tabs", list.length);
|
||||
countEl.textContent = q ? T("sd_tab_match_count", "Matching tabs: %s", list.length) : T("sd_tab_count", "Tabs: %s", list.length);
|
||||
}
|
||||
list.forEach(function (t, i) { listEl.appendChild(renderTabRow(t, i)); });
|
||||
}
|
||||
|
||||
@@ -78,8 +78,9 @@ def load_table():
|
||||
if not line.startswith('{"'):
|
||||
continue
|
||||
# id, name, row, key, action, refusal, accepts, need_bodies, need_sketches, need_sheet,
|
||||
# sketch_mode, family, ... — split on top-level commas, respecting quotes.
|
||||
f, cur, q, esc = [], "", False, False
|
||||
# sketch_mode, family, ... — split on top-level commas, respecting quotes and the
|
||||
# L(...) / L_CONTEXT(...) translation markers.
|
||||
f, cur, q, esc, depth = [], "", False, False, 0
|
||||
for ch in line[1:]:
|
||||
if esc:
|
||||
cur += ch; esc = False; continue
|
||||
@@ -87,16 +88,18 @@ def load_table():
|
||||
cur += ch; esc = True; continue
|
||||
if ch == '"':
|
||||
q = not q
|
||||
if ch == "," and not q:
|
||||
if not q:
|
||||
depth += (ch == "(") - (ch == ")")
|
||||
if ch == "," and not q and depth == 0:
|
||||
f.append(cur.strip()); cur = ""; continue
|
||||
if ch == "}" and not q:
|
||||
if ch == "}" and not q and depth == 0:
|
||||
break
|
||||
cur += ch
|
||||
f.append(cur.strip())
|
||||
if len(f) < 12:
|
||||
continue
|
||||
# User-facing fields are wrapped in the L("...") gettext marker; strip it.
|
||||
lit = lambda s: None if s == "nullptr" else re.sub(r'^L\((.*)\)$', r'\1', s).strip('"')
|
||||
# User-facing fields are wrapped in L("...") or L_CONTEXT("...", ...); take the literal.
|
||||
lit = lambda s: None if s == "nullptr" else re.search(r'"((?:[^"\\]|\\.)*)"', s).group(1)
|
||||
out.append({"id": lit(f[0]), "name": lit(f[1]), "row": int(f[2]), "key": lit(f[3]),
|
||||
"action": lit(f[4]), "accepts": int(f[6].rstrip("u"), 0),
|
||||
"need_bodies": int(f[7]), "need_sketches": int(f[8]),
|
||||
|
||||
@@ -43,6 +43,11 @@ def tstr(s):
|
||||
return "nullptr" if s is None else "L(" + cstr(s) + ")"
|
||||
|
||||
|
||||
def tstr_design(s):
|
||||
# Verb name: same msgctxt as the Design panel's own tool labels.
|
||||
return "L_CONTEXT(" + cstr(s) + ', "Design")'
|
||||
|
||||
|
||||
def validate(A):
|
||||
"""Refuse an atlas the header cannot represent, naming every fault at once.
|
||||
|
||||
@@ -90,6 +95,9 @@ def main():
|
||||
"#ifndef L",
|
||||
"#define L(s) s // gettext marker, as in slic3r/GUI/I18N.hpp",
|
||||
"#endif",
|
||||
"#ifndef L_CONTEXT",
|
||||
"#define L_CONTEXT(s, context) s",
|
||||
"#endif",
|
||||
"",
|
||||
"namespace Slic3r { namespace GUI {",
|
||||
"",
|
||||
@@ -113,7 +121,7 @@ def main():
|
||||
"// nullptr -> kernel support exists, no GUI path yet (row shows disabled)",
|
||||
"struct OfferVerb {",
|
||||
" const char* id;",
|
||||
" const char* name; // drawing-office word (L10); marked L(); translated at use",
|
||||
" const char* name; // drawing-office word (L10); msgctxt \"Design\", translated at use",
|
||||
" int row; // index into kOfferRowNames, the ratified address — NEVER reorder",
|
||||
" const char* key; // shortcut shown in the row, or nullptr",
|
||||
" const char* action;",
|
||||
@@ -170,7 +178,7 @@ def main():
|
||||
n = v.get("needs") or {}
|
||||
lines.append(
|
||||
" {%s, %s, %d, %s, %s, %s, 0x%08xu, %d, %d, %s, %s, %s, %s, %s}," % (
|
||||
cstr(v["id"]), tstr(v["name"]), slots.index(v["slot"]),
|
||||
cstr(v["id"]), tstr_design(v["name"]), slots.index(v["slot"]),
|
||||
cstr(v.get("key")), cstr(v.get("action")), tstr(v.get("refusal")),
|
||||
mask, n.get("bodies", 0), n.get("sketches", 0),
|
||||
"true" if n.get("sheet") else "false",
|
||||
|
||||
@@ -36,6 +36,9 @@ EXCLUDED_DIRS = ("src/glad/", "tests/catch2/")
|
||||
# Per file. A deleted include can leave hundreds of follow-on errors.
|
||||
MAX_REPORTED = 30
|
||||
|
||||
# Subprocess output is UTF-8 whatever the locale, which is cp1252 on Windows.
|
||||
UTF8 = {"encoding": "utf-8", "errors": "replace"}
|
||||
|
||||
HUNK_RE = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@")
|
||||
DIAGNOSTIC_RE = re.compile(r"^(.+?):(\d+):(\d+): (error|warning): (.*)$")
|
||||
FIX_MESSAGE_RE = re.compile(r"^\s+Message:\s+(['\"])(.*)\1$")
|
||||
@@ -72,7 +75,8 @@ def parse_diff(diff):
|
||||
change = None
|
||||
for line in diff.splitlines():
|
||||
if line.startswith("+++ "):
|
||||
target = line[4:]
|
||||
# git appends a tab to the header of a path that contains a space.
|
||||
target = line[4:].removesuffix("\t")
|
||||
change = changes.setdefault(target[2:], FileChange()) if target.startswith("b/") else None
|
||||
continue
|
||||
if change is None:
|
||||
@@ -98,8 +102,10 @@ def is_checked(path):
|
||||
|
||||
def changed_files(merge_base):
|
||||
# Against the working tree, so a local run covers uncommitted edits too.
|
||||
diff = subprocess.run(["git", "diff", "-U0", "--no-color", "--no-ext-diff", "--diff-filter=AMR", merge_base],
|
||||
check=True, capture_output=True, text=True).stdout
|
||||
# core.quotePath=false keeps a non-ASCII path unquoted, so parse_diff sees its b/ prefix.
|
||||
diff = subprocess.run(["git", "-c", "core.quotePath=false", "diff", "-U0", "--no-color", "--no-ext-diff",
|
||||
"--diff-filter=AMR", merge_base],
|
||||
check=True, capture_output=True, **UTF8).stdout
|
||||
return {path: change for path, change in parse_diff(diff).items() if is_checked(path)}
|
||||
|
||||
|
||||
@@ -117,8 +123,9 @@ def run_clang_tidy(clang_tidy, build_dir, path, lines, extra_args):
|
||||
cmd = [clang_tidy, "-p", build_dir, "--quiet", "--export-fixes=" + fixes,
|
||||
"--extra-arg=-Wno-unknown-warning-option", "--extra-arg=-ferror-limit=0", *extra_args, path]
|
||||
if lines is not None:
|
||||
cmd.insert(1, "--line-filter=" + json.dumps([{"name": path, "lines": lines}]))
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
# clang-tidy matches the name against the end of the file's native path.
|
||||
cmd.insert(1, "--line-filter=" + json.dumps([{"name": os.path.normpath(path), "lines": lines}]))
|
||||
result = subprocess.run(cmd, capture_output=True, **UTF8)
|
||||
suggestions = parse_suggested_includes(fixes)
|
||||
output = result.stdout + result.stderr
|
||||
return result.returncode, output, parse_diagnostics(output, suggestions)
|
||||
@@ -178,7 +185,7 @@ def error_sites(errors, text):
|
||||
|
||||
def errors_alone_at(revision, clang_tidy, build_dir, path):
|
||||
"""The error sites a header had when compiled on its own at `revision`."""
|
||||
shown = subprocess.run(["git", "show", f"{revision}:{path}"], capture_output=True, text=True)
|
||||
shown = subprocess.run(["git", "show", f"{revision}:{path}"], capture_output=True, **UTF8)
|
||||
if shown.returncode != 0:
|
||||
return Counter()
|
||||
# Beside the original, so its quoted includes resolve the same way.
|
||||
@@ -234,6 +241,8 @@ def main():
|
||||
parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count())
|
||||
parser.add_argument("extra_args", nargs="*", help="passed to clang-tidy after --, e.g. -- --fix")
|
||||
args = parser.parse_args()
|
||||
# A piped stdout on Windows is cp1252, which cannot encode every character clang-tidy prints.
|
||||
sys.stdout.reconfigure(errors="replace")
|
||||
|
||||
merge_base = subprocess.run(["git", "merge-base", args.base, "HEAD"], check=True,
|
||||
capture_output=True, text=True).stdout.strip()
|
||||
|
||||
@@ -54,6 +54,21 @@ function Has([string]$Command) {
|
||||
return [bool](Get-Command $Command -ErrorAction SilentlyContinue)
|
||||
}
|
||||
|
||||
# Runs a native command with its output, stderr included, streamed to $Log or dropped.
|
||||
function Invoke-Quiet([scriptblock]$Command, [string]$Log) {
|
||||
# Under "Stop", 2>&1 turns every stderr line of a native command into a terminating error.
|
||||
$ErrorActionPreference = "Continue"
|
||||
$lines = {
|
||||
& $Command 2>&1 | ForEach-Object {
|
||||
# "$_" turns a blank stderr line into the text System.Management.Automation.RemoteException.
|
||||
if ($_ -isnot [System.Management.Automation.ErrorRecord]) { $_ }
|
||||
elseif ($null -ne $_.TargetObject) { $_.TargetObject }
|
||||
else { $_.Exception.Message }
|
||||
}
|
||||
}
|
||||
if ($Log) { & $lines | Out-File -Encoding utf8 -LiteralPath $Log } else { & $lines | Out-Null }
|
||||
}
|
||||
|
||||
function Request-Install([string]$What, [string]$Command) {
|
||||
Write-Host "Missing: $What"
|
||||
if (Ask "Install it now with: $Command ?") {
|
||||
@@ -78,11 +93,11 @@ if (-not (Has "git")) { Request-Install "Git" "build_win.bat --install-deps" }
|
||||
# Python: the py launcher, else a python.exe that is not the Microsoft Store stub.
|
||||
$Python = $null
|
||||
if (Has "py") {
|
||||
& py -3 --version *> $null
|
||||
Invoke-Quiet { & py -3 --version }
|
||||
if ($LASTEXITCODE -eq 0) { $Python = @("py", "-3") }
|
||||
}
|
||||
if (-not $Python -and (Has "python")) {
|
||||
& python --version *> $null
|
||||
Invoke-Quiet { & python --version }
|
||||
if ($LASTEXITCODE -eq 0) { $Python = @("python") }
|
||||
}
|
||||
if (-not $Python) { Request-Install "Python 3" "winget install -e --id Python.Python.3.12" }
|
||||
@@ -100,7 +115,12 @@ if (-not $VsPath) { Request-Install "Visual Studio with the C++ tools" "build_wi
|
||||
# Load the developer environment, as build_win.bat does.
|
||||
$HostArch = if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "arm64" } else { "x64" }
|
||||
$VsDevCmd = Join-Path $VsPath "Common7\Tools\VsDevCmd.bat"
|
||||
$envLines = & cmd /c "`"$VsDevCmd`" -arch=$Arch -host_arch=$HostArch -no_logo >nul && set"
|
||||
# Ignores VsDevCmd's exit code, as build_win.bat does, and checks the variables it sets
|
||||
# before applying any, cleared in cmd so values inherited from a developer shell do not count.
|
||||
$envLines = & cmd /c "set VCToolsInstallDir=& set WindowsSdkDir=& `"$VsDevCmd`" -arch=$Arch -host_arch=$HostArch -no_logo >nul 2>nul & set"
|
||||
if (-not ($envLines -match '^VCToolsInstallDir=.') -or -not ($envLines -match '^WindowsSdkDir=.')) {
|
||||
throw "Loading the Visual Studio $Arch environment failed. Run `"$VsDevCmd`" -arch=$Arch -host_arch=$HostArch in cmd to see why."
|
||||
}
|
||||
foreach ($line in $envLines) {
|
||||
$i = $line.IndexOf("=")
|
||||
if ($i -gt 0) { Set-Item -Path ("env:" + $line.Substring(0, $i)) -Value $line.Substring($i + 1) }
|
||||
@@ -190,13 +210,17 @@ $cmakeArgs = @("-S", ".", "-B", $BuildDir, "-G", "Ninja", "-DCMAKE_BUILD_TYPE=Re
|
||||
Write-Host "Configuring $BuildDir with $Compiler"
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
$Log = Join-Path $BuildDir "configure.log"
|
||||
& cmake @cmakeArgs *> $Log
|
||||
Invoke-Quiet { & cmake @cmakeArgs } $Log
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Get-Content $Log -Tail 20
|
||||
Get-Content -LiteralPath $Log -Tail 20
|
||||
throw "Configuring failed; the full log is in $Log."
|
||||
}
|
||||
& cmake --build $BuildDir --target git_commit_hash_header *> $null
|
||||
if ($LASTEXITCODE -ne 0) { throw "Generating git_commit_hash.h failed." }
|
||||
$HashLog = Join-Path $BuildDir "git_commit_hash.log"
|
||||
Invoke-Quiet { & cmake --build $BuildDir --target git_commit_hash_header } $HashLog
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Get-Content -LiteralPath $HashLog -Tail 20
|
||||
throw "Generating git_commit_hash.h failed; the full log is in $HashLog."
|
||||
}
|
||||
|
||||
# --- Base revision ------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@ external deps).
|
||||
Run from the repo root: python -m unittest discover -s scripts/tests -v
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
@@ -62,6 +65,10 @@ class TestParseChangedLines(unittest.TestCase):
|
||||
def test_pure_rename_has_no_changed_lines(self):
|
||||
self.assertNotIn("src/libslic3r/Renamed.cpp", self.changed)
|
||||
|
||||
def test_path_with_a_space_drops_the_tab_git_appends(self):
|
||||
changed = clang_tidy_diff.parse_diff("+++ b/src/libslic3r/Foo Bar.cpp\t\n@@ -1,0 +2 @@\n+int x;\n")
|
||||
self.assertEqual(changed["src/libslic3r/Foo Bar.cpp"].lines, [[2, 2]])
|
||||
|
||||
|
||||
class TestNamesRemovedInclude(unittest.TestCase):
|
||||
def test_matches_however_the_include_was_spelled(self):
|
||||
@@ -128,5 +135,37 @@ class TestParseSuggestedIncludes(unittest.TestCase):
|
||||
self.assertEqual(clang_tidy_diff.parse_suggested_includes("/nonexistent/fixes.yaml"), {})
|
||||
|
||||
|
||||
class TestSubprocessCalls(unittest.TestCase):
|
||||
def run_patched(self, function, *args, returncode=0, stdout=""):
|
||||
done = subprocess.CompletedProcess([], returncode, stdout, "")
|
||||
with mock.patch.object(clang_tidy_diff.subprocess, "run", return_value=done) as run, \
|
||||
mock.patch.object(clang_tidy_diff.os.path, "normpath", wraps=os.path.normpath) as normpath:
|
||||
result = function(*args)
|
||||
return result, run.call_args, normpath
|
||||
|
||||
def test_line_filter_names_the_file_with_native_separators(self):
|
||||
_, call, normpath = self.run_patched(clang_tidy_diff.run_clang_tidy, "clang-tidy", "build",
|
||||
"src/libslic3r/Color.cpp", [[4, 4]], [])
|
||||
line_filter = next(arg for arg in call.args[0] if arg.startswith("--line-filter="))
|
||||
self.assertEqual(json.loads(line_filter.split("=", 1)[1]),
|
||||
[{"name": os.path.join("src", "libslic3r", "Color.cpp"), "lines": [[4, 4]]}])
|
||||
normpath.assert_any_call("src/libslic3r/Color.cpp")
|
||||
|
||||
def test_changed_files_reads_non_ascii_paths_and_text_as_utf8(self):
|
||||
diff = "+++ b/src/libslic3r/Über.cpp\n@@ -1,0 +2 @@\n+// 打印\n"
|
||||
files, call, _ = self.run_patched(clang_tidy_diff.changed_files, "base", stdout=diff)
|
||||
self.assertIn("core.quotePath=false", call.args[0])
|
||||
self.assertEqual(call.kwargs["encoding"], "utf-8")
|
||||
self.assertEqual(files["src/libslic3r/Über.cpp"].lines, [[2, 2]])
|
||||
|
||||
def test_clang_tidy_and_git_show_output_is_decoded_as_utf8(self):
|
||||
_, call, _ = self.run_patched(clang_tidy_diff.run_clang_tidy, "clang-tidy", "build",
|
||||
"src/libslic3r/Color.cpp", None, [])
|
||||
self.assertEqual(call.kwargs["encoding"], "utf-8")
|
||||
_, call, _ = self.run_patched(clang_tidy_diff.errors_alone_at, "base", "clang-tidy", "build",
|
||||
"src/libslic3r/Color.hpp", returncode=128)
|
||||
self.assertEqual(call.kwargs["encoding"], "utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -153,6 +153,8 @@ using namespace nlohmann;
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/Jobs/SendJob.hpp"
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ extern "C"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <boost/algorithm/string/constants.hpp>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
class OpenGLVersionCheck
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
|
||||
#include "git_commit_hash.h"
|
||||
#include "libslic3r_version.h"
|
||||
#include "StackWalker.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <ctime>
|
||||
#include <excpt.h>
|
||||
|
||||
static std::string g_log_folder;
|
||||
static std::atomic<int> g_crash_log_count = 0;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include "StackWalker.h"
|
||||
#include <eh.h>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
class CBaseException : public CStackWalker
|
||||
{
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <strsafe.h>
|
||||
//#include <atlconv.h>
|
||||
#include <dbghelp.h>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#pragma comment(lib, "version.lib")
|
||||
#pragma comment( lib, "dbghelp.lib" )
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
namespace textconv_helper
|
||||
{
|
||||
|
||||
@@ -126,6 +126,13 @@
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>OrcaSlicer uses your local network to discover and connect to printers.</string>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_octoprint._tcp</string>
|
||||
<string>_http._tcp</string>
|
||||
</array>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef WIN32
|
||||
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include <cstdio>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <psapi.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "libslic3r/CAD/GeometryEngine.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
@@ -73,19 +74,19 @@ std::vector<TopoDS_Shape> GeometryEngine::read_step_solids(const std::string& pa
|
||||
try {
|
||||
STEPControl_Reader reader;
|
||||
if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
|
||||
err = "cannot read STEP file";
|
||||
err = _u8L("cannot read STEP file");
|
||||
return out;
|
||||
}
|
||||
reader.TransferRoots();
|
||||
const TopoDS_Shape shape = reader.OneShape();
|
||||
if (shape.IsNull()) { err = "STEP file has no geometry"; return out; }
|
||||
if (shape.IsNull()) { err = _u8L("STEP file has no geometry"); return out; }
|
||||
// One body per top-level solid; fall back to the whole shape (shells/faces) if none.
|
||||
for (TopExp_Explorer ex(shape, TopAbs_SOLID); ex.More(); ex.Next())
|
||||
out.push_back(ex.Current());
|
||||
if (out.empty())
|
||||
out.push_back(shape);
|
||||
} catch (const Standard_Failure& e) {
|
||||
err = *e.what() ? e.what() : "OCCT failed to read STEP";
|
||||
err = *e.what() ? e.what() : _u8L("OCCT failed to read STEP");
|
||||
out.clear();
|
||||
}
|
||||
return out;
|
||||
@@ -115,7 +116,7 @@ TopoDS_Shape GeometryEngine::mesh_to_brep(const indexed_triangle_set& its,
|
||||
if (tolerance <= 0.0)
|
||||
throw std::runtime_error("mesh_to_brep: tolerance must be > 0");
|
||||
if (its.indices.empty())
|
||||
throw std::runtime_error("mesh_to_brep: mesh has no triangles");
|
||||
throw std::runtime_error(_u8L("mesh_to_brep: mesh has no triangles"));
|
||||
|
||||
// 1. Tolerance-quantized vertex dedup. A merged vertex keeps the exact coordinates of the
|
||||
// first input occurrence — vertices are grouped by a cell, never snapped onto its grid.
|
||||
@@ -150,8 +151,8 @@ TopoDS_Shape GeometryEngine::mesh_to_brep(const indexed_triangle_set& its,
|
||||
}
|
||||
stats.kept_tris = int(tris.size());
|
||||
if (tris.empty())
|
||||
throw std::runtime_error("mesh_to_brep: every triangle was rejected as degenerate "
|
||||
"(try a smaller tolerance)");
|
||||
throw std::runtime_error(_u8L("mesh_to_brep: every triangle was rejected as degenerate "
|
||||
"(try a smaller tolerance)"));
|
||||
|
||||
// 3. One face per triangle, sharing vertices and edges through the caches.
|
||||
std::vector<TopoDS_Vertex> vertex_cache(verts.size());
|
||||
@@ -361,7 +362,7 @@ TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radi
|
||||
// A too-large radius (e.g. >= half the smallest spanned dimension) makes the
|
||||
// operation degenerate; OCCT leaves IsDone() false. Report it instead of
|
||||
// silently returning the unfilleted solid (which reads as a false success).
|
||||
if (!fillet.IsDone()) throw std::runtime_error("fillet radius too large for this geometry");
|
||||
if (!fillet.IsDone()) throw std::runtime_error(_u8L("fillet radius too large for this geometry"));
|
||||
return fillet.Shape();
|
||||
}
|
||||
|
||||
@@ -377,7 +378,7 @@ TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double dis
|
||||
chamfer.Add(distance, edge); // symmetric chamfer
|
||||
chamfer.Build();
|
||||
|
||||
if (!chamfer.IsDone()) throw std::runtime_error("chamfer distance too large for this geometry");
|
||||
if (!chamfer.IsDone()) throw std::runtime_error(_u8L("chamfer distance too large for this geometry"));
|
||||
return chamfer.Shape();
|
||||
}
|
||||
|
||||
@@ -404,7 +405,7 @@ TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radi
|
||||
}
|
||||
mk.Build();
|
||||
|
||||
if (!mk.IsDone()) throw std::runtime_error("apply_fillet: OCCT fillet failed");
|
||||
if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_fillet: OCCT fillet failed"));
|
||||
return mk.Shape();
|
||||
}
|
||||
|
||||
@@ -421,7 +422,7 @@ TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double dis
|
||||
}
|
||||
mk.Build();
|
||||
|
||||
if (!mk.IsDone()) throw std::runtime_error("apply_chamfer: OCCT chamfer failed");
|
||||
if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_chamfer: OCCT chamfer failed"));
|
||||
return mk.Shape();
|
||||
}
|
||||
|
||||
@@ -547,12 +548,13 @@ GeometryEngine::MassProps GeometryEngine::mass_properties(const TopoDS_Shape& sh
|
||||
std::string GeometryEngine::primitive_name(PrimitiveType type)
|
||||
{
|
||||
switch (type) {
|
||||
case PrimitiveType::Box: return "Box";
|
||||
case PrimitiveType::Cylinder: return "Cylinder";
|
||||
case PrimitiveType::Sphere: return "Sphere";
|
||||
case PrimitiveType::Cone: return "Cone";
|
||||
case PrimitiveType::Torus: return "Torus";
|
||||
default: return "Unknown";
|
||||
// TRN Default name of an object created from the box primitive shape.
|
||||
case PrimitiveType::Box: return _u8L("Box");
|
||||
case PrimitiveType::Cylinder: return _u8L("Cylinder");
|
||||
case PrimitiveType::Sphere: return _u8L("Sphere");
|
||||
case PrimitiveType::Cone: return _u8L("Cone");
|
||||
case PrimitiveType::Torus: return _u8L("Torus");
|
||||
default: return _u8L("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "libslic3r/CAD/SketchEngine.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
|
||||
#include <Standard_Handle.hxx>
|
||||
#include <GeomAbs_SurfaceType.hxx>
|
||||
@@ -138,7 +139,7 @@ Vec3d SketchPlane::to_world(const Vec2d& pt) const
|
||||
TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
|
||||
{
|
||||
if (points.size() < 2)
|
||||
throw std::runtime_error("Profile has fewer than 2 points");
|
||||
throw std::runtime_error(_u8L("Profile has fewer than 2 points"));
|
||||
|
||||
BRepBuilderAPI_MakeWire builder;
|
||||
for (size_t i = 0; i < points.size(); ++i) {
|
||||
@@ -150,7 +151,7 @@ TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
|
||||
}
|
||||
builder.Build();
|
||||
if (!builder.IsDone())
|
||||
throw std::runtime_error("Failed to build wire from profile");
|
||||
throw std::runtime_error(_u8L("Failed to build wire from profile"));
|
||||
return builder.Wire();
|
||||
}
|
||||
|
||||
@@ -158,9 +159,9 @@ TopoDS_Wire SketchProfile::to_occt_wire(const SketchPlane& plane) const
|
||||
|
||||
TopoDS_Shape SketchEngine::make_prism(const TopoDS_Shape& base, const gp_Vec& vec)
|
||||
{
|
||||
if (vec.Magnitude() < 1e-9) throw std::runtime_error("extrude depth is zero");
|
||||
if (vec.Magnitude() < 1e-9) throw std::runtime_error(_u8L("extrude depth is zero"));
|
||||
BRepPrimAPI_MakePrism prism(base, vec);
|
||||
if (!prism.IsDone()) throw std::runtime_error("extrude failed");
|
||||
if (!prism.IsDone()) throw std::runtime_error(_u8L("extrude failed"));
|
||||
return prism.Shape();
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ static TopoDS_Shape extrude_face_internal(const TopoDS_Face& face, const gp_Dir&
|
||||
if (symmetric) {
|
||||
gp_Vec halfVec = gp_Vec(dir) * (length / 2.0);
|
||||
BRepAlgoAPI_Fuse fuse(SketchEngine::make_prism(face, halfVec), SketchEngine::make_prism(face, -halfVec));
|
||||
if (!fuse.IsDone()) throw std::runtime_error("Fuse failed");
|
||||
if (!fuse.IsDone()) throw std::runtime_error(_u8L("Fuse failed"));
|
||||
return fuse.Shape();
|
||||
}
|
||||
return SketchEngine::make_prism(face, gp_Vec(dir) * length);
|
||||
@@ -179,7 +180,7 @@ TopoDS_Shape SketchEngine::make_extrude(const TopoDS_Wire& wire, const SketchPla
|
||||
double length, bool symmetric, double taper_deg)
|
||||
{
|
||||
BRepBuilderAPI_MakeFace fm(wire);
|
||||
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
|
||||
return make_extrude(fm.Face(), plane, length, symmetric, taper_deg);
|
||||
}
|
||||
|
||||
@@ -194,7 +195,7 @@ TopoDS_Shape SketchEngine::make_extrude_two_sided(const TopoDS_Wire& wire, const
|
||||
double up, double down)
|
||||
{
|
||||
BRepBuilderAPI_MakeFace fm(wire);
|
||||
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
|
||||
return make_extrude_two_sided(fm.Face(), plane, up, down);
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ TopoDS_Shape SketchEngine::make_extrude_two_sided(const TopoDS_Face& face, const
|
||||
if (d < 1e-9) return make_prism(face, gp_Vec(dir) * u);
|
||||
if (u < 1e-9) return make_prism(face, gp_Vec(dir) * -d);
|
||||
BRepAlgoAPI_Fuse fuse(make_prism(face, gp_Vec(dir) * u), make_prism(face, gp_Vec(dir) * -d));
|
||||
if (!fuse.IsDone()) throw std::runtime_error("two-sided extrude fuse failed");
|
||||
if (!fuse.IsDone()) throw std::runtime_error(_u8L("two-sided extrude fuse failed"));
|
||||
return fuse.Shape();
|
||||
}
|
||||
|
||||
@@ -216,7 +217,7 @@ TopoDS_Shape SketchEngine::make_extrude_taper(const TopoDS_Wire& wire, const Ske
|
||||
gp_Dir dir(plane.normal.x(), plane.normal.y(), plane.normal.z());
|
||||
auto straight = [&]() -> TopoDS_Shape {
|
||||
BRepBuilderAPI_MakeFace fm(wire);
|
||||
if (!fm.IsDone()) throw std::runtime_error("Failed to make face from wire");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("Failed to make face from wire"));
|
||||
return make_prism(fm.Face(), gp_Vec(dir) * length);
|
||||
};
|
||||
if (std::abs(taper_deg) >= 89.0 || std::abs(length) < 1e-9) return straight();
|
||||
@@ -262,7 +263,7 @@ TopoDS_Shape SketchEngine::make_extrude_regions(
|
||||
const SketchPlane& plane, double length, bool symmetric)
|
||||
{
|
||||
// The loop below skips regions that fail, so a zero depth is rejected before it.
|
||||
if (std::abs(length) < 1e-9) throw std::runtime_error("extrude depth is zero");
|
||||
if (std::abs(length) < 1e-9) throw std::runtime_error(_u8L("extrude depth is zero"));
|
||||
|
||||
// Drop consecutive coincident points and the closing duplicate. FreeType /
|
||||
// SVG flattening routinely emits repeated points which would build a
|
||||
@@ -348,7 +349,7 @@ TopoDS_Shape SketchEngine::make_extrude_regions(
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) throw std::runtime_error("imported regions produced no extrudable geometry");
|
||||
if (count == 0) throw std::runtime_error(_u8L("imported regions produced no extrudable geometry"));
|
||||
return count == 1 ? last : TopoDS_Shape(comp); // avoid a compound-of-one
|
||||
}
|
||||
|
||||
@@ -356,7 +357,7 @@ TopoDS_Shape SketchEngine::make_revolve(const TopoDS_Wire& wire, const gp_Ax1& a
|
||||
{
|
||||
BRepBuilderAPI_MakeFace faceMaker(wire);
|
||||
if (!faceMaker.IsDone())
|
||||
throw std::runtime_error("Failed to make face from wire");
|
||||
throw std::runtime_error(_u8L("Failed to make face from wire"));
|
||||
TopoDS_Face face = faceMaker.Face();
|
||||
|
||||
// A profile on both sides of the axis sweeps through itself; MakeRevol then fails with no
|
||||
@@ -387,7 +388,7 @@ TopoDS_Shape SketchEngine::make_revolve(const TopoDS_Wire& wire, const gp_Ax1& a
|
||||
if (angle_rad < 0) { axis.Reverse(); angle_rad = -angle_rad; }
|
||||
BRepPrimAPI_MakeRevol rev(face, axis, angle_rad);
|
||||
if (!rev.IsDone())
|
||||
throw std::runtime_error("Failed to revolve");
|
||||
throw std::runtime_error(_u8L("Failed to revolve"));
|
||||
if (!BRepCheck_Analyzer(rev.Shape()).IsValid())
|
||||
throw std::runtime_error("the profile crosses the revolve axis — it must lie on one side of it");
|
||||
return rev.Shape();
|
||||
@@ -397,29 +398,29 @@ TopoDS_Shape SketchEngine::make_sweep(const TopoDS_Wire& profile, const TopoDS_W
|
||||
{
|
||||
BRepBuilderAPI_MakeFace faceMaker(profile);
|
||||
if (!faceMaker.IsDone())
|
||||
throw std::runtime_error("Failed to make face from sweep profile");
|
||||
throw std::runtime_error(_u8L("Failed to make face from sweep profile"));
|
||||
TopoDS_Face face = faceMaker.Face();
|
||||
|
||||
BRepOffsetAPI_MakePipe pipe(path, face);
|
||||
pipe.Build();
|
||||
if (!pipe.IsDone())
|
||||
throw std::runtime_error("Failed to sweep profile along path");
|
||||
throw std::runtime_error(_u8L("Failed to sweep profile along path"));
|
||||
return pipe.Shape();
|
||||
}
|
||||
|
||||
TopoDS_Shape SketchEngine::make_loft(const std::vector<TopoDS_Wire>& profiles, bool ruled)
|
||||
{
|
||||
if (profiles.size() < 2)
|
||||
throw std::runtime_error("loft needs at least 2 profiles");
|
||||
throw std::runtime_error(_u8L("loft needs at least 2 profiles"));
|
||||
BRepOffsetAPI_ThruSections loft(true /*solid*/, ruled);
|
||||
for (const TopoDS_Wire& w : profiles) {
|
||||
if (w.IsNull()) throw std::runtime_error("loft: null profile wire");
|
||||
loft.AddWire(w);
|
||||
}
|
||||
loft.Build();
|
||||
if (!loft.IsDone()) throw std::runtime_error("loft failed");
|
||||
if (!loft.IsDone()) throw std::runtime_error(_u8L("loft failed"));
|
||||
TopoDS_Shape s = loft.Shape();
|
||||
if (s.IsNull()) throw std::runtime_error("loft produced no solid");
|
||||
if (s.IsNull()) throw std::runtime_error(_u8L("loft produced no solid"));
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -427,16 +428,16 @@ TopoDS_Shape SketchEngine::make_loft(const std::vector<TopoDS_Wire>& profiles, b
|
||||
TopoDS_Shape SketchEngine::make_loft_surface(const std::vector<TopoDS_Wire>& profiles, bool ruled)
|
||||
{
|
||||
if (profiles.size() < 2)
|
||||
throw std::runtime_error("loft needs at least 2 profiles");
|
||||
throw std::runtime_error(_u8L("loft needs at least 2 profiles"));
|
||||
BRepOffsetAPI_ThruSections loft(false /*shell, no end caps*/, ruled);
|
||||
for (const TopoDS_Wire& w : profiles) {
|
||||
if (w.IsNull()) throw std::runtime_error("loft: null profile wire");
|
||||
loft.AddWire(w);
|
||||
}
|
||||
loft.Build();
|
||||
if (!loft.IsDone()) throw std::runtime_error("loft failed");
|
||||
if (!loft.IsDone()) throw std::runtime_error(_u8L("loft failed"));
|
||||
TopoDS_Shape s = loft.Shape();
|
||||
if (s.IsNull()) throw std::runtime_error("loft produced no shape");
|
||||
if (s.IsNull()) throw std::runtime_error(_u8L("loft produced no shape"));
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -444,11 +445,11 @@ TopoDS_Shape SketchEngine::make_pocket(const TopoDS_Wire& wire, const SketchPlan
|
||||
const TopoDS_Shape& target, double depth)
|
||||
{
|
||||
BRepBuilderAPI_MakeFace fm(wire);
|
||||
if (!fm.IsDone()) throw std::runtime_error("Pocket face failed");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("Pocket face failed"));
|
||||
TopoDS_Shape tool = extrude_face_internal(fm.Face(),
|
||||
gp_Dir(plane.normal.x(), plane.normal.y(), plane.normal.z()), depth + 1.0, false);
|
||||
BRepAlgoAPI_Cut cut(target, tool);
|
||||
if (!cut.IsDone()) throw std::runtime_error("Pocket cut failed");
|
||||
if (!cut.IsDone()) throw std::runtime_error(_u8L("Pocket cut failed"));
|
||||
return cut.Shape();
|
||||
}
|
||||
|
||||
@@ -929,7 +930,7 @@ static TopoDS_Face checked_profile_face(const TopoDS_Face& f)
|
||||
TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
|
||||
const SketchPlane& plane)
|
||||
{
|
||||
if (wires.empty()) throw std::runtime_error("sketch has no closed loop");
|
||||
if (wires.empty()) throw std::runtime_error(_u8L("sketch has no closed loop"));
|
||||
|
||||
// The ASSEMBLED face below is built on the SKETCH's own plane rather than on a surface OCCT
|
||||
// infers from the outer wire. The inferred plane has no reason to share the sketch's normal,
|
||||
@@ -944,7 +945,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
|
||||
|
||||
if (wires.size() == 1) {
|
||||
BRepBuilderAPI_MakeFace fm(wires[0]);
|
||||
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
|
||||
return checked_profile_face(fm.Face());
|
||||
}
|
||||
|
||||
@@ -956,7 +957,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
|
||||
areas.reserve(wires.size());
|
||||
for (const TopoDS_Wire& w : wires) {
|
||||
BRepBuilderAPI_MakeFace fm(w);
|
||||
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
|
||||
faces.push_back(fm.Face());
|
||||
GProp_GProps props;
|
||||
BRepGProp::SurfaceProperties(faces.back(), props);
|
||||
@@ -982,10 +983,10 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
if (!got) throw std::runtime_error("sketch loop does not bound a face");
|
||||
if (!got) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
|
||||
BRepClass_FaceClassifier fc(faces[outer], p, 1e-7);
|
||||
if (fc.State() != TopAbs_IN)
|
||||
throw std::runtime_error("sketch has two disjoint regions; put each in its own sketch");
|
||||
throw std::runtime_error(_u8L("sketch has two disjoint regions; put each in its own sketch"));
|
||||
// Add the hole loop AS-IS and let ShapeFix_Face sort the orientations out below.
|
||||
// Reversing it here only works when the sketch happened to wind both loops the same
|
||||
// way: a circle drawn clockwise inside a counter-clockwise rectangle comes out matching
|
||||
@@ -995,7 +996,7 @@ TopoDS_Face SketchEngine::wires_to_face(const std::vector<TopoDS_Wire>& wires,
|
||||
// 147520 — a body larger than its own bounding box, which is the signature of it.
|
||||
fm.Add(wires[i]);
|
||||
}
|
||||
if (!fm.IsDone()) throw std::runtime_error("sketch loop does not bound a face");
|
||||
if (!fm.IsDone()) throw std::runtime_error(_u8L("sketch loop does not bound a face"));
|
||||
// Winding-independent classification of outer vs holes — the same idiom make_extrude_regions
|
||||
// already uses for imported glyphs, which is why holed TEXT extruded correctly all along
|
||||
// while a holed SKETCH did not.
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "libslic3r/AABBTreeLines.hpp" // search structure for found close points
|
||||
#include "libslic3r/Line.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include <sstream>
|
||||
|
||||
// Experimentaly suggested ration of font ascent by multiple fonts
|
||||
// to get approx center of normal text line
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "FuzzySkin.hpp"
|
||||
|
||||
#include "libnoise/noise.h"
|
||||
#include <functional>
|
||||
|
||||
// #define DEBUG_FUZZY
|
||||
|
||||
|
||||
@@ -301,6 +301,9 @@ struct SurfaceFillParams
|
||||
float skin_infill_depth = 0;
|
||||
bool symmetric_infill_y_axis = false;
|
||||
|
||||
// Top fill for 3D honeycomb
|
||||
bool infill_complete_top = false;
|
||||
|
||||
// Params for Lateral honeycomb
|
||||
float infill_overhang_angle = 60.f;
|
||||
|
||||
@@ -344,6 +347,7 @@ struct SurfaceFillParams
|
||||
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_1);
|
||||
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_2);
|
||||
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_complete_top);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
|
||||
RETURN_COMPARE_NON_EQUAL(skin_infill_depth);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
|
||||
@@ -931,6 +935,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
|
||||
} else if (params.pattern == ipZigZag) {
|
||||
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
|
||||
} else if (params.pattern == ip3DHoneycomb) {
|
||||
params.infill_complete_top = region_config.infill_complete_top;
|
||||
}
|
||||
|
||||
if (surface.is_solid()) {
|
||||
@@ -1430,6 +1436,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
} else if (surface_fill.params.pattern == ipZigZag) {
|
||||
params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
|
||||
|
||||
} else if (surface_fill.params.pattern == ip3DHoneycomb) {
|
||||
params.infill_complete_top = surface_fill.params.infill_complete_top;
|
||||
}
|
||||
if (surface_fill.params.pattern == ipGrid)
|
||||
params.can_reverse = false;
|
||||
|
||||
@@ -61,7 +61,7 @@ static coordf_t troctWave(coordf_t pos, coordf_t gridSize, coordf_t Zpos)
|
||||
|
||||
// Identify the important points of curve change within a truncated
|
||||
// octahedron wave (as waveform fraction t):
|
||||
// 1. Start of wave (always 0.0)
|
||||
// 1. Start of wave (always 0.0; not needed if the pattern base starts here)
|
||||
// 2. Transition to upper "horizontal" part
|
||||
// 3. Transition from upper "horizontal" part
|
||||
// 4. Transition to lower "horizontal" part
|
||||
@@ -73,17 +73,11 @@ static coordf_t troctWave(coordf_t pos, coordf_t gridSize, coordf_t Zpos)
|
||||
* \ /
|
||||
* o---o
|
||||
*/
|
||||
static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
|
||||
static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
|
||||
{
|
||||
std::vector<coordf_t> res = {0.};
|
||||
std::vector<coordf_t> res;
|
||||
coordf_t perpOffset = abs(triWave(Zpos, gridSize) / 2.);
|
||||
|
||||
coordf_t normalisedOffset = perpOffset / gridSize;
|
||||
// // for debugging: just generate evenly-distributed points
|
||||
// for(coordf_t i = 0; i < 2; i += 0.05){
|
||||
// res.push_back(gridSize * i);
|
||||
// }
|
||||
// note: 0 == straight line
|
||||
if(normalisedOffset > 0){
|
||||
res.push_back(gridSize * (0. + normalisedOffset));
|
||||
res.push_back(gridSize * (1. - normalisedOffset));
|
||||
@@ -93,113 +87,251 @@ static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
|
||||
return(res);
|
||||
}
|
||||
|
||||
// Generate an array of points that are in the same direction as the
|
||||
// basic printing line (i.e. Y points for columns, X points for rows)
|
||||
// Note: a negative offset only causes a change in the perpendicular
|
||||
// direction
|
||||
static std::vector<coordf_t> colinearPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
||||
const size_t baseLocation, size_t gridLength)
|
||||
// Add additional dense fill in line with the pattern direction to
|
||||
// cover the top squares of the pattern
|
||||
static Polylines addTops(coordf_t Zpos, coordf_t gridSize, coordf_t lengthX, coordf_t lengthY, coordf_t spacing,
|
||||
size_t multiline_count, size_t topDistance)
|
||||
{
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(baseLocation);
|
||||
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= (gridSize*2)) {
|
||||
for(size_t pi = 0; pi < critPoints.size(); pi++){
|
||||
points.push_back(baseLocation + cLoc + critPoints[pi]);
|
||||
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
coordf_t zHalfCycle = fmod(zCycle, 0.5) * 2.;
|
||||
bool printVert = zCycle < 0.5;
|
||||
coordf_t offsetX = multiline_count;
|
||||
coordf_t offsetY = multiline_count;
|
||||
coordf_t perpOffset = abs(triWave(Zpos, gridSize) / 2.);
|
||||
coordf_t gridPoint = gridSize * (0. + perpOffset / gridSize);
|
||||
coordf_t topOffset = gridSize / 2.0 - abs(troctWave(gridPoint, gridSize, Zpos));
|
||||
coordf_t multilineAdjust = (sqrt(2) - 1.0) / 2.;
|
||||
Polylines lines;
|
||||
size_t pointCount = 0;
|
||||
coordf_t gridStartL = gridSize * 0.5 - topOffset;
|
||||
coordf_t gridEndL = gridSize * 0.5 + topOffset;
|
||||
if((topDistance == 0) && (multiline_count == 1)){
|
||||
// extend out a little bit on the first layer to help fuse the cover
|
||||
gridStartL -= spacing;
|
||||
gridEndL += spacing;
|
||||
} else if(multiline_count > 1) {
|
||||
// match start point to the corner edge
|
||||
gridStartL -= spacing * multiline_count * multilineAdjust;
|
||||
gridEndL += spacing * multiline_count * multilineAdjust;
|
||||
}
|
||||
// top cover extents perpendicular to the direction of travel
|
||||
coordf_t gridStartP = gridSize * 0.5 - topOffset + spacing * multiline_count / 2. + spacing / 2.;
|
||||
coordf_t gridEndP = gridSize * 0.5 + topOffset - spacing * multiline_count / 2. - spacing / 2.;
|
||||
coordf_t x, y;
|
||||
int xm, ym;
|
||||
// if the print direction needs to be rotated, then swap the extents
|
||||
if((topDistance % 2) == 0){
|
||||
std::swap(gridStartL, gridStartP);
|
||||
std::swap(gridEndL, gridEndP);
|
||||
}
|
||||
// adjust spacing so that it starts and ends on exactly the right place
|
||||
// and increase fill density slightly to reduce gaps
|
||||
coordf_t region_count = floor((gridEndP - gridStartP) / (spacing / sqrt(2)));
|
||||
if(region_count <= 0){
|
||||
return lines;
|
||||
}
|
||||
spacing = (gridEndP - gridStartP) / region_count;
|
||||
for (x = offsetX, xm = 0; x <= (lengthX); x+= gridSize, xm = xm ^ 1) {
|
||||
for (y = offsetY, ym = 0; y <= (lengthY); y += gridSize, ym = ym ^ 1) {
|
||||
if(((xm ^ ym) == 1) == printVert){
|
||||
continue;
|
||||
}
|
||||
// // For debugging: remove 0,0 -> 1,1 top to help understand orientation
|
||||
// if((x <= (gridSize + EPSILON)) && (y <= (gridSize + EPSILON)) && ((y - x) < EPSILON)){
|
||||
// continue;
|
||||
// }
|
||||
Polyline newPoints;
|
||||
int dirMod = xm ^ ym;
|
||||
if(printVert == (topDistance % 2)){
|
||||
if(y < (lengthY - spacing * multiline_count * 1.5)){
|
||||
coordf_t endPMod = std::min(lengthX - (multiline_count * (spacing + 1) / 2.), x + gridEndP) - x;
|
||||
coordf_t endLMod = std::min(lengthY - (multiline_count * (spacing + 1) / 2.), y + gridEndL) - y;
|
||||
for(coordf_t xi = gridStartP; xi < (endPMod + EPSILON); xi += spacing, dirMod = dirMod ^ 1){
|
||||
newPoints.points.push_back((dirMod == 0) ? Point(x + xi, y + gridStartL) : Point(x + xi, y + endLMod));
|
||||
newPoints.points.push_back((dirMod == 0) ? Point(x + xi, y + endLMod) : Point(x + xi, y + gridStartL));
|
||||
pointCount += 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(x < (lengthX - spacing * multiline_count * 1.5)){
|
||||
coordf_t endPMod = std::min(lengthY - (multiline_count * (spacing + 1) / 2.), y + gridEndP) - y;
|
||||
coordf_t endLMod = std::min(lengthX - (multiline_count * (spacing + 1) / 2.), x + gridEndL) - x;
|
||||
for(coordf_t yi = gridStartP; yi < (endPMod + EPSILON); yi += spacing, dirMod = dirMod ^ 1){
|
||||
newPoints.points.push_back((dirMod == 0) ? Point(x + gridStartL, y + yi) : Point(x + endLMod, y + yi));
|
||||
newPoints.points.push_back((dirMod == 0) ? Point(x + endLMod, y + yi) : Point(x + gridStartL, y + yi));
|
||||
pointCount += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.push_back(newPoints);
|
||||
}
|
||||
}
|
||||
points.push_back(gridLength);
|
||||
return points;
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Generate an array of points for the dimension that is perpendicular to
|
||||
// the basic printing line (i.e. X points for columns, Y points for rows)
|
||||
static std::vector<coordf_t> perpendPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
||||
size_t baseLocation, size_t gridLength,
|
||||
size_t offsetBase, coordf_t perpDir)
|
||||
// Generate a set of polylines that complete octahedron curves on the
|
||||
// extremities of a pattern
|
||||
static Polylines makeEndPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
||||
coordf_t lengthX, coordf_t lengthY, coordf_t spacing, size_t multiline_count)
|
||||
{
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(offsetBase);
|
||||
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= gridSize*2) {
|
||||
for(size_t pi = 0; pi < critPoints.size(); pi++){
|
||||
Polylines lines;
|
||||
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
bool printVert = zCycle < 0.5;
|
||||
bool printHoriz = zCycle >= 0.5;
|
||||
int zFlipOffset = ((sgn(fmod(zCycle, 0.5) - 0.25) > 0) == printVert) ? 0 : 1;
|
||||
// create templates for copying
|
||||
Polylines startLines, endLines;
|
||||
for(size_t li = 0; li < multiline_count; li++){
|
||||
coordf_t oAdj = (li - ((multiline_count - 1) / 2.)) * spacing; // orthogonal line adjustment
|
||||
coordf_t dAdj = oAdj * sqrt(2); // diagonal line adjustment
|
||||
Polyline startLine, endLine;
|
||||
// Left Bottom; Bottom Left
|
||||
startLine.points.push_back(printHoriz ? Point(oAdj, -dAdj) : Point(-dAdj, oAdj));
|
||||
// Right Bottom; Top Left
|
||||
endLine.points.push_back(printHoriz ? Point(-oAdj, -dAdj) : Point(-dAdj, -oAdj));
|
||||
for(size_t pi = 0; pi < 2; pi++){
|
||||
int pDir = pi * 2 - 1;
|
||||
coordf_t pAdj = pDir * (sqrt(2) - 1) * oAdj;
|
||||
coordf_t troctOffset = abs(troctWave(critPoints[pi], gridSize, Zpos));
|
||||
startLine.points.push_back(printHoriz ?
|
||||
Point(-troctOffset, critPoints[pi] + pAdj) :
|
||||
Point(critPoints[pi] + pAdj, -troctOffset));
|
||||
endLine.points.push_back(printHoriz ?
|
||||
Point(troctOffset, critPoints[pi] + pAdj) :
|
||||
Point(critPoints[pi] + pAdj, troctOffset));
|
||||
}
|
||||
// Left Top; Bottom Right
|
||||
startLine.points.push_back(printHoriz ? Point(oAdj, gridSize + dAdj) : Point(gridSize + dAdj, oAdj));
|
||||
// Right Top; Top Right
|
||||
endLine.points.push_back(printHoriz ? Point(-oAdj, gridSize + dAdj) : Point(gridSize + dAdj, -oAdj));
|
||||
startLines.push_back(startLine);
|
||||
endLines.push_back(endLine);
|
||||
}
|
||||
coordf_t gridMaxX = ceil((lengthX - EPSILON) / gridSize) * gridSize;
|
||||
coordf_t gridMaxY = ceil((lengthY - EPSILON) / gridSize) * gridSize;
|
||||
for(size_t li = 0; li < multiline_count; li++){
|
||||
coordf_t mlFactor = (li - ((multiline_count - 1) / 2.)) * spacing;
|
||||
for (coordf_t cLoc = zFlipOffset * gridSize; cLoc < ((printHoriz ? gridMaxY : gridMaxX) - EPSILON); cLoc += gridSize * 2) {
|
||||
Polyline tsLine(startLines[li]);
|
||||
Polyline teLine(endLines[li]);
|
||||
tsLine.translate(printVert ? Point(cLoc, -mlFactor) : Point(-mlFactor, cLoc));
|
||||
teLine.translate(printVert ? Point(cLoc, gridMaxY + mlFactor) : Point(gridMaxX + mlFactor, cLoc));
|
||||
lines.push_back(tsLine);
|
||||
lines.push_back(teLine);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Generate a polyline that describes a single path segment through
|
||||
// the infill in the same direction as the basic printing line (i.e. X
|
||||
// points for columns, Y points for rows)
|
||||
static Polyline patternPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
||||
coordf_t gridLength, coordf_t perpDir, int print_dir, coordf_t oAdj)
|
||||
{
|
||||
Polyline line;
|
||||
coordf_t dAdj = oAdj * (sqrt(2) - 1); // additional diagonal adjustment
|
||||
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
int zFlipDirection = sgn(fmod(zCycle, 0.5) - 0.25);
|
||||
bool hitEnd = false;
|
||||
int endPi = -1;
|
||||
size_t pi = 0;
|
||||
size_t piOfs = 0;
|
||||
line.points.push_back((print_dir == 1) ? Point(dAdj, 0.) : Point(0., dAdj));
|
||||
coordf_t gridMax = ceil((gridLength - EPSILON) / gridSize) * gridSize;
|
||||
for (coordf_t cLoc = 0; cLoc < gridMax; cLoc += gridSize, piOfs = (piOfs + 2) % 4) {
|
||||
for(pi = piOfs; pi < (piOfs + 2); pi++){
|
||||
coordf_t offset = troctWave(critPoints[pi], gridSize, Zpos);
|
||||
points.push_back(offsetBase + (offset * perpDir));
|
||||
coordf_t offsetFlip = sgn(offset);
|
||||
coordf_t posFlip = floor(((pi + 1) % 4) / 2) * 2 - 1;
|
||||
coordf_t posLin = cLoc - (piOfs * gridSize / 2.) + critPoints[pi];
|
||||
coordf_t posPerp = offset * perpDir;
|
||||
line.points.push_back((print_dir == 1) ?
|
||||
Point(posPerp, posLin + posFlip * dAdj * perpDir * zFlipDirection * print_dir) :
|
||||
Point(posLin + posFlip * dAdj * perpDir * zFlipDirection * print_dir, posPerp));
|
||||
}
|
||||
}
|
||||
points.push_back(offsetBase);
|
||||
return points;
|
||||
}
|
||||
|
||||
static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
|
||||
{
|
||||
assert(x.size() == y.size());
|
||||
Pointfs out;
|
||||
out.reserve(x.size());
|
||||
for (size_t i = 0; i < x.size(); ++ i)
|
||||
out.push_back(Vec2d(x[i], y[i]));
|
||||
return out;
|
||||
line.points.push_back((print_dir == 1) ? Point(dAdj, gridMax) : Point(gridMax, dAdj));
|
||||
return line;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron.
|
||||
static std::vector<Pointfs> makeActualGrid(coordf_t Zpos, coordf_t gridSize, size_t boundsX, size_t boundsY)
|
||||
static Polylines makeZigZag(coordf_t Zpos, coordf_t gridSize, coordf_t lengthX, coordf_t lengthY,
|
||||
coordf_t spacing, size_t multiline_count)
|
||||
{
|
||||
std::vector<Pointfs> points;
|
||||
Polylines lines;
|
||||
std::vector<coordf_t> critPoints = getCriticalPoints(Zpos, gridSize);
|
||||
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
bool printVert = zCycle < 0.5;
|
||||
if (printVert) {
|
||||
int perpDir = -1;
|
||||
for (coordf_t x = 0; x <= (boundsX); x+= gridSize, perpDir *= -1) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
perpendPoints(Zpos, gridSize, critPoints, 0, boundsY, x, perpDir),
|
||||
colinearPoints(Zpos, gridSize, critPoints, 0, boundsY));
|
||||
BoundingBox extents;
|
||||
int perpDir = -1;
|
||||
int perpDirPattern = -1;
|
||||
coordf_t gridMax = ceil(((printVert ? lengthX : lengthY) - EPSILON) / gridSize) * gridSize;
|
||||
for (coordf_t pPos = 0; pPos < gridMax; pPos += gridSize, perpDirPattern *= -1) {
|
||||
for (size_t li = 0; li < multiline_count; li++){
|
||||
coordf_t oAdj = (li - ((multiline_count - 1) / 2.)) * spacing; // orthogonal line adjustment
|
||||
Polyline newPoints;
|
||||
newPoints = patternPoints(Zpos, gridSize, critPoints,
|
||||
printVert ? lengthY : lengthX,
|
||||
perpDirPattern, printVert ? 1 : -1, oAdj);
|
||||
if (perpDir == 1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
}
|
||||
} else {
|
||||
int perpDir = 1;
|
||||
for (coordf_t y = gridSize; y <= (boundsY); y+= gridSize, perpDir *= -1) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
colinearPoints(Zpos, gridSize, critPoints, 0, boundsX),
|
||||
perpendPoints(Zpos, gridSize, critPoints, 0, boundsX, y, perpDir));
|
||||
if (perpDir == -1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
std::reverse(newPoints.points.begin(), newPoints.points.end());
|
||||
newPoints.translate(printVert ? Point(pPos + oAdj, 0.) : Point(0., pPos + oAdj));
|
||||
extents.merge(newPoints.points);
|
||||
lines.push_back(newPoints);
|
||||
perpDir *= -1;
|
||||
}
|
||||
}
|
||||
return points;
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron with a specified
|
||||
// grid square size.
|
||||
// gridWidth and gridHeight define the width and height of the bounding box respectively
|
||||
static Polylines makeGrid(coordf_t z, coordf_t gridSize, coordf_t boundWidth, coordf_t boundHeight, bool fillEvenly)
|
||||
// Note: this uses the 'complete' infill parameter to determine if the
|
||||
// square tops should be enclosed (true) or open (false). Alternatively,
|
||||
// a rotation angle of 180 degrees or greater can be used.
|
||||
static Polylines makeGrid(coordf_t z, coordf_t zLast, coordf_t gridSize,
|
||||
coordf_t lengthX, coordf_t lengthY,
|
||||
bool completeTops, coordf_t spacing, size_t multiline_count, size_t layer_count)
|
||||
{
|
||||
std::vector<Pointfs> polylines = makeActualGrid(z, gridSize, boundWidth, boundHeight);
|
||||
coordf_t zCycle = fmod(z + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
bool printVert = zCycle < 0.5;
|
||||
coordf_t zCycleLast = fmod(zLast + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
bool printVertLast = zCycleLast < 0.5;
|
||||
Polylines result;
|
||||
result.reserve(polylines.size());
|
||||
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin();
|
||||
it_polylines != polylines.end(); ++ it_polylines) {
|
||||
result.push_back(Polyline());
|
||||
Polyline &polyline = result.back();
|
||||
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
|
||||
polyline.points.push_back(Point(coord_t((*it)(0)), coord_t((*it)(1))));
|
||||
Polylines polyZag = makeZigZag(z, gridSize, lengthX, lengthY, spacing, multiline_count);
|
||||
result.insert(result.end(), polyZag.begin(), polyZag.end());
|
||||
// add end connectors
|
||||
std::vector<coordf_t> critPoints = getCriticalPoints(z, gridSize);
|
||||
Polylines endPoints = makeEndPoints(z, gridSize, critPoints, lengthX, lengthY, spacing, multiline_count);
|
||||
result.insert(result.end(), endPoints.begin(), endPoints.end());
|
||||
// add tops for the first <multiline_count> layers in each cycle
|
||||
if(completeTops && (printVert != printVertLast)){
|
||||
coordf_t layerHeight = (z - zLast) / (multiline_count * layer_count);
|
||||
size_t top_distance = 0;
|
||||
for(coordf_t zCheck = z; zCheck >= (zLast + EPSILON); zCheck -= layerHeight * layer_count, top_distance++){
|
||||
coordf_t zCheckCycle = fmod(zCheck + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
||||
if(printVert != (zCheckCycle < 0.5)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
Polylines polytops = addTops(z, gridSize, lengthX, lengthY, spacing, multiline_count, top_distance);
|
||||
result.insert(result.end(), polytops.begin(), polytops.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FillParams has the following useful information:
|
||||
// density <0 .. 1> [proportion of space to fill]
|
||||
// anchor_length [???]
|
||||
// anchor_length_max [???]
|
||||
// dont_connect() [avoid connect lines]
|
||||
// dont_adjust [avoid filling space evenly]
|
||||
// monotonic [fill strictly left to right]
|
||||
// complete [complete each loop]
|
||||
// multiline [number of lines to draw for each pattern line]
|
||||
// complete_top [should the top surfaces of the pattern be filled]
|
||||
|
||||
void Fill3DHoneycomb::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
@@ -208,78 +340,62 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
// Support infill angle
|
||||
auto infill_angle = float(this->angle);
|
||||
if (std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle);
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
|
||||
// Expand the bounding box to avoid artifacts at the edges
|
||||
coord_t expand = 5 * (scale_(this->spacing));
|
||||
bb.offset(expand);
|
||||
// Increase the bounding box outwards to avoid edge clipping artefacts
|
||||
coord_t expandSize = 5. * scale_(this->spacing);
|
||||
bb.offset(expandSize);
|
||||
|
||||
// Adjustment for combining infill setting
|
||||
size_t layersPerSlice = 1;
|
||||
if(thickness_layers > 0){
|
||||
layersPerSlice = thickness_layers;
|
||||
}
|
||||
|
||||
// Note: with equally-scaled X/Y/Z, the pattern will create a vertically-stretched
|
||||
// truncated octahedron; so Z is pre-adjusted first by scaling by sqrt(2)
|
||||
coordf_t zScale = sqrt(2);
|
||||
|
||||
// adjustment to account for the additional distance of octagram curves
|
||||
// note: this only strictly applies for a rectangular area where the total
|
||||
// Z travel distance is a multiple of the spacing... but it should
|
||||
// be at least better than the prevous estimate which assumed straight
|
||||
// lines
|
||||
// Density adjustment to account for the additional distance of
|
||||
// octagram curves. [This only strictly applies for a rectangular
|
||||
// area where the total Z travel distance is a multiple of the
|
||||
// spacing]
|
||||
// = 4 * integrate(func=4*x(sqrt(2) - 1) + 1, from=0, to=0.25)
|
||||
// = (sqrt(2) + 1) / 2 [... I think]
|
||||
// make a first guess at the preferred grid Size
|
||||
coordf_t gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
|
||||
|
||||
// This density calculation is incorrect for many values > 25%, possibly
|
||||
// due to quantisation error, so this value is used as a first guess, then the
|
||||
// Z scale is adjusted to make the layer patterns consistent / symmetric
|
||||
// This means that the resultant infill won't be an ideal truncated octahedron,
|
||||
// but it should look better than the equivalent quantised version
|
||||
|
||||
//Orca: uses a fixed layer height to avoid inconsistent bridges and variable layer height artifacts.
|
||||
//coordf_t layerHeight = scale_(thickness_layers);
|
||||
coordf_t layerHeight = scale_(1.0);
|
||||
// ceiling to an integer value of layers per Z
|
||||
// (with a little nudge in case it's close to perfect)
|
||||
// make a first guess at the preferred grid Size (in unscaled units)
|
||||
coordf_t gridSize = (scale_(this->spacing) *
|
||||
((zScale + 1.) / 2.) * params.multiline / params.density);
|
||||
coordf_t layerHeight = scale_(params.layer_height);
|
||||
coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
|
||||
if(params.density > 0.42){ // exact layer pattern for >42% density
|
||||
// If a density over 42% is requested, set an exact layer pattern
|
||||
if((params.density > 0.42) || (layersPerModule < 2)){
|
||||
layersPerModule = 2;
|
||||
// re-adjust the grid size for a partial octahedral path
|
||||
// (scale of 1.1 guessed based on modeling)
|
||||
gridSize = (scale_(this->spacing) * 1.1 * params.multiline / params.density);
|
||||
// re-adjust zScale to make layering consistent
|
||||
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
||||
} else {
|
||||
if(layersPerModule < 2){
|
||||
layersPerModule = 2;
|
||||
}
|
||||
// re-adjust zScale to make layering consistent
|
||||
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
||||
// re-adjust the grid size to account for the new zScale
|
||||
gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
|
||||
// re-calculate layersPerModule and zScale
|
||||
layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
|
||||
if(layersPerModule < 2){
|
||||
layersPerModule = 2;
|
||||
}
|
||||
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
||||
}
|
||||
|
||||
// align bounding box to a multiple of our honeycomb grid module
|
||||
// (a module is 2*$gridSize since one $gridSize half-module is
|
||||
// growing while the other $gridSize half-module is shrinking)
|
||||
bb.merge(align_to_grid(bb.min, Point(gridSize*4, gridSize*4)));
|
||||
// align bounding box to a multiple of the octahedron grid so that
|
||||
// layers with different starting points have matching origins
|
||||
bb.merge(align_to_grid(bb.min, Point(gridSize * 2., gridSize * 2.)));
|
||||
|
||||
// Z adjustment to start at the widest point for the lowest layer
|
||||
coordf_t startOffset = gridSize / 2. + scale_(params.layer_height / 2.);
|
||||
// generate pattern
|
||||
Polylines polylines =
|
||||
makeGrid(
|
||||
scale_(this->z) * zScale,
|
||||
gridSize,
|
||||
bb.size()(0),
|
||||
bb.size()(1),
|
||||
!params.dont_adjust);
|
||||
scale_(this->z) * zScale + startOffset,
|
||||
scale_(this->z - (params.layer_height * params.multiline * layersPerSlice)) * zScale + startOffset,
|
||||
gridSize, bb.size()(0), bb.size()(1),
|
||||
params.infill_complete_top,
|
||||
scale_(this->spacing),
|
||||
params.multiline,
|
||||
layersPerSlice);
|
||||
|
||||
// move pattern in place
|
||||
for (Polyline &pl : polylines){
|
||||
@@ -290,8 +406,8 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
smooth_polyline_corners(pl, params.smooth_factor, scaled<double>(params.resolution));
|
||||
}
|
||||
|
||||
// Apply multiline offset if needed
|
||||
multiline_fill(polylines, params, spacing);
|
||||
// Note: multiline fill adjustment is carried out in this code,
|
||||
// rather than using the multiline_fill function
|
||||
|
||||
// clip pattern to boundaries, chain the clipped polylines
|
||||
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
|
||||
|
||||
@@ -118,6 +118,7 @@ struct FillParams
|
||||
|
||||
float horiz_move{0.0}; //move infill to get cross zag pattern
|
||||
bool symmetric_infill_y_axis{false};
|
||||
bool infill_complete_top{false};
|
||||
coord_t symmetric_y_axis{0};
|
||||
bool locked_zag{false};
|
||||
float infill_lock_depth{0.0};
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
#endif
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <ios>
|
||||
#ifdef _WIN32
|
||||
#include <boost/interprocess/sync/file_lock.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
@@ -378,7 +378,7 @@ void Layer::simplify_support_entity_collection(ExtrusionEntityCollection* entity
|
||||
//BBS: method to simplify support path
|
||||
void Layer::simplify_support_path(ExtrusionPath * path)
|
||||
{
|
||||
const auto print_config = this->object()->print()->config();
|
||||
const auto &print_config = this->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
@@ -393,7 +393,7 @@ void Layer::simplify_support_path(ExtrusionPath * path)
|
||||
//BBS: method to simplify support path
|
||||
void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
|
||||
{
|
||||
const auto print_config = this->object()->print()->config();
|
||||
const auto &print_config = this->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
@@ -410,7 +410,7 @@ void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
|
||||
//BBS: method to simplify support path
|
||||
void Layer::simplify_support_loop(ExtrusionLoop* loop)
|
||||
{
|
||||
const auto print_config = this->object()->print()->config();
|
||||
const auto &print_config = this->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
|
||||
@@ -1101,7 +1101,7 @@ void LayerRegion::simplify_entity_collection(ExtrusionEntityCollection* entity_c
|
||||
|
||||
void LayerRegion::simplify_path(ExtrusionPath* path)
|
||||
{
|
||||
const auto print_config = this->layer()->object()->print()->config();
|
||||
const auto &print_config = this->layer()->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
@@ -1119,7 +1119,7 @@ void LayerRegion::simplify_path(ExtrusionPath* path)
|
||||
|
||||
void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
|
||||
{
|
||||
const auto print_config = this->layer()->object()->print()->config();
|
||||
const auto &print_config = this->layer()->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
@@ -1139,7 +1139,7 @@ void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
|
||||
|
||||
void LayerRegion::simplify_loop(ExtrusionLoop* loop)
|
||||
{
|
||||
const auto print_config = this->layer()->object()->print()->config();
|
||||
const auto &print_config = this->layer()->object()->print()->config();
|
||||
const bool spiral_mode = print_config.spiral_mode;
|
||||
const bool enable_arc_fitting = print_config.enable_arc_fitting;
|
||||
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <system_error>
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
#include <clocale>
|
||||
|
||||
// Defined where the floating point std::to_chars can be called, which with Apple's libc++ runtime is from macOS 13.3.
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
|
||||
@@ -1209,6 +1209,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"infill_lock_depth",
|
||||
"skin_infill_depth",
|
||||
"skin_infill_density",
|
||||
"infill_complete_top",
|
||||
"align_infill_direction_to_model",
|
||||
"extra_solid_infills",
|
||||
"center_of_surface_pattern",
|
||||
|
||||
@@ -3813,7 +3813,11 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
|
||||
auto ams_name = ams.opt_string("tray_name", 0u);
|
||||
auto filament_changed = !ams.has("filament_changed") || ams.opt_bool("filament_changed");
|
||||
auto filament_multi_color = ams.opt<ConfigOptionStrings>("filament_multi_colour")->values;
|
||||
if (filament_id.empty()) {
|
||||
auto filament_type = ams.opt_string("filament_type", 0u);
|
||||
auto is_placeholder = ams.has("filament_slot_placeholder") && ams.opt_bool("filament_slot_placeholder", 0u);
|
||||
// A printer-set tray carries a material type but no preset id; resolve it to the
|
||||
// matching Generic preset so it is not dropped and the combo array keeps aligning.
|
||||
if (filament_id.empty() && (is_placeholder || filament_type.empty())) {
|
||||
continue;
|
||||
}
|
||||
if (!filament_changed && this->filament_presets.size() > combox_info.ams_filament_presets.size()) {
|
||||
@@ -3823,16 +3827,48 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
|
||||
combox_info.ams_names.push_back(ams_name);
|
||||
continue;
|
||||
}
|
||||
auto iter = std::find_if(filaments.begin(), filaments.end(),
|
||||
[this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
|
||||
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
|
||||
auto iter = filaments.end();
|
||||
if (!filament_id.empty()) {
|
||||
iter = std::find_if(filaments.begin(), filaments.end(),
|
||||
[this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
|
||||
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
|
||||
}
|
||||
if (iter == filaments.end()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
|
||||
auto filament_type = ams.opt_string("filament_type", 0u);
|
||||
if (!filament_type.empty()) {
|
||||
auto original_type = filament_type;
|
||||
filament_type = "Generic " + filament_type;
|
||||
iter = std::find_if(filaments.begin(), filaments.end(),
|
||||
[&filament_type](auto &f) { return f.is_compatible && f.is_system && boost::algorithm::starts_with(f.name, filament_type); });
|
||||
if (iter == filaments.end()) {
|
||||
// Similarity fallback: find a generic preset whose filament_type
|
||||
// appears as a whole word in the AMS type (e.g. "ASA" in "ASA Sparkle").
|
||||
auto upper_type = boost::to_upper_copy(original_type);
|
||||
auto contains_word = [](const std::string& haystack, const std::string& needle) {
|
||||
auto pos = haystack.find(needle);
|
||||
while (pos != std::string::npos) {
|
||||
bool start_ok = (pos == 0 || !std::isalnum(static_cast<unsigned char>(haystack[pos - 1])));
|
||||
bool end_ok = (pos + needle.size() >= haystack.size() ||
|
||||
!std::isalnum(static_cast<unsigned char>(haystack[pos + needle.size()])));
|
||||
if (start_ok && end_ok)
|
||||
return true;
|
||||
pos = haystack.find(needle, pos + 1);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// Find the longest-matching preset type to prefer e.g. "PA-CF" over "PA".
|
||||
size_t best_len = 0;
|
||||
for (auto it = filaments.begin(); it != filaments.end(); ++it) {
|
||||
if (!it->is_compatible || !it->is_system || !boost::algorithm::starts_with(it->name, "Generic "))
|
||||
continue;
|
||||
auto preset_type = boost::to_upper_copy(it->config.opt_string("filament_type", 0u));
|
||||
if (preset_type.size() > best_len && contains_word(upper_type, preset_type)) {
|
||||
iter = it;
|
||||
best_len = preset_type.size();
|
||||
filament_type = "Generic " + it->config.opt_string("filament_type", 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iter == filaments.end()) {
|
||||
// Prefer old selection
|
||||
@@ -3843,7 +3879,11 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
|
||||
combox_info.ams_names.push_back(ams_name);
|
||||
continue;
|
||||
}
|
||||
iter = std::find_if(filaments.begin(), filaments.end(), [](auto &f) { return f.is_compatible && f.is_system; });
|
||||
iter = std::find_if(filaments.begin(), filaments.end(), [](auto &f) {
|
||||
return f.is_compatible && f.is_system && boost::algorithm::starts_with(f.name, "Generic ");
|
||||
});
|
||||
if (iter == filaments.end())
|
||||
iter = std::find_if(filaments.begin(), filaments.end(), [](auto &f) { return f.is_compatible && f.is_system; });
|
||||
if (iter == filaments.end())
|
||||
continue;
|
||||
}
|
||||
@@ -3881,6 +3921,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
for (auto &entry : filament_ams_list) {
|
||||
auto & ams = entry.second;
|
||||
auto filament_id = ams.opt_string("filament_id", 0u);
|
||||
const bool printer_set_tray = filament_id.empty();
|
||||
auto filament_color = ams.opt_string("filament_colour", 0u);
|
||||
auto filament_color_type = ams.opt_string("filament_colour_type", 0u);
|
||||
auto filament_changed = !ams.has("filament_changed") || ams.opt_bool("filament_changed");
|
||||
@@ -3893,42 +3934,58 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
ams_array_maps.push_back(temp);
|
||||
index++;
|
||||
if (filament_id.empty()) {
|
||||
if (use_map) {
|
||||
for (int j = maps.size() - 1; j >= 0; j--) {
|
||||
if (maps[j].slot_id == slot_id && maps[j].ams_id == ams_id) {
|
||||
maps.erase(j);
|
||||
// A tray written by the printer UI carries a material type but no
|
||||
// OrcaSlicer preset id. Resolve it to the matching Generic preset with
|
||||
// the tray's own color instead of dropping it (direct sync) or forcing
|
||||
// "Generic PLA" (mapping sync). Placeholders and typeless trays keep
|
||||
// the previous behavior.
|
||||
const auto tray_type = ams.opt_string("filament_type", 0u);
|
||||
if (is_placeholder || tray_type.empty()) {
|
||||
if (use_map) {
|
||||
for (int j = maps.size() - 1; j >= 0; j--) {
|
||||
if (maps[j].slot_id == slot_id && maps[j].ams_id == ams_id) {
|
||||
maps.erase(j);
|
||||
}
|
||||
}
|
||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||
auto default_unknown_color = "#CECECE";
|
||||
ams_filament_colors.push_back(default_unknown_color);
|
||||
ams_filament_color_types.push_back("1");
|
||||
if (filament_multi_color.size() == 0) {
|
||||
filament_multi_color.push_back(default_unknown_color);
|
||||
}
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
} else if (is_placeholder) {
|
||||
// Orca: push placeholders to keep index alignment with ams_infos
|
||||
ams_filament_presets.push_back("");
|
||||
ams_filament_colors.push_back("");
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
}
|
||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||
auto default_unknown_color = "#CECECE";
|
||||
ams_filament_colors.push_back(default_unknown_color);
|
||||
ams_filament_color_types.push_back("1");
|
||||
if (filament_multi_color.size() == 0) {
|
||||
filament_multi_color.push_back(default_unknown_color);
|
||||
}
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
} else if (is_placeholder) {
|
||||
// Orca: push placeholders to keep index alignment with ams_infos
|
||||
ams_filament_presets.push_back("");
|
||||
ams_filament_colors.push_back("");
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!filament_changed && this->filament_presets.size() > ams_filament_presets.size()) {
|
||||
ams_filament_presets.push_back(this->filament_presets[ams_filament_presets.size()]);
|
||||
ams_filament_colors.push_back(filament_color);
|
||||
ams_filament_color_types.push_back(filament_color_type);
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
ams_infos.back().valid = true;
|
||||
continue;
|
||||
}
|
||||
bool has_type = false;
|
||||
auto filament_type = ams.opt_string("filament_type", 0u);
|
||||
auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id, &has_type, filament_type](auto &f) {
|
||||
has_type |= f.config.opt_string("filament_type", 0u) == filament_type;
|
||||
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
|
||||
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
|
||||
auto iter = filaments.end();
|
||||
if (!filament_id.empty()) {
|
||||
iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id, &has_type, filament_type](auto &f) {
|
||||
has_type |= f.config.opt_string("filament_type", 0u) == filament_type;
|
||||
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
|
||||
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
|
||||
} else {
|
||||
// The material type is the only identity a printer-set tray carries.
|
||||
has_type = std::any_of(filaments.begin(), filaments.end(), [&filament_type](auto &f) {
|
||||
return f.is_compatible && f.config.opt_string("filament_type", 0u) == filament_type; });
|
||||
}
|
||||
if (iter == filaments.end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
|
||||
if (!filament_type.empty()) {
|
||||
@@ -3968,6 +4025,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool resolved_by_material_type = printer_set_tray && iter != filaments.end();
|
||||
if (iter == filaments.end()) {
|
||||
// Prefer old selection
|
||||
if (ams_filament_presets.size() < this->filament_presets.size()) {
|
||||
@@ -3975,6 +4033,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
ams_filament_colors.push_back(filament_color);
|
||||
ams_filament_color_types.push_back(filament_color_type);
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
ams_infos.back().valid = true;
|
||||
unknowns.emplace_back(&ams, has_type ? L("The filament may not be compatible with the current machine settings. Generic filament presets will be used.") :
|
||||
L("The filament model is unknown. Still using the previous filament preset."));
|
||||
continue;
|
||||
@@ -3990,7 +4049,8 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
if (iter == filaments.end())
|
||||
continue;
|
||||
}
|
||||
unknowns.emplace_back(&ams, boost::algorithm::starts_with(iter->name, filament_type) ?
|
||||
if (!resolved_by_material_type)
|
||||
unknowns.emplace_back(&ams, boost::algorithm::starts_with(iter->name, filament_type) ?
|
||||
(has_type ? L("The filament may not be compatible with the current machine settings. Generic filament presets will be used.") :
|
||||
L("The filament model is unknown. Generic filament presets will be used.")) :
|
||||
(has_type ? L("The filament may not be compatible with the current machine settings. A random filament preset will be used.") :
|
||||
@@ -4001,6 +4061,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
ams_filament_colors.push_back(filament_color);
|
||||
ams_filament_color_types.push_back(filament_color_type);
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
ams_infos.back().valid = true;
|
||||
}
|
||||
if (ams_filament_presets.empty())
|
||||
return 0;
|
||||
@@ -4440,9 +4501,21 @@ std::vector<std::vector<DynamicPrintConfig>> PresetBundle::get_extruder_filament
|
||||
return filament_infos;
|
||||
}
|
||||
|
||||
// ORCA TODO: currently, this function assumes the printer name follows the pattern of "<printer_model> <nozzle_diameter>", e.g.
|
||||
// printer_type: "Bambu Lab X2D", nozzle_diameter_str: "0.4 nozzle" => printer_name: "Bambu Lab X2D 0.4 nozzle". If the printer name does
|
||||
// not follow this pattern, the function may not work correctly.
|
||||
std::string PresetBundle::get_printer_model_display_name(const std::string &model_id) const
|
||||
{
|
||||
if (model_id.empty())
|
||||
return {};
|
||||
for (const auto &vendor_entry : vendors) {
|
||||
for (const auto &model : vendor_entry.second.models) {
|
||||
if (model.model_id == model_id)
|
||||
return model.name;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// ORCA TODO: this assumes printer names follow "<printer_model> <nozzle_diameter>", e.g.
|
||||
// "Bambu Lab X2D 0.4 nozzle". Other naming schemes may not resolve correctly.
|
||||
std::set<std::string> PresetBundle::get_printer_names_by_printer_type_and_nozzle(const std::string &printer_type, std::string nozzle_diameter_str, bool system_only)
|
||||
{
|
||||
std::set<std::string> printer_names;
|
||||
@@ -4464,7 +4537,9 @@ std::set<std::string> PresetBundle::get_printer_names_by_printer_type_and_nozzle
|
||||
if (printer_it->name.find(nozzle_diameter_str) != std::string::npos) printer_names.insert(printer_it->name);
|
||||
}
|
||||
|
||||
assert(printer_names.size() == 1);
|
||||
// No match is normal for a connected machine the user has not installed; only an
|
||||
// ambiguous match is a bug (the caller assumes one preset per model and nozzle).
|
||||
assert(printer_names.size() <= 1);
|
||||
|
||||
for (auto& printer_name : printer_names) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " printer name: " << printer_name;
|
||||
@@ -4477,8 +4552,8 @@ std::vector<Preset *> PresetBundle::get_filament_presets_for_machine(const std::
|
||||
const std::string &nozzle_diameter_str,
|
||||
bool include_user_presets)
|
||||
{
|
||||
// Printer model plus nozzle diameter is expected to resolve to a single system printer preset;
|
||||
// get_printer_names_by_printer_type_and_nozzle asserts as much in debug builds.
|
||||
// Printer model plus nozzle diameter normally resolves to a single system printer preset.
|
||||
// Zero matches is normal for a connected machine the user never installed.
|
||||
const std::set<std::string> printer_names = get_printer_names_by_printer_type_and_nozzle(printer_type, nozzle_diameter_str);
|
||||
const Preset *printer = printer_names.empty() ? nullptr : printers.find_preset(*printer_names.begin());
|
||||
if (printer == nullptr)
|
||||
|
||||
@@ -411,6 +411,9 @@ public:
|
||||
|
||||
std::vector<std::vector<DynamicPrintConfig>> get_extruder_filament_info() const;
|
||||
|
||||
// Resolve a vendor model id (an agent-reported machine identity) to the display name
|
||||
// printer profiles use as printer_model. Empty when no loaded vendor declares it.
|
||||
std::string get_printer_model_display_name(const std::string &model_id) const;
|
||||
std::set<std::string> get_printer_names_by_printer_type_and_nozzle(const std::string &printer_type, std::string nozzle_diameter_str, bool system_only = true);
|
||||
// Orca: the root filament presets a connected machine can use, resolved with the rule the rest
|
||||
// of the app applies (is_compatible_with_printer): an empty compatible_printers means every
|
||||
|
||||
@@ -4574,6 +4574,13 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("infill_complete_top", coBool);
|
||||
def->label = L("Fill pattern tops");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Choose this option if you want to completely fill in the tops of the infill pattern");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
// Orca: max layer height for combined infill
|
||||
def = this->add("infill_combination_max_layer_height", coFloatOrPercent);
|
||||
def->label = L("Infill combination - Max layer height");
|
||||
@@ -5301,7 +5308,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionEnum<InputShaperType>(InputShaperType::Default));
|
||||
|
||||
def = this->add("input_shaping_freq_x", coFloat);
|
||||
def->label = L("X");
|
||||
def->label = L_CONTEXT("X", "Axis");
|
||||
def->tooltip = L("Resonant frequency for the X axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal.");
|
||||
def->sidetext = L("Hz"); // Hertz, CIS languages need translation
|
||||
def->min = 0;
|
||||
@@ -5310,7 +5317,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("input_shaping_freq_y", coFloat);
|
||||
def->label = L("Y");
|
||||
def->label = L_CONTEXT("Y", "Axis");
|
||||
def->tooltip = L("Resonant frequency for the Y axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.");
|
||||
def->sidetext = L("Hz"); // Hertz, CIS languages need translation
|
||||
def->min = 0;
|
||||
@@ -5319,7 +5326,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("input_shaping_damp_x", coFloat);
|
||||
def->label = L("X");
|
||||
def->label = L_CONTEXT("X", "Axis");
|
||||
def->tooltip = L("Damping ratio for the X axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
@@ -5327,7 +5334,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0.1));
|
||||
|
||||
def = this->add("input_shaping_damp_y", coFloat);
|
||||
def->label = L("Y");
|
||||
def->label = L_CONTEXT("Y", "Axis");
|
||||
def->tooltip = L("Damping ratio for the Y axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
@@ -8530,14 +8537,14 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("display_pixels_x", coInt);
|
||||
//def->full_label = L("");
|
||||
def->label = ("X");
|
||||
def->label = L_CONTEXT("X", "Axis");
|
||||
//def->tooltip = L("");
|
||||
def->min = 100;
|
||||
def->set_default_value(new ConfigOptionInt(2560));
|
||||
|
||||
def = this->add("display_pixels_y", coInt);
|
||||
//def->full_label = L("");
|
||||
def->label = ("Y");
|
||||
def->label = L_CONTEXT("Y", "Axis");
|
||||
//def->tooltip = L("");
|
||||
def->min = 100;
|
||||
def->set_default_value(new ConfigOptionInt(1440));
|
||||
|
||||
@@ -1358,6 +1358,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, bottom_layer_direction))
|
||||
((ConfigOptionString, solid_infill_rotate_template))
|
||||
((ConfigOptionBool, symmetric_infill_y_axis))
|
||||
((ConfigOptionBool, infill_complete_top))
|
||||
((ConfigOptionFloat, infill_shift_step))
|
||||
((ConfigOptionString, sparse_infill_rotate_template))
|
||||
((ConfigOptionPercent, sparse_infill_density))
|
||||
|
||||
@@ -3450,14 +3450,14 @@ void PrintObject::bridge_over_infill()
|
||||
const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve ||
|
||||
region_config.sparse_infill_pattern == ipOctagramSpiral;
|
||||
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
|
||||
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
|
||||
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
|
||||
|
||||
area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(),
|
||||
[internal_unsupported_area](const Polygon &p) {
|
||||
return intersection({p}, internal_unsupported_area).empty();
|
||||
ExPolygons bridge_components = intersection_ex(expand(candidate.new_polys, flow.scaled_spacing()), deep_infill_area);
|
||||
// Orca: Filter whole bridge areas so their holes remain holes.
|
||||
bridge_components.erase(std::remove_if(bridge_components.begin(), bridge_components.end(),
|
||||
[&internal_unsupported_area](const ExPolygon &component) {
|
||||
return intersection_ex(component, internal_unsupported_area).empty();
|
||||
}),
|
||||
area_to_be_bridge.end());
|
||||
bridge_components.end());
|
||||
Polygons area_to_be_bridge = to_polygons(std::move(bridge_components));
|
||||
|
||||
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
|
||||
|
||||
|
||||
@@ -1944,7 +1944,7 @@ Polygons TreeSupport::get_trim_support_regions(
|
||||
static const double no_overlap_xy_gap = 0.2f;
|
||||
double gap_xy_scaled = scale_(gap_xy);
|
||||
SupportLayer& support_layer = *support_layer_ptr;
|
||||
auto m_print_config = object.print()->config();
|
||||
const PrintConfig& print_config = object.print()->config();
|
||||
|
||||
size_t idx_object_layer_overlapping = size_t(-1);
|
||||
|
||||
@@ -1991,7 +1991,7 @@ Polygons TreeSupport::get_trim_support_regions(
|
||||
const Layer& object_layer = *object.layers()[i];
|
||||
bool some_region_overlaps = false;
|
||||
for (LayerRegion* region : object_layer.regions()) {
|
||||
coordf_t bridging_height = region->region().bridging_height_avg(m_print_config);
|
||||
coordf_t bridging_height = region->region().bridging_height_avg(print_config);
|
||||
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
|
||||
break;
|
||||
some_region_overlaps = true;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <tbb/task_arena.h>
|
||||
|
||||
#include "Thread.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
// We are using quite an old TBB 2017 U7, which does not support global control API officially.
|
||||
// Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
|
||||
#include <tbb/tbb.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace boost::posix_time { class ptime; }
|
||||
#if ! defined(TBB_VERSION_MAJOR)
|
||||
|
||||
@@ -368,6 +368,10 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/MarkdownTip.hpp
|
||||
GUI/MediaFilePanel.cpp
|
||||
GUI/MediaFilePanel.h
|
||||
GUI/OrcaFilesPanel.cpp
|
||||
GUI/OrcaFilesPanel.h
|
||||
GUI/StoragePanel.cpp
|
||||
GUI/StoragePanel.h
|
||||
GUI/MediaPlayCtrl.cpp
|
||||
GUI/MediaPlayCtrl.h
|
||||
GUI/WebRtcMediaController.cpp
|
||||
@@ -457,6 +461,11 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/PresetHints.hpp
|
||||
GUI/PrinterCloudAuthDialog.cpp
|
||||
GUI/PrinterCloudAuthDialog.hpp
|
||||
GUI/Printer/BambuFileGridModel.cpp
|
||||
GUI/Printer/BambuFileGridModel.h
|
||||
GUI/Printer/IFileGridModel.h
|
||||
GUI/Printer/RemoteFileGridModel.cpp
|
||||
GUI/Printer/RemoteFileGridModel.h
|
||||
GUI/Printer/PrinterFileSystem.cpp
|
||||
GUI/Printer/PrinterFileSystem.h
|
||||
GUI/PrinterWebView.cpp
|
||||
@@ -772,8 +781,12 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/ICameraSignalingChannel.hpp
|
||||
Utils/OrcaCloudServiceAgent.cpp
|
||||
Utils/OrcaCloudServiceAgent.hpp
|
||||
Utils/OrcaMqttConnection.cpp
|
||||
Utils/OrcaMqttConnection.hpp
|
||||
Utils/OrcaPrinterAgent.cpp
|
||||
Utils/OrcaPrinterAgent.hpp
|
||||
Utils/OrcaCloudSignalingChannel.cpp
|
||||
Utils/OrcaCloudSignalingChannel.hpp
|
||||
Utils/QidiPrinterAgent.cpp
|
||||
Utils/QidiPrinterAgent.hpp
|
||||
Utils/SnapmakerPrinterAgent.cpp
|
||||
@@ -917,7 +930,7 @@ target_include_directories(libslic3r_gui PRIVATE Utils ${CMAKE_CURRENT_BINARY_DI
|
||||
|
||||
if (WIN32)
|
||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/WebView2/include)
|
||||
target_link_libraries(libslic3r_gui Advapi32)
|
||||
target_link_libraries(libslic3r_gui Advapi32 Crypt32)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "slic3r/GUI/DeviceCore/DevExtruderSystem.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevManager.h"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
|
||||
@@ -1228,11 +1229,11 @@ void AMSDryCtrWin::update_normal_description(DevAms* dev_ams)
|
||||
for (const auto& lim : ams_limits) {
|
||||
if (dev_ams->GetAmsType() == lim.type) {
|
||||
if (temp_val > lim.max_temp) {
|
||||
wxString msg = wxString::Format(_L("%s maximum drying temperature is %d°C."), wxString(lim.name), lim.max_temp);
|
||||
wxString msg = wxString::Format(_L("%s maximum drying temperature is %d\u2103." /* °C */), wxString(lim.name), lim.max_temp);
|
||||
warning_text += msg + "\n";
|
||||
can_enable_button = false;
|
||||
} else if (temp_val < lim.min_temp) {
|
||||
wxString msg = wxString::Format(_L("%s minimum drying temperature is %d°C."), wxString(lim.name), lim.min_temp);
|
||||
wxString msg = wxString::Format(_L("%s minimum drying temperature is %d\u2103." /* °C */), wxString(lim.name), lim.min_temp);
|
||||
warning_text += msg + "\n";
|
||||
can_enable_button = false;
|
||||
}
|
||||
@@ -1260,7 +1261,7 @@ void AMSDryCtrWin::update_normal_description(DevAms* dev_ams)
|
||||
auto limit_temperature = preset.value().filament_dev_ams_drying_heat_distortion_temperature;
|
||||
if (temp_val > limit_temperature) {
|
||||
warning_text += _L("The temperature shall not exceed the filament's heat distortion temperature") + "(" +
|
||||
wxString::Format(wxT("%d"), static_cast<int>(limit_temperature)) + wxString::FromUTF8("°C)\n");
|
||||
wxString::Format(wxT("%d"), static_cast<int>(limit_temperature)) + wxString::FromUTF8(u8"\u2103" /* °C */) + ")\n";
|
||||
can_enable_button = false;
|
||||
}
|
||||
}
|
||||
@@ -1633,8 +1634,16 @@ int AMSDryCtrWin::update_filament_list(DevAms* dev_ams, MachineObject* obj)
|
||||
stream << std::fixed << std::setprecision(1) << obj->GetExtderSystem()->GetNozzleDiameter(extruder_id);
|
||||
std::string nozzle_diameter_str = stream.str();
|
||||
|
||||
// The connected device's model may not resolve (OrcaSonar's is optional); the
|
||||
// helper falls back to the selected profile so the list is never empty.
|
||||
const std::string filament_printer_model = resolve_filament_printer_model(obj->printer_type, preset_bundle);
|
||||
if (filament_printer_model.empty()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " cannot resolve a printer model for the filament list";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Preset *filament_it : preset_bundle->get_filament_presets_for_machine(
|
||||
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
filament_printer_model, nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
if (!filament_id_set.insert(filament_it->filament_id).second)
|
||||
continue;
|
||||
const std::string filament_alias = filaments.get_preset_alias(*filament_it, true);
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <slic3r/GUI/DeviceManager.hpp>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ios>
|
||||
@@ -472,6 +474,13 @@ void AMSMaterialsSetting::update_filament_editing(bool is_printing)
|
||||
m_button_confirm->Show(true);
|
||||
}
|
||||
|
||||
// A third-party tray owns its temp range; BBL RFID trays keep the read-only preset values.
|
||||
// Only the OrcaSonar agent accepts the temp fields, so other agents stay read-only.
|
||||
const bool is_orca_agent = obj && obj->printer_agent_id == ORCA_PRINTER_AGENT_ID;
|
||||
const bool can_edit = !is_printing || obj->is_support_filament_setting_inprinting;
|
||||
m_input_nozzle_min->Enable(m_is_third && can_edit && is_orca_agent);
|
||||
m_input_nozzle_max->Enable(m_is_third && can_edit && is_orca_agent);
|
||||
|
||||
if (!m_is_third) {
|
||||
m_tip_readonly->SetLabelText(wxEmptyString);
|
||||
m_tip_readonly->Hide();
|
||||
@@ -494,6 +503,8 @@ void AMSMaterialsSetting::update_filament_editing(bool is_printing)
|
||||
if (m_view_only) { // Orca: view-only (2D laser/cut) — lock every edit control and hide apply/reset
|
||||
m_comboBox_filament->Enable(false);
|
||||
m_comboBox_cali_result->Enable(false);
|
||||
m_input_nozzle_min->Enable(false);
|
||||
m_input_nozzle_max->Enable(false);
|
||||
m_input_k_val->Enable(false);
|
||||
m_input_n_val->Enable(false);
|
||||
m_button_confirm->Hide();
|
||||
@@ -543,7 +554,7 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
}
|
||||
|
||||
// set k / n value
|
||||
if (obj->cali_version <= -1 && obj->get_printer_series() == PrinterSeries::SERIES_P1P) {
|
||||
if (obj->supports_extrusion_cali() && obj->cali_version <= -1 && obj->get_printer_series() == PrinterSeries::SERIES_P1P) {
|
||||
// set extrusion cali ratio
|
||||
int cali_tray_id = ams_id * 4 + slot_id;
|
||||
|
||||
@@ -564,7 +575,7 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
}
|
||||
obj->command_extrusion_cali_set(cali_tray_id, "", "", k, n);
|
||||
}
|
||||
else {
|
||||
else if (obj->supports_extrusion_cali()) {
|
||||
PACalibIndexInfo select_index_info;
|
||||
int tray_id = ams_id * 4 + slot_id;
|
||||
if (is_virtual_tray()) {
|
||||
@@ -723,6 +734,21 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (nozzle_temp_min_int < FILAMENT_MIN_TEMP || nozzle_temp_min_int > FILAMENT_MAX_TEMP ||
|
||||
nozzle_temp_max_int < FILAMENT_MIN_TEMP || nozzle_temp_max_int > FILAMENT_MAX_TEMP) {
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("The input value should be greater than %1% and less than %2%"), FILAMENT_MIN_TEMP, FILAMENT_MAX_TEMP),
|
||||
wxEmptyString, wxICON_WARNING | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
if (nozzle_temp_min_int > nozzle_temp_max_int) {
|
||||
MessageDialog msg_dlg(nullptr, _L("The minimum temperature cannot be greater than the maximum temperature."),
|
||||
wxEmptyString, wxICON_WARNING | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Orca: log the tray payload this dialog hands the printer, so the filament_id resolved from the
|
||||
// dropdown selection can be checked against the tray_info_idx the AMS actually receives. A
|
||||
@@ -746,7 +772,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
wxString k_text = m_input_k_val->GetTextCtrl()->GetValue();
|
||||
wxString n_text = m_input_n_val->GetTextCtrl()->GetValue();
|
||||
|
||||
if (obj->cali_version <= -1 && (obj->get_printer_series() != PrinterSeries::SERIES_X1) && !ExtrusionCalibration::check_k_validation(k_text)) {
|
||||
if (obj->supports_extrusion_cali() && obj->cali_version <= -1 && (obj->get_printer_series() != PrinterSeries::SERIES_X1) && !ExtrusionCalibration::check_k_validation(k_text)) {
|
||||
wxString k_tips = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE);
|
||||
wxString kn_tips = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f, N in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE, 0.6, 2.0);
|
||||
MessageDialog msg_dlg(nullptr, k_tips, wxEmptyString, wxICON_WARNING | wxOK);
|
||||
@@ -776,7 +802,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
vt_tray = VIRTUAL_TRAY_DEPUTY_ID;
|
||||
}
|
||||
|
||||
if (obj->cali_version >= 0) {
|
||||
if (obj->supports_extrusion_cali() && obj->cali_version >= 0) {
|
||||
PACalibIndexInfo select_index_info;
|
||||
select_index_info.tray_id = vt_tray;
|
||||
select_index_info.ams_id = ams_id;
|
||||
@@ -795,7 +821,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
|
||||
CalibUtils::select_PA_calib_result(select_index_info);
|
||||
}
|
||||
else {
|
||||
else if (obj->supports_extrusion_cali()) {
|
||||
obj->command_extrusion_cali_set(vt_tray, "", "", k, n);
|
||||
}
|
||||
}
|
||||
@@ -817,7 +843,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
;
|
||||
}
|
||||
|
||||
if (obj->cali_version >= 0) {
|
||||
if (obj->supports_extrusion_cali() && obj->cali_version >= 0) {
|
||||
PACalibIndexInfo select_index_info;
|
||||
select_index_info.tray_id = cali_tray_id;
|
||||
select_index_info.ams_id = ams_id;
|
||||
@@ -836,7 +862,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
|
||||
CalibUtils::select_PA_calib_result(select_index_info);
|
||||
}
|
||||
else {
|
||||
else if (obj->supports_extrusion_cali()) {
|
||||
obj->command_extrusion_cali_set(cali_tray_id, "", "", k, n);
|
||||
}
|
||||
}
|
||||
@@ -932,7 +958,12 @@ bool AMSMaterialsSetting::is_virtual_tray()
|
||||
|
||||
void AMSMaterialsSetting::update_widgets()
|
||||
{
|
||||
if (obj && obj->get_printer_series() == PrinterSeries::SERIES_X1 && obj->cali_version <= -1) {
|
||||
if (obj && !obj->supports_extrusion_cali()) {
|
||||
// No printer-side K/N records: do not offer them.
|
||||
m_panel_normal->Show();
|
||||
m_panel_kn->Hide();
|
||||
}
|
||||
else if (obj && obj->get_printer_series() == PrinterSeries::SERIES_X1 && obj->cali_version <= -1) {
|
||||
// Low version firmware does not display k value
|
||||
m_panel_kn->Hide();
|
||||
}
|
||||
@@ -993,6 +1024,9 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
|
||||
m_input_k_val->GetTextCtrl()->SetValue(k);
|
||||
m_input_n_val->GetTextCtrl()->SetValue(n);
|
||||
// Tray values are re-supplied by every caller; clear so a previous popup cannot leak in.
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
|
||||
wxArrayString filament_items;
|
||||
wxString bambu_filament_name;
|
||||
@@ -1016,11 +1050,14 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
}
|
||||
stream << std::fixed << std::setprecision(1) << machine_diameter;
|
||||
std::string nozzle_diameter_str = stream.str();
|
||||
// OrcaSonar's model id is optional, so the device model alone can resolve to nothing;
|
||||
// the helper falls back to the selected profile so the dropdown is never empty.
|
||||
const std::string filament_printer_model = resolve_filament_printer_model(obj->printer_type, preset_bundle);
|
||||
|
||||
if (preset_bundle) {
|
||||
if (preset_bundle && !filament_printer_model.empty()) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << preset_bundle->filaments.size();
|
||||
for (Preset *filament_it : preset_bundle->get_filament_presets_for_machine(
|
||||
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
filament_printer_model, nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
if (!filament_id_set.insert(filament_it->filament_id).second)
|
||||
continue;
|
||||
const std::string alias = preset_bundle->filaments.get_preset_alias(*filament_it, true);
|
||||
@@ -1087,6 +1124,12 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
else {
|
||||
m_comboBox_filament->Show();
|
||||
m_readonly_filament->Hide();
|
||||
// A printer-set tray carries its own temps; the preset selection only fills
|
||||
// them when the tray has none.
|
||||
if (!temp_min.IsEmpty() && temp_min != "0")
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(temp_min);
|
||||
if (!temp_max.IsEmpty() && temp_max != "0")
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(temp_max);
|
||||
}
|
||||
|
||||
if (obj->cali_version >= 0) {
|
||||
@@ -1223,7 +1266,12 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
}
|
||||
}
|
||||
|
||||
if (filament_items.IsEmpty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "ams_materials_setting: no filament presets for printer model \""
|
||||
<< filament_printer_model << "\" at nozzle " << nozzle_diameter_str;
|
||||
|
||||
m_comboBox_filament->Set(filament_items);
|
||||
m_comboBox_from_printer = true;
|
||||
m_comboBox_filament->SetSelection(selection_idx);
|
||||
post_select_event(selection_idx);
|
||||
|
||||
@@ -1231,9 +1279,6 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
m_comboBox_filament->SetValue(wxEmptyString);
|
||||
}
|
||||
|
||||
// Set the flag whether to open the filament setting dialog from the device page
|
||||
m_comboBox_filament->SetClientData(new int(1));
|
||||
|
||||
update();
|
||||
Layout();
|
||||
Fit();
|
||||
@@ -1277,50 +1322,53 @@ int AMSMaterialsSetting::get_filament_variant_index(const Preset &filament, cons
|
||||
|
||||
void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
{
|
||||
// Get the flag whether to open the filament setting dialog from the device page
|
||||
int* from_printer = static_cast<int*>(m_comboBox_filament->GetClientData());
|
||||
// True for the popup's own initial selection: the dialog pre-filled the tray's
|
||||
// temps, so the matching preset must not overwrite them.
|
||||
const bool initial_printer_selection = m_comboBox_from_printer;
|
||||
|
||||
m_filament_type = "";
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
std::string filament_printer_model;
|
||||
if (preset_bundle) {
|
||||
std::ostringstream stream;
|
||||
if (obj) {
|
||||
// Defensive: this dialog is opened only from StatusPanel (BBL-only) today, so the fallback fires
|
||||
// only during the brief BBL startup window before firmware reports nozzle info. Without this,
|
||||
// the "0.0" lookup string returns an empty set and filament lookup yields no results.
|
||||
// Use the selected profile's nozzle diameter until the connected device reports one.
|
||||
float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
|
||||
if (machine_diameter == 0.0f) {
|
||||
const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter");
|
||||
if (opt) machine_diameter = static_cast<const ConfigOptionFloats *>(opt)->values[0];
|
||||
}
|
||||
stream << std::fixed << std::setprecision(1) << machine_diameter;
|
||||
filament_printer_model = resolve_filament_printer_model(obj->printer_type, preset_bundle);
|
||||
}
|
||||
std::string nozzle_diameter_str = stream.str();
|
||||
// Resolve the selection against the same list Popup() built the dropdown from, so the two
|
||||
// halves of the dialog cannot disagree about which filaments this machine can use.
|
||||
const std::string selected = m_comboBox_filament->GetValue().ToStdString();
|
||||
if (!selected.empty()) {
|
||||
if (!selected.empty() && !filament_printer_model.empty()) {
|
||||
const std::string filament_id = map_filament_items[selected].filament_id;
|
||||
for (Preset *it : preset_bundle->get_filament_presets_for_machine(
|
||||
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
filament_printer_model, nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
if (it->filament_id != filament_id)
|
||||
continue;
|
||||
// ) if nozzle_temperature_range is found
|
||||
const int variant_index = get_filament_variant_index(*it, nozzle_diameter_str);
|
||||
ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low");
|
||||
if (opt_min) {
|
||||
ConfigOptionInts* opt_min_ints = dynamic_cast<ConfigOptionInts*>(opt_min);
|
||||
if (opt_min_ints) {
|
||||
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
|
||||
if (!initial_printer_selection) {
|
||||
// ) if nozzle_temperature_range is found
|
||||
const int variant_index = get_filament_variant_index(*it, nozzle_diameter_str);
|
||||
ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low");
|
||||
if (opt_min) {
|
||||
ConfigOptionInts* opt_min_ints = dynamic_cast<ConfigOptionInts*>(opt_min);
|
||||
if (opt_min_ints) {
|
||||
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index));
|
||||
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigOption* opt_max = it->config.option("nozzle_temperature_range_high");
|
||||
if (opt_max) {
|
||||
ConfigOptionInts* opt_max_ints = dynamic_cast<ConfigOptionInts*>(opt_max);
|
||||
if (opt_max_ints) {
|
||||
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index));
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
|
||||
ConfigOption* opt_max = it->config.option("nozzle_temperature_range_high");
|
||||
if (opt_max) {
|
||||
ConfigOptionInts* opt_max_ints = dynamic_cast<ConfigOptionInts*>(opt_max);
|
||||
if (opt_max_ints) {
|
||||
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index));
|
||||
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigOption* opt_type = it->config.option("filament_type");
|
||||
@@ -1359,13 +1407,16 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
m_button_confirm->Disable(); // ORCA No need to change style
|
||||
m_comboBox_cali_result->Clear();
|
||||
m_comboBox_cali_result->SetValue(wxEmptyString);
|
||||
m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
m_input_n_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
m_comboBox_filament->SetClientData(new int(0));
|
||||
if (!initial_printer_selection) {
|
||||
m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
m_input_n_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
}
|
||||
m_comboBox_from_printer = false;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
m_button_confirm->Enable(true); // ORCA No need to change style
|
||||
if (!m_view_only)
|
||||
m_button_confirm->Enable(true); // ORCA No need to change style
|
||||
}
|
||||
|
||||
//filament id
|
||||
@@ -1389,6 +1440,9 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ams_filament_id.empty())
|
||||
m_clr_picker->is_empty(false);
|
||||
|
||||
wxArrayString items;
|
||||
m_pa_profile_items.clear();
|
||||
m_comboBox_cali_result->SetValue(wxEmptyString);
|
||||
@@ -1444,7 +1498,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
|
||||
m_comboBox_cali_result->Set(items);
|
||||
if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
if (from_printer && (*from_printer == 1)) {
|
||||
if (initial_printer_selection) {
|
||||
for (auto slot : obj->vt_slot) {
|
||||
if (slot.id == std::to_string(ams_id))
|
||||
cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, slot.cali_idx);
|
||||
@@ -1461,10 +1515,11 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (from_printer && (*from_printer == 1)) {
|
||||
if (initial_printer_selection) {
|
||||
DevAmsTray* selected_tray = this->obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (!selected_tray)
|
||||
{
|
||||
m_comboBox_from_printer = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1495,7 +1550,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
else {
|
||||
if (!ams_filament_id.empty()) {
|
||||
//m_input_k_val->GetTextCtrl()->SetValue("0.00");
|
||||
m_input_k_val->Enable(true);
|
||||
m_input_k_val->Enable(!m_view_only);
|
||||
}
|
||||
else {
|
||||
//m_input_k_val->GetTextCtrl()->SetValue("0.00");
|
||||
@@ -1503,7 +1558,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
m_comboBox_filament->SetClientData(new int(0));
|
||||
m_comboBox_from_printer = false;
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect)
|
||||
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
std::string ams_filament_id;
|
||||
std::string ams_setting_id;
|
||||
|
||||
bool m_is_third;
|
||||
bool m_is_third = false;
|
||||
// Orca: view-only mode (laser/cut). When set, the dialog is inspectable but every
|
||||
// editing control is disabled and no command is sent.
|
||||
bool m_view_only = false;
|
||||
@@ -200,6 +200,9 @@ protected:
|
||||
wxStaticText* m_n_param;
|
||||
TextInput* m_input_n_val;
|
||||
int m_filament_selection;
|
||||
// True while the popup's initial, printer-driven selection is being handled; it
|
||||
// keeps that selection from overwriting the tray's own temps with preset temps.
|
||||
bool m_comboBox_from_printer = false;
|
||||
|
||||
int m_pa_cali_select_id = 0;
|
||||
|
||||
|
||||
@@ -530,7 +530,8 @@ void AMSSetting::on_insert_material_read(wxCommandEvent &event)
|
||||
bool tray_read_opt = m_checkbox_Insert_material_auto_read->GetValue();
|
||||
bool remain_opt = m_checkbox_remain->GetValue();
|
||||
|
||||
m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt);
|
||||
if (m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt) != 0)
|
||||
UpdateByObj(m_obj);
|
||||
|
||||
m_sizer_Insert_material_tip_inline->Layout();
|
||||
Layout();
|
||||
@@ -556,7 +557,8 @@ void AMSSetting::on_starting_read(wxCommandEvent &event)
|
||||
bool tray_read_opt = m_checkbox_Insert_material_auto_read->GetValue();
|
||||
bool remain_opt = m_checkbox_remain->GetValue();
|
||||
|
||||
m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt);
|
||||
if (m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt) != 0)
|
||||
UpdateByObj(m_obj);
|
||||
|
||||
m_sizer_starting_tip_inline->Layout();
|
||||
Layout();
|
||||
@@ -570,7 +572,8 @@ void AMSSetting::on_remain(wxCommandEvent& event)
|
||||
bool start_read_opt = m_checkbox_starting_auto_read->GetValue();
|
||||
bool tray_read_opt = m_checkbox_Insert_material_auto_read->GetValue();
|
||||
bool remain_opt = m_checkbox_remain->GetValue();
|
||||
m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt);
|
||||
if (m_obj->command_ams_user_settings(start_read_opt, tray_read_opt, remain_opt) != 0)
|
||||
UpdateByObj(m_obj);
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
@@ -806,4 +809,4 @@ void AMSSettingArrangeAMSOrder::OnBtnRearrangeClicked(wxCommandEvent& event)
|
||||
}
|
||||
#endif
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -264,7 +264,8 @@ AboutDialog::AboutDialog()
|
||||
vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5));
|
||||
auto version_string = std::string(SoftFever_VERSION); // _L("Orca Slicer ") + " " + std::string(SoftFever_VERSION);
|
||||
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
|
||||
wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format("Build %s", build_commit_label), wxDefaultPosition, wxDefaultSize);
|
||||
// TRN %s is the commit the application was built from
|
||||
wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format(_L("Build %s"), build_commit_label), wxDefaultPosition, wxDefaultSize);
|
||||
credits_string->SetFont(_build_string_font);
|
||||
wxFont version_font = GetFont();
|
||||
version_font = version_font.Scaled(1.85f); // SetPointSize(20) not works on macOS because it uses a 72 PPI reference
|
||||
@@ -330,7 +331,7 @@ AboutDialog::AboutDialog()
|
||||
|
||||
copyright_hor_sizer->Add(copyright_ver_sizer, 0, wxLEFT, FromDIP(20));
|
||||
|
||||
wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2026 OrcaSlicer Pte Ltd All Rights Reserved", wxDefaultPosition, wxDefaultSize);
|
||||
wxStaticText *html_text = new wxStaticText(this, wxID_ANY, _L("Copyright(C) 2026 OrcaSlicer Pte Ltd All Rights Reserved"), wxDefaultPosition, wxDefaultSize);
|
||||
html_text->SetForegroundColour(wxColour(107, 107, 107));
|
||||
|
||||
copyright_ver_sizer->Add(html_text, 0, wxALL , 0);
|
||||
|
||||
@@ -329,7 +329,8 @@ void AmsMapingPopup::update_mapping_items(MachineObject* obj, const std::vector<
|
||||
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? "Ext-R" : "Ext-L";
|
||||
// TRN Short labels of the external spool of the right and left nozzle
|
||||
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? _L("Ext-R") : _L("Ext-L");
|
||||
auto ams_mapping_item_container = new MappingContainer(m_right_marea_panel, shown_name, 1);
|
||||
ams_mapping_item_container->SetName(m_right_marea_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
@@ -506,7 +507,7 @@ void AmsMapingPopup::update_ams_tips(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj && obj->GetFilaSwitch()->IsInstalled()) {
|
||||
const auto& msg = _L("External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
const auto& msg = _L("External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
|
||||
m_ams_tips_msg_panel->AddMessage(msg, "#FF6F00", "");
|
||||
}
|
||||
|
||||
@@ -588,7 +589,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
// slots Ext-R / Ext-L inline in the two-nozzle left+right views (matches that overload's result).
|
||||
if ((m_show_type == ShowType::LEFT_AND_RIGHT || m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC)
|
||||
&& (tray_data[i].id == VIRTUAL_TRAY_MAIN_ID || tray_data[i].id == VIRTUAL_TRAY_DEPUTY_ID)) {
|
||||
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? wxString("Ext-R") : wxString("Ext-L"));
|
||||
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? _L("Ext-R") : _L("Ext-L"));
|
||||
} else {
|
||||
m_mapping_item->set_tray_index(wxGetApp().transition_tridid(tray_data[i].id));
|
||||
}
|
||||
@@ -607,7 +608,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
"External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,7 +625,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
"External spools are not supported since Filament Track Switch has been installed. If you want to use an external spool, please uninstall it.");
|
||||
}
|
||||
} else if (m_show_type != ShowType::RIGHT && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
can_pick_the_item = false;
|
||||
@@ -718,7 +719,7 @@ void AmsMapingPopup::add_ext_ams_mapping(TrayData tray_data, MappingItem* item)
|
||||
});
|
||||
}
|
||||
|
||||
item->set_tray_index("Ext");
|
||||
item->set_tray_index(_L("Ext"));
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -65,6 +65,8 @@
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/dcmemory.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@@ -1152,6 +1154,7 @@ void AuxiliaryPanel::update_all_cover()
|
||||
m_sizer_description->Add(m_text_description, 0, wxALIGN_TOP | wxRIGHT, FromDIP(10));
|
||||
m_input_description = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
|
||||
wxSize(FromDIP(450), FromDIP(300)), wxTE_MULTILINE | wxTE_PROCESS_ENTER);
|
||||
m_input_description->SetBackgroundColour(*wxWHITE);
|
||||
m_input_description->SetFont(::Label::Body_14);
|
||||
m_sizer_description->Add(m_input_description, 0, wxALIGN_CENTER, 0);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace GUI {
|
||||
|
||||
|
||||
AuxiliaryDialog::AuxiliaryDialog(wxWindow * parent)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Auxiliaryies"), wxDefaultPosition,
|
||||
: DPIDialog(parent, wxID_ANY, _L("Auxiliaries"), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
m_aux_list = new AuxiliaryList(this);
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/PartPlate.hpp"
|
||||
#include "slic3r/Utils/PrintHost.hpp"
|
||||
#ifdef _WIN32
|
||||
#include <excpt.h>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#ifndef L
|
||||
#define L(s) s // gettext marker, as in slic3r/GUI/I18N.hpp
|
||||
#endif
|
||||
#ifndef L_CONTEXT
|
||||
#define L_CONTEXT(s, context) s
|
||||
#endif
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
@@ -52,7 +55,7 @@ inline uint32_t offer_bit(OfferSel s) { return 1u << int(s); }
|
||||
// nullptr -> kernel support exists, no GUI path yet (row shows disabled)
|
||||
struct OfferVerb {
|
||||
const char* id;
|
||||
const char* name; // drawing-office word (L10); marked L(); translated at use
|
||||
const char* name; // drawing-office word (L10); msgctxt "Design", translated at use
|
||||
int row; // index into kOfferRowNames, the ratified address — NEVER reorder
|
||||
const char* key; // shortcut shown in the row, or nullptr
|
||||
const char* action;
|
||||
@@ -99,107 +102,107 @@ static const bool kOfferRowFlat[] = {
|
||||
};
|
||||
|
||||
static const OfferVerb kOfferVerbs[] = {
|
||||
{"sketch", L("Sketch"), 0, "Shift+S", "key:S+S", L("Click a face or a reference plane in the viewport, then a sketch tool"), 0x00000403u, 0, 0, false, false, nullptr, "design_sketch", L("Click a face or a reference plane, then pick a drawing tool")},
|
||||
{"extrude", L("Extrude"), 1, "Shift+E", "key:S+E", L("Create a sketch, or pick a solid face, first"), 0x00004002u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch profile, or push/pull a picked face")},
|
||||
{"revolve", L("Revolve"), 1, "Shift+R", "key:S+R", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a profile about an axis")},
|
||||
{"sweep", L("Sweep"), 1, "Shift+W", "key:S+W", L("Create a profile sketch to sweep first"), 0x00004000u, 0, 2, false, false, nullptr, "design_sweep", L("Sweep a profile along a path")},
|
||||
{"loft", L("Loft"), 1, "Shift+L", "key:S+L", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between two or more profiles")},
|
||||
{"thicken", L("Thicken"), 1, nullptr, "fly:material#4", L("Thicken needs a solid body — add or import one first"), 0x0000000au, 1, 0, false, false, nullptr, "design_thicken", L("Offset a solid face into a thin plate (new body)")},
|
||||
{"rib", L("Rib"), 1, "R", "fly:material#5", L("Rib needs a solid body — add or import one first"), 0x00010000u, 1, 0, false, false, nullptr, "design_rib", L("Grow a thin wall from an open sketch line, fused to a body")},
|
||||
{"boolean", L("Join"), 1, "Shift+B", "btn:bool#0", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Fuse the tool body into the target — one solid, no seam")},
|
||||
{"bool_subtract", L("Subtract"), 1, nullptr, "btn:bool#1", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Cut the tool body out of the target")},
|
||||
{"bool_intersect", L("Intersect"), 1, nullptr, "btn:bool#2", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Keep only where the two bodies overlap")},
|
||||
{"surf_extrude", L("Surface Extrude"), 1, "Shift+G", "key:S+G", L("Create a sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch into a sheet body (no end caps)")},
|
||||
{"surf_revolve", L("Surface Revolve"), 1, nullptr, "fly:surface#1", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a sketch profile into a sheet body")},
|
||||
{"surf_loft", L("Surface Loft"), 1, nullptr, "fly:surface#2", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between 2+ profiles, open (no end caps)")},
|
||||
{"surf_fill", L("Surface Fill"), 1, nullptr, "fly:surface#3", L("Create a closed sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_surface", L("Fill a sketch boundary with a smooth face")},
|
||||
{"thicken_surf", L("Thicken Surface"), 1, nullptr, "fly:surface#5", L("Thicken Surface needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_thicken", L("Thicken a sheet body into a solid")},
|
||||
{"hole", L("Hole"), 2, "Shift+H", "key:S+H", L("Pick a face or a plane to drill into"), 0x00000402u, 1, 0, false, false, nullptr, "design_hole", L("Drill a hole, centerd on a picked face or placed on a plane")},
|
||||
{"thread", L("Thread"), 2, "Shift+T", "key:S+T", L("Pick a cylindrical surface (bore / outer) or a circular edge for a thread"), 0x00000024u, 1, 0, false, false, nullptr, "design_thread", L("Thread a cylindrical surface (inner bore / outer) or a circular edge")},
|
||||
{"shell", L("Shell"), 2, "Shift+K", "key:S+K", L("Shell needs a solid body — add or import one first"), 0x00000082u, 1, 0, false, false, nullptr, "design_shell", L("Hollow the body to a wall thickness, opening a picked face")},
|
||||
{"cut", L("Cut"), 2, "Shift+X", "key:S+X", L("Cut needs a solid body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_cut", L("Trim the body with a plane — drag the offset arrow; keep one half or both")},
|
||||
{"split", L("Split"), 2, nullptr, nullptr, L("Split needs a solid body — add or import one first"), 0x000000feu, 1, 0, false, false, nullptr, nullptr, L("Split the body along a picked face into two solids")},
|
||||
{"fillet", L("Fillet"), 3, "Shift+F", "btn:dress#0", L("Pick an edge to round"), 0x000000b2u, 1, 0, false, false, nullptr, "design_filletedge", L("Pick an edge, then drag the radius arrow or type it")},
|
||||
{"chamfer", L("Chamfer"), 3, nullptr, "btn:dress#1", L("Pick an edge to bevel"), 0x000000b2u, 1, 0, false, false, nullptr, "design_chamfer", L("Pick an edge, then drag the distance arrow or type it")},
|
||||
{"draft", L("Draft"), 3, "Shift+D", "key:S+D", L("Pick a face to taper"), 0x0000000au, 1, 0, false, false, nullptr, "design_draft", L("Tilt a picked face by a draft angle")},
|
||||
{"surf_offset", L("Surface Offset"), 3, nullptr, "fly:surface#4", L("Surface Offset needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_offset", L("Offset a sheet body's shell by a signed distance")},
|
||||
{"pattern", L("Linear pattern"), 4, "Shift+N", "btn:pat#0", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_array", L("Repeat the body along a direction — drag the spacing, set the count")},
|
||||
{"pattern_circular", L("Circular pattern"), 4, nullptr, "btn:pat#1", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_polararray", L("Repeat the body around an axis — set the count and sweep")},
|
||||
{"mirror", L("Mirror"), 4, "Shift+Z", "key:S+Z", L("Mirror needs a body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_mirror", L("Reflect a body about a plane")},
|
||||
{"pat_curve", L("Pattern on Curve"), 4, nullptr, nullptr, L("Pattern on Curve needs a body and a curve"), 0x00000090u, 1, 0, false, false, nullptr, nullptr, L("Repeat the body along a picked curve")},
|
||||
{"transform", L("Move"), 5, "Shift+Y", "key:S+Y", L("Transform needs a body — add or import one first"), 0x000021feu, 1, 0, false, false, nullptr, "design_move", L("Move and/or rotate an existing body")},
|
||||
{"mate", L("Mate"), 5, nullptr, "fly:placement#2", L("Mate needs two coordinate systems — create them first"), 0x00001202u, 2, 0, false, false, nullptr, "design_c_coincident", L("Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)")},
|
||||
{"align", L("Align to"), 5, nullptr, nullptr, L("Align needs a body — add or import one first"), 0x00000002u, 1, 0, false, false, nullptr, nullptr, L("Align the body to a picked face or plane")},
|
||||
{"plane", L("Plane"), 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false, nullptr, "design_plane", L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)")},
|
||||
{"axis", L("Axis"), 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false, nullptr, "design_line", L("Datum axis (two points, face normal, cylinder centerline, two planes, along edge)")},
|
||||
{"coordsys_v", L("Coordinate system"), 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false, nullptr, "design_point", L("Datum coordinate system (world point, or face + direction edge)")},
|
||||
{"helix", L("Helix"), 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false, nullptr, "design_thread", L("Helical curve (spring path) — use as a sweep path for coils / springs / augers")},
|
||||
{"project", L("Project"), 6, nullptr, "fly:plane#4", L("Project needs a body — add or import one first"), 0x00000482u, 1, 0, false, false, nullptr, "design_sketch", L("Project body edges onto a plane as sketch entities")},
|
||||
{"measure", L("Measure"), 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false, nullptr, nullptr, L("Measure between the picked points, edges or faces")},
|
||||
{"mass_props", L("Volume and area"), 6, nullptr, "btn:mass", nullptr, 0x000000feu, 1, 0, false, false, nullptr, "info", L("Report the volume and surface area of the selected body")},
|
||||
{"interference", L("Interference"), 6, nullptr, "btn:interference", L("Interference needs at least two bodies"), 0x00000280u, 2, 0, false, false, nullptr, nullptr, L("Check whether two bodies overlap — reports, changes nothing")},
|
||||
{"edit_feature", L("Edit"), 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false, nullptr, "design_edit", L("Reopen the selected feature to change what it was made from")},
|
||||
{"rename", L("Rename…"), 8, "F2", "btn:rename", L("Select a feature, or a body, to rename it"), 0x00004080u, 0, 0, false, false, nullptr, nullptr, L("Give this feature a name you will recognise in the tree (a body takes its name from the feature that makes it)")},
|
||||
{"delete_face", L("Delete Face"), 7, nullptr, "fly:dressup#3", L("Delete Face needs a body — add or import one first"), 0x0000000eu, 1, 0, false, false, nullptr, "design_delete", L("Remove faces from a body and heal the solid")},
|
||||
{"colour", L("Color"), 8, nullptr, "btn:colour", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "color_palette", L("Set the selected body's display color")},
|
||||
{"delete", L("Delete"), 7, "Del", "btn:delete", nullptr, 0x000f7c00u, 0, 0, false, false, nullptr, "design_delete", L("Delete what is selected")},
|
||||
{"delete_body", L("Delete Body"), 7, nullptr, "btn:delete_body", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "design_delete", L("Delete this whole body — removes the feature it was made from")},
|
||||
{"sk_line_t", L("Line"), 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_line", L("Line — click start, then end")},
|
||||
{"sk_polyline", L("Polyline"), 0, nullptr, "fly:design_line#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_polyline", L("Click points; click the first point to close the loop, right-click to end it open")},
|
||||
{"sk_rect", L("Corner rectangle"), 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect", L("Rectangle — click two opposite corners")},
|
||||
{"sk_rect_center", L("Center rectangle"), 0, nullptr, "fly:design_rect#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_crect", L("Click center, then a corner")},
|
||||
{"sk_rect_oblique", L("Oblique rectangle"), 0, nullptr, "fly:design_rect#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_oblique", L("Click two corners of one edge, then a point for the width")},
|
||||
{"sk_rect_rounded", L("Rounded rectangle"), 0, nullptr, "fly:design_rect#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_rounded", L("Click two opposite corners, then a point for the corner radius")},
|
||||
{"sk_circle", L("Center circle"), 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle", L("Circle — click center, then radius")},
|
||||
{"sk_circle_2pt", L("2-point circle"), 0, nullptr, "fly:design_circle#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle2pt", L("Click two ends of the diameter")},
|
||||
{"sk_circle_3pt", L("3-point circle"), 0, nullptr, "fly:design_circle#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle3pt", L("Click three points on the circle")},
|
||||
{"sk_arc_t", L("3-point arc"), 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc3pt", L("Arc — click start, end, then a point")},
|
||||
{"sk_arc_tangent", L("Tangent arc"), 0, nullptr, "fly:design_arc3pt#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_tangentarc", L("Click start (on the last entity) then end")},
|
||||
{"sk_arc_center", L("Center-point arc"), 0, nullptr, "fly:design_arc3pt#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc_center", L("Click center, then start, then a point for the end angle")},
|
||||
{"sk_slot", L("Slot"), 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot", L("Slot — two centerline ends, then the width")},
|
||||
{"sk_slot_arc", L("Arc slot"), 0, nullptr, "fly:design_slot#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot_arc", L("Click center, start, end, then a point for the width")},
|
||||
{"sk_ellipse", L("Ellipse"), 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse", L("Ellipse — center, major end, minor point")},
|
||||
{"sk_ellipse_arc", L("Elliptical arc"), 0, nullptr, "fly:design_ellipse#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse_arc", L("Click center, major-axis end, minor point, then arc start and end")},
|
||||
{"sk_spline", L("Spline"), 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_bspline", L("Spline — click control points")},
|
||||
{"sk_poly_3", L("Triangle"), 0, nullptr, "btn:poly#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Triangle — click center, then a vertex")},
|
||||
{"sk_poly_4", L("Square"), 0, nullptr, "btn:poly#4", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Square — click center, then a vertex")},
|
||||
{"sk_poly_5", L("Pentagon"), 0, nullptr, "btn:poly#5", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Pentagon — click center, then a vertex")},
|
||||
{"sk_polygon", L("Hexagon"), 0, "G", "btn:poly#6", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Hexagon — click center, then a vertex")},
|
||||
{"sk_poly_8", L("Octagon"), 0, nullptr, "btn:poly#8", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Octagon — click center, then a vertex")},
|
||||
{"sk_poly_12", L("Dodecagon"), 0, nullptr, "btn:poly#12", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Dodecagon — click center, then a vertex")},
|
||||
{"sk_poly_inscribed", L("Inscribed"), 0, nullptr, "btn:polyfit#0", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its corners (inscribed)")},
|
||||
{"sk_poly_circumscribed", L("Circumscribed"), 0, nullptr, "btn:polyfit#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its flats (circumscribed)")},
|
||||
{"sk_point_t", L("Point"), 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_point", L("Point — click to place")},
|
||||
{"sk_text", L("Text"), 0, nullptr, "btn:text", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_text", L("Type text; it becomes a Text feature on this sketch's plane, editable later")},
|
||||
{"sk_svg", L("SVG"), 0, nullptr, "btn:svg", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_svg", L("Import an SVG outline into this sketch as editable lines")},
|
||||
{"sk_offset", L("Offset"), 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_offset", L("Offset — pick an entity, drag the distance")},
|
||||
{"sk_trim", L("Trim"), 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_trim", L("Trim — click a segment to trim it")},
|
||||
{"sk_fillet", L("Fillet"), 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_filletedge", L("Fillet — pick two lines, set the radius")},
|
||||
{"sk_chamfer", L("Chamfer"), 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_chamfer", L("Chamfer — pick two lines, set the distance")},
|
||||
{"sk_array", L("Linear array"), 4, nullptr, "fly:design_array#0", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_array", L("Pick entities, drag the spacing handle, click the count; click empty to apply")},
|
||||
{"sk_array_polar", L("Polar array"), 4, nullptr, "fly:design_array#1", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_polararray", L("Pick entities, drag the sweep handle, click the count; click empty to apply")},
|
||||
{"sk_mirror", L("Mirror"), 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_mirror", L("Mirror — pick axis, then entities")},
|
||||
{"sk_move", L("Move"), 5, nullptr, "fly:design_move#0", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_move", L("Pick entities, then drag the handle or click the distance; click empty to apply")},
|
||||
{"sk_rotate", L("Rotate"), 5, nullptr, "fly:design_move#1", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_rotate", L("Pick entities, then drag around the pivot or click the angle; click empty to apply")},
|
||||
{"sk_scale", L("Scale"), 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_scale", L("Pick entities, then drag the handle or click the factor; click empty to apply")},
|
||||
{"sk_dimension", L("Dimension"), 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_dimension", L("Dimension — click 2 points or an entity")},
|
||||
{"sk_constrain", L("Constrain"), 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_constrain", L("Constrain the selected sketch entities to each other")},
|
||||
{"sketch", L_CONTEXT("Sketch", "Design"), 0, "Shift+S", "key:S+S", L("Click a face or a reference plane in the viewport, then a sketch tool"), 0x00000403u, 0, 0, false, false, nullptr, "design_sketch", L("Click a face or a reference plane, then pick a drawing tool")},
|
||||
{"extrude", L_CONTEXT("Extrude", "Design"), 1, "Shift+E", "key:S+E", L("Create a sketch, or pick a solid face, first"), 0x00004002u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch profile, or push/pull a picked face")},
|
||||
{"revolve", L_CONTEXT("Revolve", "Design"), 1, "Shift+R", "key:S+R", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a profile about an axis")},
|
||||
{"sweep", L_CONTEXT("Sweep", "Design"), 1, "Shift+W", "key:S+W", L("Create a profile sketch to sweep first"), 0x00004000u, 0, 2, false, false, nullptr, "design_sweep", L("Sweep a profile along a path")},
|
||||
{"loft", L_CONTEXT("Loft", "Design"), 1, "Shift+L", "key:S+L", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between two or more profiles")},
|
||||
{"thicken", L_CONTEXT("Thicken", "Design"), 1, nullptr, "fly:material#4", L("Thicken needs a solid body — add or import one first"), 0x0000000au, 1, 0, false, false, nullptr, "design_thicken", L("Offset a solid face into a thin plate (new body)")},
|
||||
{"rib", L_CONTEXT("Rib", "Design"), 1, "R", "fly:material#5", L("Rib needs a solid body — add or import one first"), 0x00010000u, 1, 0, false, false, nullptr, "design_rib", L("Grow a thin wall from an open sketch line, fused to a body")},
|
||||
{"boolean", L_CONTEXT("Join", "Design"), 1, "Shift+B", "btn:bool#0", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Fuse the tool body into the target — one solid, no seam")},
|
||||
{"bool_subtract", L_CONTEXT("Subtract", "Design"), 1, nullptr, "btn:bool#1", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Cut the tool body out of the target")},
|
||||
{"bool_intersect", L_CONTEXT("Intersect", "Design"), 1, nullptr, "btn:bool#2", L("Boolean needs two bodies — add or import a second one"), 0x00000200u, 2, 0, false, false, nullptr, "design_boolean", L("Keep only where the two bodies overlap")},
|
||||
{"surf_extrude", L_CONTEXT("Surface Extrude", "Design"), 1, "Shift+G", "key:S+G", L("Create a sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_extrude", L("Extrude a sketch into a sheet body (no end caps)")},
|
||||
{"surf_revolve", L_CONTEXT("Surface Revolve", "Design"), 1, nullptr, "fly:surface#1", L("Create a sketch profile to revolve first"), 0x00004000u, 0, 0, false, false, nullptr, "design_revolve", L("Revolve a sketch profile into a sheet body")},
|
||||
{"surf_loft", L_CONTEXT("Surface Loft", "Design"), 1, nullptr, "fly:surface#2", L("Create at least two profile sketches to loft"), 0x00004000u, 0, 2, false, false, nullptr, "design_loft", L("Loft (skin) between 2+ profiles, open (no end caps)")},
|
||||
{"surf_fill", L_CONTEXT("Surface Fill", "Design"), 1, nullptr, "fly:surface#3", L("Create a closed sketch first"), 0x00004000u, 0, 0, false, false, nullptr, "design_surface", L("Fill a sketch boundary with a smooth face")},
|
||||
{"thicken_surf", L_CONTEXT("Thicken Surface", "Design"), 1, nullptr, "fly:surface#5", L("Thicken Surface needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_thicken", L("Thicken a sheet body into a solid")},
|
||||
{"hole", L_CONTEXT("Hole", "Design"), 2, "Shift+H", "key:S+H", L("Pick a face or a plane to drill into"), 0x00000402u, 1, 0, false, false, nullptr, "design_hole", L("Drill a hole, centerd on a picked face or placed on a plane")},
|
||||
{"thread", L_CONTEXT("Thread", "Design"), 2, "Shift+T", "key:S+T", L("Pick a cylindrical surface (bore / outer) or a circular edge for a thread"), 0x00000024u, 1, 0, false, false, nullptr, "design_thread", L("Thread a cylindrical surface (inner bore / outer) or a circular edge")},
|
||||
{"shell", L_CONTEXT("Shell", "Design"), 2, "Shift+K", "key:S+K", L("Shell needs a solid body — add or import one first"), 0x00000082u, 1, 0, false, false, nullptr, "design_shell", L("Hollow the body to a wall thickness, opening a picked face")},
|
||||
{"cut", L_CONTEXT("Cut", "Design"), 2, "Shift+X", "key:S+X", L("Cut needs a solid body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_cut", L("Trim the body with a plane — drag the offset arrow; keep one half or both")},
|
||||
{"split", L_CONTEXT("Split", "Design"), 2, nullptr, nullptr, L("Split needs a solid body — add or import one first"), 0x000000feu, 1, 0, false, false, nullptr, nullptr, L("Split the body along a picked face into two solids")},
|
||||
{"fillet", L_CONTEXT("Fillet", "Design"), 3, "Shift+F", "btn:dress#0", L("Pick an edge to round"), 0x000000b2u, 1, 0, false, false, nullptr, "design_filletedge", L("Pick an edge, then drag the radius arrow or type it")},
|
||||
{"chamfer", L_CONTEXT("Chamfer", "Design"), 3, nullptr, "btn:dress#1", L("Pick an edge to bevel"), 0x000000b2u, 1, 0, false, false, nullptr, "design_chamfer", L("Pick an edge, then drag the distance arrow or type it")},
|
||||
{"draft", L_CONTEXT("Draft", "Design"), 3, "Shift+D", "key:S+D", L("Pick a face to taper"), 0x0000000au, 1, 0, false, false, nullptr, "design_draft", L("Tilt a picked face by a draft angle")},
|
||||
{"surf_offset", L_CONTEXT("Surface Offset", "Design"), 3, nullptr, "fly:surface#4", L("Surface Offset needs a surface body — make one with a Surface tool first"), 0x00000100u, 0, 0, true, false, nullptr, "design_offset", L("Offset a sheet body's shell by a signed distance")},
|
||||
{"pattern", L_CONTEXT("Linear pattern", "Design"), 4, "Shift+N", "btn:pat#0", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_array", L("Repeat the body along a direction — drag the spacing, set the count")},
|
||||
{"pattern_circular", L_CONTEXT("Circular pattern", "Design"), 4, nullptr, "btn:pat#1", L("Pattern needs a solid body — add or import one first"), 0x00006082u, 1, 0, false, false, nullptr, "design_polararray", L("Repeat the body around an axis — set the count and sweep")},
|
||||
{"mirror", L_CONTEXT("Mirror", "Design"), 4, "Shift+Z", "key:S+Z", L("Mirror needs a body — add or import one first"), 0x000004feu, 1, 0, false, false, nullptr, "design_mirror", L("Reflect a body about a plane")},
|
||||
{"pat_curve", L_CONTEXT("Pattern on Curve", "Design"), 4, nullptr, nullptr, L("Pattern on Curve needs a body and a curve"), 0x00000090u, 1, 0, false, false, nullptr, nullptr, L("Repeat the body along a picked curve")},
|
||||
{"transform", L_CONTEXT("Move", "Design"), 5, "Shift+Y", "key:S+Y", L("Transform needs a body — add or import one first"), 0x000021feu, 1, 0, false, false, nullptr, "design_move", L("Move and/or rotate an existing body")},
|
||||
{"mate", L_CONTEXT("Mate", "Design"), 5, nullptr, "fly:placement#2", L("Mate needs two coordinate systems — create them first"), 0x00001202u, 2, 0, false, false, nullptr, "design_c_coincident", L("Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)")},
|
||||
{"align", L_CONTEXT("Align to", "Design"), 5, nullptr, nullptr, L("Align needs a body — add or import one first"), 0x00000002u, 1, 0, false, false, nullptr, nullptr, L("Align the body to a picked face or plane")},
|
||||
{"plane", L_CONTEXT("Plane", "Design"), 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false, nullptr, "design_plane", L("Reference plane (offset / tilt / midplane / tangent / two edges / coincident)")},
|
||||
{"axis", L_CONTEXT("Axis", "Design"), 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false, nullptr, "design_line", L("Datum axis (two points, face normal, cylinder centerline, two planes, along edge)")},
|
||||
{"coordsys_v", L_CONTEXT("Coordinate system", "Design"), 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false, nullptr, "design_point", L("Datum coordinate system (world point, or face + direction edge)")},
|
||||
{"helix", L_CONTEXT("Helix", "Design"), 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false, nullptr, "design_thread", L("Helical curve (spring path) — use as a sweep path for coils / springs / augers")},
|
||||
{"project", L_CONTEXT("Project", "Design"), 6, nullptr, "fly:plane#4", L("Project needs a body — add or import one first"), 0x00000482u, 1, 0, false, false, nullptr, "design_sketch", L("Project body edges onto a plane as sketch entities")},
|
||||
{"measure", L_CONTEXT("Measure", "Design"), 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false, nullptr, nullptr, L("Measure between the picked points, edges or faces")},
|
||||
{"mass_props", L_CONTEXT("Volume and area", "Design"), 6, nullptr, "btn:mass", nullptr, 0x000000feu, 1, 0, false, false, nullptr, "info", L("Report the volume and surface area of the selected body")},
|
||||
{"interference", L_CONTEXT("Interference", "Design"), 6, nullptr, "btn:interference", L("Interference needs at least two bodies"), 0x00000280u, 2, 0, false, false, nullptr, nullptr, L("Check whether two bodies overlap — reports, changes nothing")},
|
||||
{"edit_feature", L_CONTEXT("Edit", "Design"), 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false, nullptr, "design_edit", L("Reopen the selected feature to change what it was made from")},
|
||||
{"rename", L_CONTEXT("Rename…", "Design"), 8, "F2", "btn:rename", L("Select a feature, or a body, to rename it"), 0x00004080u, 0, 0, false, false, nullptr, nullptr, L("Give this feature a name you will recognise in the tree (a body takes its name from the feature that makes it)")},
|
||||
{"delete_face", L_CONTEXT("Delete Face", "Design"), 7, nullptr, "fly:dressup#3", L("Delete Face needs a body — add or import one first"), 0x0000000eu, 1, 0, false, false, nullptr, "design_delete", L("Remove faces from a body and heal the solid")},
|
||||
{"colour", L_CONTEXT("Color", "Design"), 8, nullptr, "btn:colour", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "color_palette", L("Set the selected body's display color")},
|
||||
{"delete", L_CONTEXT("Delete", "Design"), 7, "Del", "btn:delete", nullptr, 0x000f7c00u, 0, 0, false, false, nullptr, "design_delete", L("Delete what is selected")},
|
||||
{"delete_body", L_CONTEXT("Delete Body", "Design"), 7, nullptr, "btn:delete_body", nullptr, 0x000001feu, 1, 0, false, false, nullptr, "design_delete", L("Delete this whole body — removes the feature it was made from")},
|
||||
{"sk_line_t", L_CONTEXT("Line", "Design"), 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_line", L("Line — click start, then end")},
|
||||
{"sk_polyline", L_CONTEXT("Polyline", "Design"), 0, nullptr, "fly:design_line#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Line"), "design_polyline", L("Click points; click the first point to close the loop, right-click to end it open")},
|
||||
{"sk_rect", L_CONTEXT("Corner rectangle", "Design"), 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect", L("Rectangle — click two opposite corners")},
|
||||
{"sk_rect_center", L_CONTEXT("Center rectangle", "Design"), 0, nullptr, "fly:design_rect#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_crect", L("Click center, then a corner")},
|
||||
{"sk_rect_oblique", L_CONTEXT("Oblique rectangle", "Design"), 0, nullptr, "fly:design_rect#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_oblique", L("Click two corners of one edge, then a point for the width")},
|
||||
{"sk_rect_rounded", L_CONTEXT("Rounded rectangle", "Design"), 0, nullptr, "fly:design_rect#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Rectangle"), "design_rect_rounded", L("Click two opposite corners, then a point for the corner radius")},
|
||||
{"sk_circle", L_CONTEXT("Center circle", "Design"), 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle", L("Circle — click center, then radius")},
|
||||
{"sk_circle_2pt", L_CONTEXT("2-point circle", "Design"), 0, nullptr, "fly:design_circle#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle2pt", L("Click two ends of the diameter")},
|
||||
{"sk_circle_3pt", L_CONTEXT("3-point circle", "Design"), 0, nullptr, "fly:design_circle#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Circle"), "design_circle3pt", L("Click three points on the circle")},
|
||||
{"sk_arc_t", L_CONTEXT("3-point arc", "Design"), 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc3pt", L("Arc — click start, end, then a point")},
|
||||
{"sk_arc_tangent", L_CONTEXT("Tangent arc", "Design"), 0, nullptr, "fly:design_arc3pt#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_tangentarc", L("Click start (on the last entity) then end")},
|
||||
{"sk_arc_center", L_CONTEXT("Center-point arc", "Design"), 0, nullptr, "fly:design_arc3pt#2", nullptr, 0x000f8000u, 0, 0, false, true, L("Arc"), "design_arc_center", L("Click center, then start, then a point for the end angle")},
|
||||
{"sk_slot", L_CONTEXT("Slot", "Design"), 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot", L("Slot — two centerline ends, then the width")},
|
||||
{"sk_slot_arc", L_CONTEXT("Arc slot", "Design"), 0, nullptr, "fly:design_slot#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Slot"), "design_slot_arc", L("Click center, start, end, then a point for the width")},
|
||||
{"sk_ellipse", L_CONTEXT("Ellipse", "Design"), 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse", L("Ellipse — center, major end, minor point")},
|
||||
{"sk_ellipse_arc", L_CONTEXT("Elliptical arc", "Design"), 0, nullptr, "fly:design_ellipse#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Ellipse"), "design_ellipse_arc", L("Click center, major-axis end, minor point, then arc start and end")},
|
||||
{"sk_spline", L_CONTEXT("Spline", "Design"), 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_bspline", L("Spline — click control points")},
|
||||
{"sk_poly_3", L_CONTEXT("Triangle", "Design"), 0, nullptr, "btn:poly#3", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Triangle — click center, then a vertex")},
|
||||
{"sk_poly_4", L_CONTEXT("Square", "Design"), 0, nullptr, "btn:poly#4", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Square — click center, then a vertex")},
|
||||
{"sk_poly_5", L_CONTEXT("Pentagon", "Design"), 0, nullptr, "btn:poly#5", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Pentagon — click center, then a vertex")},
|
||||
{"sk_polygon", L_CONTEXT("Hexagon", "Design"), 0, "G", "btn:poly#6", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Hexagon — click center, then a vertex")},
|
||||
{"sk_poly_8", L_CONTEXT("Octagon", "Design"), 0, nullptr, "btn:poly#8", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Octagon — click center, then a vertex")},
|
||||
{"sk_poly_12", L_CONTEXT("Dodecagon", "Design"), 0, nullptr, "btn:poly#12", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Dodecagon — click center, then a vertex")},
|
||||
{"sk_poly_inscribed", L_CONTEXT("Inscribed", "Design"), 0, nullptr, "btn:polyfit#0", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its corners (inscribed)")},
|
||||
{"sk_poly_circumscribed", L_CONTEXT("Circumscribed", "Design"), 0, nullptr, "btn:polyfit#1", nullptr, 0x000f8000u, 0, 0, false, true, L("Polygon"), "design_polygon", L("Measure the polygon to its flats (circumscribed)")},
|
||||
{"sk_point_t", L_CONTEXT("Point", "Design"), 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_point", L("Point — click to place")},
|
||||
{"sk_text", L_CONTEXT("Text", "Design"), 0, nullptr, "btn:text", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_text", L("Type text; it becomes a Text feature on this sketch's plane, editable later")},
|
||||
{"sk_svg", L_CONTEXT("SVG", "Design"), 0, nullptr, "btn:svg", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_svg", L("Import an SVG outline into this sketch as editable lines")},
|
||||
{"sk_offset", L_CONTEXT("Offset", "Design"), 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_offset", L("Offset — pick an entity, drag the distance")},
|
||||
{"sk_trim", L_CONTEXT("Trim", "Design"), 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_trim", L("Trim — click a segment to trim it")},
|
||||
{"sk_fillet", L_CONTEXT("Fillet", "Design"), 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_filletedge", L("Fillet — pick two lines, set the radius")},
|
||||
{"sk_chamfer", L_CONTEXT("Chamfer", "Design"), 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true, nullptr, "design_chamfer", L("Chamfer — pick two lines, set the distance")},
|
||||
{"sk_array", L_CONTEXT("Linear array", "Design"), 4, nullptr, "fly:design_array#0", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_array", L("Pick entities, drag the spacing handle, click the count; click empty to apply")},
|
||||
{"sk_array_polar", L_CONTEXT("Polar array", "Design"), 4, nullptr, "fly:design_array#1", nullptr, 0x000b0000u, 0, 0, false, true, L("Array"), "design_polararray", L("Pick entities, drag the sweep handle, click the count; click empty to apply")},
|
||||
{"sk_mirror", L_CONTEXT("Mirror", "Design"), 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_mirror", L("Mirror — pick axis, then entities")},
|
||||
{"sk_move", L_CONTEXT("Move", "Design"), 5, nullptr, "fly:design_move#0", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_move", L("Pick entities, then drag the handle or click the distance; click empty to apply")},
|
||||
{"sk_rotate", L_CONTEXT("Rotate", "Design"), 5, nullptr, "fly:design_move#1", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_rotate", L("Pick entities, then drag around the pivot or click the angle; click empty to apply")},
|
||||
{"sk_scale", L_CONTEXT("Scale", "Design"), 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, L("Move"), "design_scale", L("Pick entities, then drag the handle or click the factor; click empty to apply")},
|
||||
{"sk_dimension", L_CONTEXT("Dimension", "Design"), 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_dimension", L("Dimension — click 2 points or an entity")},
|
||||
{"sk_constrain", L_CONTEXT("Constrain", "Design"), 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_constrain", L("Constrain the selected sketch entities to each other")},
|
||||
// Same verb, model-mode vocabulary: offered when a SKETCH is selected (bit 14, SkLoop), the
|
||||
// state a user is in right after finishing one. Without this row the only way in was the
|
||||
// toolbar icon, and constraints read as absent — see the Onshape-comparison report.
|
||||
// It sits in the Reference row, the address the sketch-mode Constrain has: one verb, one row.
|
||||
{"constrain", L("Constrain sketch"), 6, nullptr, "btn:constrain", L("Select a sketch to constrain it"), 0x00004000u, 0, 1, false, false, nullptr, "design_constrain", L("Add dimensions and relations (coincident, tangent, parallel...) to the selected sketch")},
|
||||
{"sk_construct", L("Construction"), 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr, nullptr, L("Toggle construction: geometry that guides but is never built")},
|
||||
{"sk_extend", L("Extend"), 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_extend", L("Extend — click a line/arc to extend it")},
|
||||
{"sk_delete", L("Delete"), 7, "Del", "btn:sk_delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_delete", L("Delete the selected sketch entities")},
|
||||
{"constrain", L_CONTEXT("Constrain sketch", "Design"), 6, nullptr, "btn:constrain", L("Select a sketch to constrain it"), 0x00004000u, 0, 1, false, false, nullptr, "design_constrain", L("Add dimensions and relations (coincident, tangent, parallel...) to the selected sketch")},
|
||||
{"sk_construct", L_CONTEXT("Construction", "Design"), 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr, nullptr, L("Toggle construction: geometry that guides but is never built")},
|
||||
{"sk_extend", L_CONTEXT("Extend", "Design"), 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_extend", L("Extend — click a line/arc to extend it")},
|
||||
{"sk_delete", L_CONTEXT("Delete", "Design"), 7, "Del", "btn:sk_delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_delete", L("Delete the selected sketch entities")},
|
||||
// Typing the defining number of the element you pointed at. Three rows rather than one so
|
||||
// each names the quantity in the drawing-office word for THAT element; all three land on
|
||||
// the same handler, because dimension_kind() already resolves the quantity from the
|
||||
// selection. Without these, an element's own numbers were reachable only by arming the
|
||||
// Dimension tool and re-picking geometry that was already selected.
|
||||
{"sk_length", L("Length…"), 7, "V", "key:V", nullptr, 0x00010000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the length of this line")},
|
||||
{"sk_radius", L("Radius / diameter…"), 7, "V", "key:V", nullptr, 0x00020000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the radius of this arc, or the diameter of this circle")},
|
||||
{"sk_angdist", L("Angle / distance…"), 7, "V", "key:V", nullptr, 0x00080000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the angle between two lines, or the distance between the two picks")},
|
||||
{"sk_length", L_CONTEXT("Length…", "Design"), 7, "V", "key:V", nullptr, 0x00010000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the length of this line")},
|
||||
{"sk_radius", L_CONTEXT("Radius / diameter…", "Design"), 7, "V", "key:V", nullptr, 0x00020000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the radius of this arc, or the diameter of this circle")},
|
||||
{"sk_angdist", L_CONTEXT("Angle / distance…", "Design"), 7, "V", "key:V", nullptr, 0x00080000u, 0, 0, false, true, nullptr, "design_dimension", L("Type the angle between two lines, or the distance between the two picks")},
|
||||
};
|
||||
static const int kOfferVerbCount = 92;
|
||||
|
||||
|
||||
@@ -110,15 +110,6 @@
|
||||
#include "slic3r/GUI/Shortcuts.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
// English-only pin for the Design tab (see design-ux-contract): one lever
|
||||
// de-translates this whole TU so our strings never half-translate against the host's
|
||||
// localized chrome. Host UI still follows the app locale; only this tab is pinned EN.
|
||||
// GOTCHA: every _L(...) in this file must take a STRING LITERAL (FromUTF8 wants const char*).
|
||||
#ifdef _L
|
||||
#undef _L
|
||||
#endif
|
||||
#define _L(s) wxString::FromUTF8(s)
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Mesh -> B-rep import defaults (see GeometryEngine::mesh_to_brep).
|
||||
@@ -498,7 +489,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
}
|
||||
m_viewport->set_sketch_construction(m_construction->GetValue());
|
||||
set_status(StatusKind::Info, m_sketch_on.IsEmpty() ? hint
|
||||
: wxString::Format(_L("%s · on %s"), hint, m_sketch_on));
|
||||
: wxString::Format(_L("%s · plane: %s"), hint, m_sketch_on));
|
||||
};
|
||||
|
||||
// Sketch-tool shortcuts (single letters, active only while a sketch is open). Family tools
|
||||
@@ -731,11 +722,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
wxString where;
|
||||
const bool have_plane = sketch_plane_target(where);
|
||||
set_status(StatusKind::Info, have_plane
|
||||
? wxString::Format(_L("Sketching on %s — pick a tool"), where)
|
||||
? wxString::Format(_L("Sketch plane: %s — pick a tool"), where)
|
||||
: _L("Click a face or a reference plane in the viewport, then a sketch tool"));
|
||||
if (m_sketch_hint) { // the card must agree with the status line, not argue with it
|
||||
m_sketch_hint->SetLabel(have_plane
|
||||
? wxString::Format(_L("Drawing on %s.\nPick a tool, or press Menu for the list."), where)
|
||||
? wxString::Format(_L("Sketch plane: %s.\nPick a tool, or press Menu for the list."), where)
|
||||
: _L("Click a face or a reference plane, then a sketch tool."));
|
||||
m_sketch_hint->Refresh();
|
||||
m_cards->Layout();
|
||||
@@ -752,7 +743,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// Add material: every feature that grows new solid material — from a profile
|
||||
// (extrude/revolve/sweep/loft), from a face (thicken) or from a line (rib).
|
||||
feat_dropdown("material", "design_extrude", _L("Add material (extrude / revolve / sweep / loft / thicken / rib)"), {
|
||||
{"design_extrude", _L("Extrude"), _L("Extrude a sketch profile, or push/pull a picked face"),
|
||||
{"design_extrude", _L_CONTEXT("Extrude", "Design"), _L("Extrude a sketch profile, or push/pull a picked face"),
|
||||
[this] {
|
||||
// Onshape push/pull: an explicitly picked solid face (Face-level cycle, no loop
|
||||
// selected) is extruded as the profile — this takes priority over re-extruding an
|
||||
@@ -833,7 +824,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_sel_solid_face = -1;
|
||||
open_tool(Tool::Thicken); // syncs m_thicken_face_label from m_sel_solid_face
|
||||
}, 0},
|
||||
{"design_rib", _L("Rib"), _L("Grow a thin wall from an open sketch line, fused to a body"),
|
||||
{"design_rib", _L_CONTEXT("Rib", "Design"), _L("Grow a thin wall from an open sketch line, fused to a body"),
|
||||
[this] {
|
||||
if (m_doc.bodies.empty()) {
|
||||
set_status(StatusKind::Error, _L("Rib needs a solid body — add or import one first"));
|
||||
@@ -873,7 +864,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// shortcut at all, which also made its card unreachable to the rig.
|
||||
});
|
||||
|
||||
auto* b_pattern = icon_btn("design_pattern", _L("Pattern"));
|
||||
auto* b_pattern = icon_btn("design_pattern", _L_CONTEXT("Pattern", "Design"));
|
||||
std::function<void()> act_pattern = [this] {
|
||||
// Pattern replicates an existing body — needs at least one solid.
|
||||
if (m_doc.bodies.empty()) {
|
||||
@@ -979,7 +970,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
}, 0},
|
||||
// Project belongs here, not in Dress-up: it consumes a body but PRODUCES sketch
|
||||
// geometry, so it is reference/curve creation like the four above, not a finishing op.
|
||||
{"design_sketch", _L("Project"), _L("Project body edges onto a plane as sketch entities"),
|
||||
{"design_sketch", _L_CONTEXT("Project", "Design"), _L("Project body edges onto a plane as sketch entities"),
|
||||
[this] {
|
||||
if (m_doc.bodies.empty()) {
|
||||
set_status(StatusKind::Error, _L("Project needs a body — add or import one first"));
|
||||
@@ -1139,7 +1130,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
[this] { open_tool(Tool::Dressup); }, SHIFT('F')},
|
||||
{"design_draft", _L("Draft (taper a face)"), _L("Tilt a picked face by a draft angle"),
|
||||
[this] { open_tool(Tool::Draft); }, SHIFT('D')},
|
||||
{"design_shell", _L("Shell"), _L("Hollow the body to a wall thickness, opening a picked face"),
|
||||
{"design_shell", _L_CONTEXT("Shell", "Design"), _L("Hollow the body to a wall thickness, opening a picked face"),
|
||||
[this] { open_tool(Tool::Shell); }, SHIFT('K')},
|
||||
{"design_delete", _L("Delete Face"), _L("Remove faces from a body and heal the solid"),
|
||||
[this] {
|
||||
@@ -1165,7 +1156,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// Hole / Thread — drilling into a solid (both face-aware)
|
||||
feat_dropdown("hole", "design_hole", _L("Hole / thread"), {
|
||||
{"design_hole", _L("Hole"), _L("Drill a hole, centerd on a picked face or placed on a plane"),
|
||||
{"design_hole", _L("Hole"), _L("Drill a hole, centered on a picked face or placed on a plane"),
|
||||
[this] {
|
||||
// #2: drill on the picked solid face, centred on it (origin = face centroid,
|
||||
// normal = inward). Otherwise fall back to the plane dropdown. m_hole_x/y then
|
||||
@@ -1542,8 +1533,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
if (n > 0) {
|
||||
m_construction->SetValue(!m_construction->GetValue()); // the mode did not move
|
||||
set_status(StatusKind::Info, wxString::Format(
|
||||
_L("Converted %d entit%s between construction and real geometry"),
|
||||
n, n == 1 ? "y" : "ies"));
|
||||
_L_PLURAL("Converted %d entity between construction and real geometry",
|
||||
"Converted %d entities between construction and real geometry", n),
|
||||
n));
|
||||
return;
|
||||
}
|
||||
m_viewport->set_sketch_construction(m_construction->GetValue()); });
|
||||
@@ -1574,26 +1566,26 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
b->Bind(wxEVT_BUTTON, [this, type](wxCommandEvent&) { apply_constraint(type); });
|
||||
cadd(b);
|
||||
};
|
||||
cbtn("design_c_horizontal", _L("Horizontal"), SketchConstraintType::Horizontal);
|
||||
cbtn("design_c_vertical", _L("Vertical"), SketchConstraintType::Vertical);
|
||||
cbtn("design_c_parallel", _L("Parallel"), SketchConstraintType::Parallel);
|
||||
cbtn("design_c_perpendicular", _L("Perpendicular"), SketchConstraintType::Perpendicular);
|
||||
cbtn("design_c_coincident", _L("Coincident"), SketchConstraintType::Coincident);
|
||||
cbtn("design_c_equal", _L("Equal length"), SketchConstraintType::EqualLength);
|
||||
cbtn("design_c_equal_radius", _L("Equal radius"), SketchConstraintType::EqualRadius);
|
||||
cbtn("design_c_collinear", _L("Collinear"), SketchConstraintType::Collinear);
|
||||
cbtn("design_c_concentric", _L("Concentric"), SketchConstraintType::Concentric);
|
||||
cbtn("design_c_tangent", _L("Tangent"), SketchConstraintType::Tangent);
|
||||
cbtn("design_c_midpoint", _L("Midpoint"), SketchConstraintType::Midpoint);
|
||||
cbtn("design_c_symmetric", _L("Symmetric"), SketchConstraintType::Symmetric);
|
||||
cbtn("design_c_sym_v", _L("Symmetric about the vertical axis"), SketchConstraintType::SymmetricAboutY);
|
||||
cbtn("design_c_sym_h", _L("Symmetric about the horizontal axis"), SketchConstraintType::SymmetricAboutX);
|
||||
cbtn("design_c_angle", _L("Angle"), SketchConstraintType::Angle);
|
||||
cbtn("design_c_radius", _L("Radius"), SketchConstraintType::Radius);
|
||||
cbtn("design_c_diameter", _L("Diameter"), SketchConstraintType::Diameter);
|
||||
cbtn("design_c_fix", _L("Fix point (anchor in place)"), SketchConstraintType::Fix);
|
||||
cbtn("design_c_dist_x", _L("Horizontal distance"), SketchConstraintType::DistanceX);
|
||||
cbtn("design_c_dist_y", _L("Vertical distance"), SketchConstraintType::DistanceY);
|
||||
cbtn("design_c_horizontal", _L_CONTEXT("Horizontal", "Sketch Constraint"), SketchConstraintType::Horizontal);
|
||||
cbtn("design_c_vertical", _L_CONTEXT("Vertical", "Sketch Constraint"), SketchConstraintType::Vertical);
|
||||
cbtn("design_c_parallel", _L_CONTEXT("Parallel", "Sketch Constraint"), SketchConstraintType::Parallel);
|
||||
cbtn("design_c_perpendicular", _L_CONTEXT("Perpendicular", "Sketch Constraint"), SketchConstraintType::Perpendicular);
|
||||
cbtn("design_c_coincident", _L_CONTEXT("Coincident", "Sketch Constraint"), SketchConstraintType::Coincident);
|
||||
cbtn("design_c_equal", _L_CONTEXT("Equal length", "Sketch Constraint"), SketchConstraintType::EqualLength);
|
||||
cbtn("design_c_equal_radius", _L_CONTEXT("Equal radius", "Sketch Constraint"), SketchConstraintType::EqualRadius);
|
||||
cbtn("design_c_collinear", _L_CONTEXT("Collinear", "Sketch Constraint"), SketchConstraintType::Collinear);
|
||||
cbtn("design_c_concentric", _L_CONTEXT("Concentric", "Sketch Constraint"), SketchConstraintType::Concentric);
|
||||
cbtn("design_c_tangent", _L_CONTEXT("Tangent", "Sketch Constraint"), SketchConstraintType::Tangent);
|
||||
cbtn("design_c_midpoint", _L_CONTEXT("Midpoint", "Sketch Constraint"), SketchConstraintType::Midpoint);
|
||||
cbtn("design_c_symmetric", _L_CONTEXT("Symmetric", "Sketch Constraint"), SketchConstraintType::Symmetric);
|
||||
cbtn("design_c_sym_v", _L_CONTEXT("Symmetric about the vertical axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutY);
|
||||
cbtn("design_c_sym_h", _L_CONTEXT("Symmetric about the horizontal axis", "Sketch Constraint"), SketchConstraintType::SymmetricAboutX);
|
||||
cbtn("design_c_angle", _L_CONTEXT("Angle", "Sketch Constraint"), SketchConstraintType::Angle);
|
||||
cbtn("design_c_radius", _L_CONTEXT("Radius", "Sketch Constraint"), SketchConstraintType::Radius);
|
||||
cbtn("design_c_diameter", _L_CONTEXT("Diameter", "Sketch Constraint"), SketchConstraintType::Diameter);
|
||||
cbtn("design_c_fix", _L_CONTEXT("Fix point (anchor in place)", "Sketch Constraint"), SketchConstraintType::Fix);
|
||||
cbtn("design_c_dist_x", _L_CONTEXT("Horizontal distance", "Sketch Constraint"), SketchConstraintType::DistanceX);
|
||||
cbtn("design_c_dist_y", _L_CONTEXT("Vertical distance", "Sketch Constraint"), SketchConstraintType::DistanceY);
|
||||
// Trim/Extend are now standalone SKETCH scissors (Mode::Trim/Extend) in the sketch
|
||||
// toolbar, NOT Constrain buttons. The other edit ops (Mirror/Offset/Fillet/Chamfer/
|
||||
// Move/…) are first-class sketch tools too. Done constraining = the action-bar ✓.
|
||||
@@ -1860,9 +1852,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
mform->Add(spin_frame(m_move_dz), 0, wxEXPAND);
|
||||
|
||||
m_move_axis = make_combo(m_cards);
|
||||
m_move_axis->Append(_L("X"));
|
||||
m_move_axis->Append(_L("Y"));
|
||||
m_move_axis->Append(_L("Z"));
|
||||
m_move_axis->Append(_L_CONTEXT("X", "Axis"));
|
||||
m_move_axis->Append(_L_CONTEXT("Y", "Axis"));
|
||||
m_move_axis->Append(_L_CONTEXT("Z", "Axis"));
|
||||
m_move_axis->SetSelection(2);
|
||||
mform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotation axis")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
mform->Add(m_move_axis, 0, wxEXPAND);
|
||||
@@ -1894,7 +1886,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// --- Extrude dialog (consumes the selected sketch) ---
|
||||
m_box_extrude = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_extrude->Add(card_header(m_cards, "design_extrude", _L("Extrude"), m_hdr_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_extrude->Add(card_header(m_cards, "design_extrude", _L_CONTEXT("Extrude", "Design"), m_hdr_extrude), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_extrude->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
m_extrude_sketch_label = new wxStaticText(m_cards, wxID_ANY, _L("Sketch: —"));
|
||||
m_box_extrude->Add(m_extrude_sketch_label, 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
@@ -1912,9 +1904,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// End condition (order MUST match ExtrudeEnd: Blind/Symmetric/TwoSided/ThroughAll/
|
||||
// UpToFace/UpToVertex). Up-to-face uses the currently click-selected solid face.
|
||||
m_extrude_end = make_combo(m_cards);
|
||||
for (const char* s : { "Blind", "Symmetric", "Two-sided", "Through all",
|
||||
"Up to face", "Up to vertex" })
|
||||
m_extrude_end->Append(s);
|
||||
for (const char* s : { L("Blind"), L("Symmetric"), L("Two-sided"), L("Through all"),
|
||||
L("Up to face"), L("Up to vertex") })
|
||||
m_extrude_end->Append(_L(s));
|
||||
m_extrude_end->SetSelection(0);
|
||||
eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("End")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
eform->Add(m_extrude_end, 0, wxEXPAND);
|
||||
@@ -1931,8 +1923,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// Order is load-bearing: index maps to BooleanMode (New=0, Add=1, Cut=2, Intersect=3).
|
||||
// Labels use Onshape wording so the choice reads as the user thinks of it.
|
||||
m_mode->Append(_L("New body")); // separate coexisting solid
|
||||
m_mode->Append(_L("Join")); // fuse into the target body (was "Add")
|
||||
m_mode->Append(_L("Cut")); // subtract from the target body
|
||||
m_mode->Append(_L_CONTEXT("Join", "Boolean Mode")); // fuse into the target body (was "Add")
|
||||
m_mode->Append(_L_CONTEXT("Cut", "Boolean Mode")); // subtract from the target body
|
||||
m_mode->Append(_L("Intersect")); // keep only the overlap
|
||||
m_mode->SetSelection(0);
|
||||
eform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -1968,6 +1960,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_face_group = make_combo(m_cards);
|
||||
m_face_group->Append(_L("Top")); // index 0 -> FaceGroup::Top
|
||||
m_face_group->Append(_L("Bottom")); // 1 -> Bottom
|
||||
// TRN Fillet/Chamfer edge group used when no edge is picked: the side edges, as opposed to the top and bottom ones.
|
||||
m_face_group->Append(_L("Lateral")); // 2 -> Lateral
|
||||
m_face_group->Append(_L("All")); // 3 -> All
|
||||
m_face_group->SetSelection(3);
|
||||
@@ -1988,9 +1981,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
auto* hform = two_col_form();
|
||||
|
||||
m_hole_plane = make_combo(m_cards);
|
||||
m_hole_plane->Append(_L("XY"));
|
||||
m_hole_plane->Append(_L("XZ"));
|
||||
m_hole_plane->Append(_L("YZ"));
|
||||
m_hole_plane->Append(_L_CONTEXT("XY", "Axis"));
|
||||
m_hole_plane->Append(_L_CONTEXT("XZ", "Axis"));
|
||||
m_hole_plane->Append(_L_CONTEXT("YZ", "Axis"));
|
||||
m_hole_plane->SetSelection(0);
|
||||
// Picking a plane here is an explicit choice: drop any on-face hijack (a stale face pick
|
||||
// could keep m_hole_on_face true, so the dropdown was ignored and the hole drilled on the
|
||||
@@ -2041,9 +2034,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
auto* tform = two_col_form();
|
||||
|
||||
m_thread_plane = make_combo(m_cards);
|
||||
m_thread_plane->Append(_L("XY"));
|
||||
m_thread_plane->Append(_L("XZ"));
|
||||
m_thread_plane->Append(_L("YZ"));
|
||||
m_thread_plane->Append(_L_CONTEXT("XY", "Axis"));
|
||||
m_thread_plane->Append(_L_CONTEXT("XZ", "Axis"));
|
||||
m_thread_plane->Append(_L_CONTEXT("YZ", "Axis"));
|
||||
m_thread_plane->SetSelection(0);
|
||||
tform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Thread plane")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
tform->Add(m_thread_plane, 0, wxEXPAND);
|
||||
@@ -2129,8 +2122,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_revolve_mode = make_combo(m_cards);
|
||||
m_revolve_mode->Append(_L("New body")); // same four words as Extrude
|
||||
m_revolve_mode->Append(_L("Join"));
|
||||
m_revolve_mode->Append(_L("Cut"));
|
||||
m_revolve_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
|
||||
m_revolve_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
|
||||
m_revolve_mode->Append(_L("Intersect"));
|
||||
m_revolve_mode->SetSelection(0);
|
||||
rform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -2159,8 +2152,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_sweep_mode = make_combo(m_cards);
|
||||
m_sweep_mode->Append(_L("New body")); // same four words as Extrude
|
||||
m_sweep_mode->Append(_L("Join"));
|
||||
m_sweep_mode->Append(_L("Cut"));
|
||||
m_sweep_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
|
||||
m_sweep_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
|
||||
m_sweep_mode->Append(_L("Intersect"));
|
||||
m_sweep_mode->SetSelection(0);
|
||||
sform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -2172,14 +2165,14 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// --- Pattern (replicate the target body: linear or circular) ---
|
||||
m_box_pattern = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_pattern->Add(card_header(m_cards, "design_pattern", _L("Pattern"), m_hdr_pattern), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_pattern->Add(card_header(m_cards, "design_pattern", _L_CONTEXT("Pattern", "Design"), m_hdr_pattern), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_pattern->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
{
|
||||
auto* pform = two_col_form();
|
||||
|
||||
m_pattern_type = make_combo(m_cards);
|
||||
m_pattern_type->Append(_L("Linear"));
|
||||
m_pattern_type->Append(_L("Circular"));
|
||||
m_pattern_type->Append(_L_CONTEXT("Linear", "Design"));
|
||||
m_pattern_type->Append(_L_CONTEXT("Circular", "Design"));
|
||||
m_pattern_type->SetSelection(0);
|
||||
pform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Type")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
pform->Add(m_pattern_type, 0, wxEXPAND);
|
||||
@@ -2215,7 +2208,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
auto* bform = two_col_form();
|
||||
|
||||
m_bool_op = make_combo(m_cards);
|
||||
m_bool_op->Append(_L("Join")); // the Extrude result word; the offer row says the same
|
||||
m_bool_op->Append(_L_CONTEXT("Join", "Boolean Mode")); // the Extrude result word; the offer row says the same
|
||||
m_bool_op->Append(_L("Subtract"));
|
||||
m_bool_op->Append(_L("Intersect"));
|
||||
m_bool_op->SetSelection(0);
|
||||
@@ -2255,7 +2248,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// --- Cut (split a body with a plane): parameters only; ✓/✗ live on the ribbon ---
|
||||
m_box_cut = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_cut->Add(card_header(m_cards, "design_cut", _L("Cut"), m_hdr_cut), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_cut->Add(card_header(m_cards, "design_cut", _L_CONTEXT("Cut", "Design"), m_hdr_cut), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_cut->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
{
|
||||
auto* cform = two_col_form();
|
||||
@@ -2314,6 +2307,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_plane_base = make_combo(m_cards);
|
||||
populate_plane_choices(m_plane_base); // XY/XZ/YZ + any existing datum planes
|
||||
// TRN Plane tool: the reference plane (XY, XZ, YZ or a datum) the new plane is built from.
|
||||
plform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Base")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
plform->Add(m_plane_base, 0, wxEXPAND);
|
||||
|
||||
@@ -2371,8 +2365,8 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
m_loft_mode = make_combo(m_cards);
|
||||
m_loft_mode->Append(_L("New body")); // same four words as Extrude
|
||||
m_loft_mode->Append(_L("Join"));
|
||||
m_loft_mode->Append(_L("Cut"));
|
||||
m_loft_mode->Append(_L_CONTEXT("Join", "Boolean Mode"));
|
||||
m_loft_mode->Append(_L_CONTEXT("Cut", "Boolean Mode"));
|
||||
m_loft_mode->Append(_L("Intersect"));
|
||||
m_loft_mode->SetSelection(0);
|
||||
lform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Result")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -2515,7 +2509,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
sform->Add(m_shell_face_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_box_shell = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_shell->Add(card_header(m_cards, "design_shell", _L("Shell"), m_hdr_shell), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_shell->Add(card_header(m_cards, "design_shell", _L_CONTEXT("Shell", "Design"), m_hdr_shell), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_shell->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
m_box_shell->Add(new wxStaticText(m_cards, wxID_ANY,
|
||||
_L("Pick a solid face to open it, then set the wall thickness.")),
|
||||
@@ -2564,9 +2558,9 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Translate Z")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
xform->Add(spin_frame(m_xf_dz), 0, wxEXPAND);
|
||||
m_xf_axis = make_combo(m_cards);
|
||||
m_xf_axis->Append(_L("X"));
|
||||
m_xf_axis->Append(_L("Y"));
|
||||
m_xf_axis->Append(_L("Z"));
|
||||
m_xf_axis->Append(_L_CONTEXT("X", "Axis"));
|
||||
m_xf_axis->Append(_L_CONTEXT("Y", "Axis"));
|
||||
m_xf_axis->Append(_L_CONTEXT("Z", "Axis"));
|
||||
m_xf_axis->SetSelection(2); // default Z
|
||||
m_xf_axis->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { refresh_preview(); });
|
||||
xform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Rotate axis")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -2663,7 +2657,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// --- Rib (thin wall from an open sketch line, fused to a body) ---
|
||||
m_box_rib = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_rib->Add(card_header(m_cards, "design_rib", _L("Rib"), m_hdr_rib), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_rib->Add(card_header(m_cards, "design_rib", _L_CONTEXT("Rib", "Design"), m_hdr_rib), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_rib->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
{
|
||||
auto* rform = two_col_form();
|
||||
@@ -2699,7 +2693,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
|
||||
// --- Project (project body edges onto a plane as sketch entities) ---
|
||||
m_box_project = new wxBoxSizer(wxVERTICAL);
|
||||
m_box_project->Add(card_header(m_cards, "design_sketch", _L("Project"), m_hdr_project), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_project->Add(card_header(m_cards, "design_sketch", _L_CONTEXT("Project", "Design"), m_hdr_project), 0, wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
m_box_project->Add(new wxStaticLine(m_cards), 0, wxEXPAND | wxALL, 8);
|
||||
{
|
||||
auto* pform = two_col_form();
|
||||
@@ -2904,11 +2898,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_cs_x = make_spin(m_cards, 0.0, -100000.0, 100000.0);
|
||||
m_cs_y = make_spin(m_cards, 0.0, -100000.0, 100000.0);
|
||||
m_cs_z = make_spin(m_cards, 0.0, -100000.0, 100000.0);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("X", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_x), 0, wxEXPAND);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Y")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Y", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_y), 0, wxEXPAND);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("Z")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L_CONTEXT("Z", "Axis")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_z), 0, wxEXPAND);
|
||||
|
||||
auto cs_pick = [&](const wxString& label, ::Button*& btn, wxStaticText*& lbl, CoordSysPick target) {
|
||||
@@ -2937,11 +2931,11 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_cs_hx = make_spin(m_cards, 1.0, -100000.0, 100000.0);
|
||||
m_cs_hy = make_spin(m_cards, 0.0, -100000.0, 100000.0);
|
||||
m_cs_hz = make_spin(m_cards, 0.0, -100000.0, 100000.0);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint X")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction X")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_hx), 0, wxEXPAND);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint Y")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Y")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_hy), 0, wxEXPAND);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X hint Z")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(new wxStaticText(m_cards, wxID_ANY, _L("X direction Z")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
csform->Add(spin_frame(m_cs_hz), 0, wxEXPAND);
|
||||
|
||||
m_box_coordsys->Add(csform, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 12);
|
||||
@@ -3190,7 +3184,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// one click away on a row the user has just selected — the message points at it instead
|
||||
// of describing a problem with no way out.
|
||||
if (const std::string* why = (sel >= 0) ? mate_conflict_reason(sel) : nullptr) {
|
||||
set_status(StatusKind::Error, wxString::FromUTF8(*why) + _L(" — the eye suppresses this mate"));
|
||||
set_status(StatusKind::Error, wxString::Format(_L("%s. Click the eye icon to suppress this mate."), wxString::FromUTF8(*why)));
|
||||
} else if (sel >= 0 && sel < int(m_doc.features.size())) {
|
||||
// Name the two gestures the row supports, because neither is visible on it.
|
||||
set_status(StatusKind::Info, wxString::Format(
|
||||
@@ -3882,7 +3876,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
if (got && m_viewport) m_viewport->set_escalate_on_repick(true);
|
||||
}
|
||||
const int nb = int(m_doc.bodies.size());
|
||||
const wxString bodytag = (nb > 1) ? wxString::Format(_L("Body %d "), body + 1) : wxString();
|
||||
const bool bodytag = nb > 1;
|
||||
// Each sub-element line ends by naming the NEXT click (gem). Escalation to the
|
||||
// whole body is a gesture nothing on screen would otherwise reveal, and the status line
|
||||
// is the only surface that can teach it at the moment it applies. It REPLACES the old
|
||||
@@ -3894,13 +3888,22 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
// into the panel. Say "what applies to it", never "verbs" — that is this codebase's
|
||||
// word for a tool-offer entry, not a word the drawing office uses, and the plane and
|
||||
// bodies-list lines already say it the right way.
|
||||
set_status(level == 4 ? bodytag + _L("vertex selected — click again for the whole body")
|
||||
: level == 1 ? bodytag + _L("selected (whole body) — right-click for what applies to it")
|
||||
: level == 2 ? bodytag + wxString::Format(_L("face %d selected — right-click to push/pull it, or click again for the whole body"), face)
|
||||
: level == 3 && m_sel_solid_edges.size() > 1
|
||||
? bodytag + wxString::Format(_L_PLURAL("%zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all", "%zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all", m_sel_solid_edges.size()), m_sel_solid_edges.size())
|
||||
: level == 3 ? bodytag + wxString::Format(_L("edge %d selected — Shift+click to add edges, or click again for the whole body"), edge)
|
||||
: _L("Nothing selected"));
|
||||
set_status(level == 4 ? (bodytag ? wxString::Format(_L("Body %d: vertex selected — click again for the whole body"), body + 1)
|
||||
: _L("Vertex selected — click again for the whole body"))
|
||||
: level == 1 ? (bodytag ? wxString::Format(_L("Body %d selected (whole body) — right-click for what applies to it"), body + 1)
|
||||
: _L("Whole body selected — right-click for what applies to it"))
|
||||
: level == 2 ? (bodytag ? wxString::Format(_L("Body %d: face %d selected — right-click to push/pull it, or click again for the whole body"), body + 1, face)
|
||||
: wxString::Format(_L("Face %d selected — right-click to push/pull it, or click again for the whole body"), face))
|
||||
: level == 3 && m_sel_solid_edges.size() > 1
|
||||
? (bodytag ? wxString::Format(_L_PLURAL("Body %d: %zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
|
||||
"Body %d: %zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
|
||||
unsigned(m_sel_solid_edges.size())), body + 1, m_sel_solid_edges.size())
|
||||
: wxString::Format(_L_PLURAL("%zu edge selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
|
||||
"%zu edges selected — Shift+click adds or removes one, Fillet/Chamfer dresses them all",
|
||||
unsigned(m_sel_solid_edges.size())), m_sel_solid_edges.size()))
|
||||
: level == 3 ? (bodytag ? wxString::Format(_L("Body %d: edge %d selected — Shift+click to add edges, or click again for the whole body"), body + 1, edge)
|
||||
: wxString::Format(_L("Edge %d selected — Shift+click to add edges, or click again for the whole body"), edge))
|
||||
: _L("Nothing selected"));
|
||||
m_status->Refresh();
|
||||
});
|
||||
// A click on nothing drops a list row as it drops a pick. Not while a card is open: the
|
||||
@@ -3995,7 +3998,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
if (m_viewport && m_viewport->is_sketching())
|
||||
m_viewport->set_sketch_plane(plane_from_choice(m_ref_plane));
|
||||
}
|
||||
const char* nm = (base == 0) ? "XY" : (base == 1) ? "XZ" : (base == 2) ? "YZ" : "datum";
|
||||
const wxString nm = ref_plane_name(base);
|
||||
// Both halves named the TOOLBAR, which no longer carries either button: the tools
|
||||
// moved to the offer. Name the gesture that actually works in each mode, and say
|
||||
// what a plain click does, since the two are easy to confuse on a plane.
|
||||
@@ -4036,10 +4039,12 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
if (m_xf_angle) m_xf_angle->SetValue(sign * aa.angle() * 180.0 / M_PI);
|
||||
}
|
||||
const int nb = int(m_doc.bodies.size());
|
||||
const wxString tag = (nb > 1) ? wxString::Format(_L("Body %d "), body + 1) : wxString();
|
||||
const Vec3d t = xform.translation();
|
||||
set_status(StatusKind::Info, tag + wxString::Format(_L("placed (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
|
||||
t.x(), t.y(), t.z()));
|
||||
set_status(StatusKind::Info, nb > 1
|
||||
? wxString::Format(_L("Body %d placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
|
||||
body + 1, t.x(), t.y(), t.z())
|
||||
: wxString::Format(_L("Placed at (%.1f, %.1f, %.1f) mm — drag arrows to move, rings to rotate"),
|
||||
t.x(), t.y(), t.z()));
|
||||
});
|
||||
|
||||
// Fillet/Chamfer radius gizmo: dragging (or editing) the edge-anchored arrow writes the
|
||||
@@ -4126,7 +4131,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
set_status(error ? StatusKind::Error : StatusKind::Info, wxString::FromUTF8(msg));
|
||||
});
|
||||
m_viewport->set_on_sketch_exit_refused([this]() {
|
||||
set_status(StatusKind::Info, _L("Sketch kept — Finish to commit it, Cancel to discard"));
|
||||
set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
|
||||
});
|
||||
|
||||
// Ctrl+Z / Ctrl+Shift+Z (Ctrl+Y) from the viewport → feature-history undo/redo.
|
||||
@@ -4444,7 +4449,7 @@ void DesignPanel::apply_dof_status(int dof, bool ok, bool has_constraints)
|
||||
m_dof_status->SetLabel(_L("✓ Fully constrained"));
|
||||
} else if (dof > 0) {
|
||||
m_dof_status->SetForegroundColour(dp_ctl_text());
|
||||
m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), dof));
|
||||
m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", dof), dof));
|
||||
} else {
|
||||
m_dof_status->SetLabel(wxString());
|
||||
}
|
||||
@@ -4521,7 +4526,7 @@ void DesignPanel::set_ui_mode(UiMode m)
|
||||
if (sketching && m_sketch_banner_txt != nullptr)
|
||||
m_sketch_banner_txt->SetLabel(
|
||||
wxString::Format(_L("Editing: Sketch %d · N = look normal to the plane · "
|
||||
"Finish or Cancel in the toolbar"),
|
||||
"Confirm or Cancel in the toolbar"),
|
||||
m_feature_counter + 1));
|
||||
m_sketch_banner->Show(sketching);
|
||||
m_sketch_banner->GetParent()->Layout();
|
||||
@@ -4778,7 +4783,7 @@ void DesignPanel::text_dialog_done(bool accepted)
|
||||
void DesignPanel::on_import_svg()
|
||||
{
|
||||
wxFileDialog dlg(this, _L("Import SVG"), wxEmptyString, wxEmptyString,
|
||||
"SVG files (*.svg)|*.svg|All files|*.*",
|
||||
_L("SVG files (*.svg)|*.svg|All files|*.*"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
@@ -4890,7 +4895,7 @@ bool DesignPanel::recompute_guarded(const wxString& message)
|
||||
void DesignPanel::on_import_step()
|
||||
{
|
||||
wxFileDialog dlg(this, _L("Import STEP"), wxEmptyString, wxEmptyString,
|
||||
"STEP files (*.step;*.stp)|*.step;*.stp|All files|*.*",
|
||||
_L("STEP files (*.step;*.stp)|*.step;*.stp|All files|*.*"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
@@ -4938,7 +4943,9 @@ void DesignPanel::on_import_step()
|
||||
set_tree_selection(int(m_doc.features.size()) - 1);
|
||||
set_status_ok(); // canonical post-recompute viewport/pick/parts refresh
|
||||
set_status(StatusKind::Info, wxString::Format(
|
||||
_L("Imported %d solid(s) — pick a face or edge, then Fillet / Cut / Shell to modify"),
|
||||
_L_PLURAL("Imported %d solid — pick a face or edge, then Fillet / Cut / Shell to modify",
|
||||
"Imported %d solids — pick a face or edge, then Fillet / Cut / Shell to modify",
|
||||
unsigned(solids.size())),
|
||||
int(solids.size())));
|
||||
}
|
||||
|
||||
@@ -4949,7 +4956,7 @@ void DesignPanel::on_import_step()
|
||||
void DesignPanel::on_import_mesh()
|
||||
{
|
||||
wxFileDialog dlg(this, _L("Import mesh"), wxEmptyString, wxEmptyString,
|
||||
"Mesh files (*.stl;*.obj)|*.stl;*.obj|All files|*.*",
|
||||
_L("Mesh files (*.stl;*.obj)|*.stl;*.obj|All files|*.*"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
@@ -5032,8 +5039,8 @@ void DesignPanel::on_import_mesh()
|
||||
stats.kept_tris, stats.faces_final, stats.volume));
|
||||
} else {
|
||||
set_status(StatusKind::Warning, wxString::Format(
|
||||
_L("Imported as an open shell (not watertight): %d boundary edge(s), %d non-manifold "
|
||||
"edge(s) — %d triangles → %d faces. The source mesh has holes or duplicated "
|
||||
_L("Imported as an open shell (not watertight). Boundary edges: %d, non-manifold "
|
||||
"edges: %d, triangles: %d → faces: %d. The source mesh has holes or duplicated "
|
||||
"geometry; boolean features may fail on it"),
|
||||
stats.boundary_edges, stats.nonmanifold_edges, stats.kept_tris, stats.faces_final));
|
||||
}
|
||||
@@ -5144,7 +5151,7 @@ void DesignPanel::cancel_insert()
|
||||
set_ui_mode(UiMode::Feature);
|
||||
sync_sketch_display();
|
||||
refresh_tree();
|
||||
set_status(StatusKind::Info, _L("Insert cancelled"));
|
||||
set_status(StatusKind::Info, _L("Insert canceled"));
|
||||
}
|
||||
|
||||
void DesignPanel::on_transform_imported(int feat_idx)
|
||||
@@ -5171,7 +5178,7 @@ void DesignPanel::on_add_sketch()
|
||||
m_doc.add_sketch(shape, plane, m_width->GetValue(), m_height->GetValue(),
|
||||
m_radius->GetValue(), feature_name(_L("Sketch")));
|
||||
m_doc.recompute(); // a lone sketch yields an empty body; that is expected
|
||||
set_status(StatusKind::Info, wxString::Format(_L("Sketch added on %s — select it, then right-click to Extrude"), where));
|
||||
set_status(StatusKind::Info, wxString::Format(_L("Sketch added (plane: %s) — select it, then right-click to Extrude"), where));
|
||||
refresh_tree();
|
||||
}
|
||||
|
||||
@@ -5882,7 +5889,8 @@ void DesignPanel::on_check_interference()
|
||||
body_name(sorted[k].body_b), sorted[k].volume);
|
||||
if (sorted.size() > shown)
|
||||
list += wxString::FromUTF8(", …");
|
||||
set_status(StatusKind::Warning, wxString::Format(_L("%zu overlapping pairs: %s"), sorted.size(), list));
|
||||
set_status(StatusKind::Warning, wxString::Format(_L_PLURAL("%zu overlapping pair: %s", "%zu overlapping pairs: %s",
|
||||
unsigned(sorted.size())), sorted.size(), list));
|
||||
}
|
||||
|
||||
// Volume and surface area of the selected body. A report, not a feature: it never checkpoints,
|
||||
@@ -6082,7 +6090,7 @@ void DesignPanel::populate_plane_choices(ComboBox* c) const
|
||||
if (!c) return;
|
||||
const int keep = c->GetSelection();
|
||||
c->Clear();
|
||||
c->Append(_L("XY")); c->Append(_L("XZ")); c->Append(_L("YZ"));
|
||||
c->Append(_L_CONTEXT("XY", "Axis")); c->Append(_L_CONTEXT("XZ", "Axis")); c->Append(_L_CONTEXT("YZ", "Axis"));
|
||||
for (const auto& dp : m_doc.resolve_datum_planes())
|
||||
c->Append(wxString::FromUTF8(dp.first));
|
||||
c->SetSelection((keep >= 0 && keep < int(c->GetCount())) ? keep : 0);
|
||||
@@ -6131,8 +6139,8 @@ SketchPlane DesignPanel::sketch_plane_from_selection(wxString& what) const
|
||||
const int fi = (m_sel_solid_face >= 0 && m_sel_solid_body >= 0) ? m_sel_solid_face : m_pick_face;
|
||||
if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
|
||||
what = (m_doc.bodies.size() > 1)
|
||||
? wxString::Format(_L("the picked face of Body %d"), fb + 1)
|
||||
: _L("the picked face");
|
||||
? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
|
||||
: _L("Picked face");
|
||||
return p;
|
||||
}
|
||||
what = ref_plane_name(m_ref_plane);
|
||||
@@ -6150,8 +6158,8 @@ bool DesignPanel::sketch_plane_target(wxString& what) const
|
||||
SketchPlane p;
|
||||
if (fb >= 0 && fi >= 0 && m_doc.plane_of_face(fb, fi, p)) {
|
||||
what = (m_doc.bodies.size() > 1)
|
||||
? wxString::Format(_L("the picked face of Body %d"), fb + 1)
|
||||
: _L("the picked face");
|
||||
? wxString::Format(_L("Picked face (Body %d)"), fb + 1)
|
||||
: _L("Picked face");
|
||||
return true;
|
||||
}
|
||||
if (m_plane_picked) { what = ref_plane_name(m_ref_plane); return true; }
|
||||
@@ -6377,8 +6385,10 @@ static wxString sketch_step_prompt(DesignSketchTool::Mode m, int step, int picks
|
||||
switch (m) {
|
||||
case Mode::Select:
|
||||
return picks > 0
|
||||
? wxString::Format(_L("%d selected · Del removes them · Shift-click adds · "
|
||||
"double-click takes the whole loop"), picks)
|
||||
? wxString::Format(_L_PLURAL("%d selected · Del removes it · Shift-click adds · "
|
||||
"double-click takes the whole loop",
|
||||
"%d selected · Del removes them · Shift-click adds · "
|
||||
"double-click takes the whole loop", picks), picks)
|
||||
: _L("Select — click an entity to pick it · drag an endpoint or center to move it · "
|
||||
"Shift-click adds · Del removes");
|
||||
case Mode::Constrain:
|
||||
@@ -6627,8 +6637,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
||||
return (v.accepts & bit) && v.need_bodies <= bodies && v.need_sketches <= sketches
|
||||
&& (!v.need_sheet || sheet);
|
||||
};
|
||||
// Names and reasons live in the generated table as plain literals; they are the same strings
|
||||
// the toolbar already ships, so the catalogue already carries their translations.
|
||||
// Row names, verb names, refusals, families and hints are L()-marked in the generated table.
|
||||
// Scope the lookup to the APPLICATION catalog. A bare wxGetTranslation() searches every
|
||||
// loaded catalog, wxWidgets' own wxstd included — so on a non-English system the two row
|
||||
// names that happen to be wx standard strings came back translated while the other six,
|
||||
@@ -6642,7 +6651,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
||||
return wxGetTranslation(wxString::FromUTF8(s), SLIC3R_APP_KEY);
|
||||
};
|
||||
auto label = [&](const OfferVerb& v) {
|
||||
wxString s = tr(v.name);
|
||||
wxString s = I18N::translate(v.name, "Design");
|
||||
if (v.key && *v.key) s += "\t" + wxString::FromUTF8(v.key);
|
||||
return s;
|
||||
};
|
||||
@@ -7630,8 +7639,8 @@ void DesignPanel::flip_section_view()
|
||||
if (!m_viewport || !m_section_on) return;
|
||||
m_section_upper = !m_section_upper;
|
||||
m_viewport->set_section_plane(true, m_section_cut_z, m_section_upper);
|
||||
set_status(StatusKind::Info, wxString::Format(_L("Section view — showing the %s half"),
|
||||
m_section_upper ? _L("upper") : _L("lower")));
|
||||
set_status(StatusKind::Info, m_section_upper ? _L("Section view — showing the upper half")
|
||||
: _L("Section view — showing the lower half"));
|
||||
}
|
||||
|
||||
void DesignPanel::sync_body_visible()
|
||||
@@ -8295,8 +8304,10 @@ void DesignPanel::apply_live_constraint(SketchConstraintType type)
|
||||
// tool auto-selects only the entity it just drew — so after drawing two lines exactly
|
||||
// one is selected and the button correctly refuses. Say how many are picked, or the
|
||||
// refusal is indistinguishable from a dead button.
|
||||
fail(wxString::Format(_L("%s — %d selected. Press Esc for the Select tool, click one "
|
||||
"line, then Shift- or Ctrl-click the other."),
|
||||
fail(wxString::Format(_L_PLURAL("%s — %d selected. Press Esc for the Select tool, click one "
|
||||
"line, then Shift- or Ctrl-click the other.",
|
||||
"%s — %d selected. Press Esc for the Select tool, click one "
|
||||
"line, then Shift- or Ctrl-click the other.", unsigned(sel.size())),
|
||||
constraint_reject_text(plan.reason, type), int(sel.size())));
|
||||
return;
|
||||
case ConstraintPlan::Kind::AskValue:
|
||||
@@ -8364,7 +8375,7 @@ void DesignPanel::refresh_constrain_dof()
|
||||
m_dof_status->SetLabel(_L("✓ Fully constrained")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
|
||||
} else if (r.dof > 0) {
|
||||
m_dof_status->SetForegroundColour(dp_ctl_text());
|
||||
m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), r.dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
|
||||
m_dof_status->SetLabel(wxString::Format(_L_PLURAL("%d degree of freedom", "%d degrees of freedom", r.dof), r.dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
|
||||
} else {
|
||||
m_dof_status->SetLabel(wxString()); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty());
|
||||
}
|
||||
@@ -8398,7 +8409,8 @@ wxString DesignPanel::constraint_label(const SketchEntityConstraintDef& d) const
|
||||
// an empty string, or a NULL format string that crashes wxString::Format.
|
||||
wxString s; s << wxUniChar(c) << ei; // avoid %c assert in Unicode build
|
||||
if (r == SketchPointRole::P1) s += wxString::FromUTF8("·P1");
|
||||
else if (r == SketchPointRole::Center) s += wxString::FromUTF8("·Ctr");
|
||||
// TRN Design sketch: abbreviation of "center", appended to a point tag such as "L3·Ctr"
|
||||
else if (r == SketchPointRole::Center) s += _L("·Ctr");
|
||||
else if (r == SketchPointRole::P0) s += wxString::FromUTF8("·P0");
|
||||
return s;
|
||||
};
|
||||
@@ -8408,27 +8420,27 @@ wxString DesignPanel::constraint_label(const SketchEntityConstraintDef& d) const
|
||||
};
|
||||
switch (d.type) {
|
||||
case T::Fix: return wxString::Format(_L("Fix %s"), tag(d.ea, d.ra));
|
||||
case T::Coincident: return two(_L("Coincident"));
|
||||
case T::Horizontal: return two(_L("Horizontal"));
|
||||
case T::Vertical: return two(_L("Vertical"));
|
||||
case T::Coincident: return two(_L_CONTEXT("Coincident", "Sketch Constraint"));
|
||||
case T::Horizontal: return two(_L_CONTEXT("Horizontal", "Sketch Constraint"));
|
||||
case T::Vertical: return two(_L_CONTEXT("Vertical", "Sketch Constraint"));
|
||||
case T::Distance: return wxString::Format("%s = %s", two(_L("Distance")), en_format(d.value));
|
||||
case T::LockX: return wxString::Format(_L("Lock X %s"), tag(d.ea, d.ra));
|
||||
case T::LockY: return wxString::Format(_L("Lock Y %s"), tag(d.ea, d.ra));
|
||||
case T::EqualLength: return two(_L("Equal"));
|
||||
case T::Parallel: return two(_L("Parallel"));
|
||||
case T::Perpendicular: return two(_L("Perpendicular"));
|
||||
case T::Concentric: return two(_L("Concentric"));
|
||||
case T::Tangent: return two(_L("Tangent"));
|
||||
case T::Midpoint: return two(_L("Midpoint"));
|
||||
case T::EqualLength: return two(_L_CONTEXT("Equal", "Sketch Constraint"));
|
||||
case T::Parallel: return two(_L_CONTEXT("Parallel", "Sketch Constraint"));
|
||||
case T::Perpendicular: return two(_L_CONTEXT("Perpendicular", "Sketch Constraint"));
|
||||
case T::Concentric: return two(_L_CONTEXT("Concentric", "Sketch Constraint"));
|
||||
case T::Tangent: return two(_L_CONTEXT("Tangent", "Sketch Constraint"));
|
||||
case T::Midpoint: return two(_L_CONTEXT("Midpoint", "Sketch Constraint"));
|
||||
case T::Symmetric: return wxString::Format(_L("Symmetric %s — %s / %s"),
|
||||
tag(d.ea, d.ra), tag(d.eb, d.rb), tag(d.ec, d.rc));
|
||||
case T::SymmetricAboutY: return wxString::Format(_L("Symmetric about Y axis %s — %s"),
|
||||
tag(d.ea, d.ra), tag(d.eb, d.rb));
|
||||
case T::SymmetricAboutX: return wxString::Format(_L("Symmetric about X axis %s — %s"),
|
||||
tag(d.ea, d.ra), tag(d.eb, d.rb));
|
||||
case T::Angle: return wxString::Format(wxString::FromUTF8("%s = %s°"), two(_L("Angle")), en_format(d.value * 180.0 / M_PI, 1));
|
||||
case T::Radius: return wxString::Format("%s %s = %s", _L("Radius"), tag(d.ea, d.ra), en_format(d.value));
|
||||
case T::Diameter: return wxString::Format("%s %s = %s", _L("Diameter"), tag(d.ea, d.ra), en_format(d.value));
|
||||
case T::Angle: return wxString::Format(wxString::FromUTF8("%s = %s°"), two(_L_CONTEXT("Angle", "Sketch Constraint")), en_format(d.value * 180.0 / M_PI, 1));
|
||||
case T::Radius: return wxString::Format("%s %s = %s", _L_CONTEXT("Radius", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
|
||||
case T::Diameter: return wxString::Format("%s %s = %s", _L_CONTEXT("Diameter", "Sketch Constraint"), tag(d.ea, d.ra), en_format(d.value));
|
||||
case T::PointOnLine: return two(_L("On line"));
|
||||
case T::PointOnObject: return two(_L("On edge"));
|
||||
}
|
||||
@@ -9702,7 +9714,7 @@ void DesignPanel::load_feature_into_dialog(const CadFeature& f)
|
||||
wxString s;
|
||||
for (size_t i = 0; i < m_del_faces.size(); ++i) {
|
||||
if (i > 0) s += ", ";
|
||||
s += wxString::Format("Face %d", m_del_faces[i]);
|
||||
s += wxString::Format(_L("Face %d"), m_del_faces[i]);
|
||||
}
|
||||
m_del_face_list->SetLabel(s.empty() ? _L("(none)") : s);
|
||||
}
|
||||
@@ -9768,7 +9780,7 @@ void DesignPanel::on_edit_feature()
|
||||
m_viewport->set_display_sketches({});
|
||||
m_viewport->edit_sketch(f.entities, f.entity_constraints, f.plane);
|
||||
}
|
||||
set_status(StatusKind::Info, _L("Editing sketch — drag a handle or click a quote to edit"));
|
||||
set_status(StatusKind::Info, _L("Editing sketch — drag a handle or double-click a dimension to edit it"));
|
||||
} else {
|
||||
load_feature_into_dialog(f);
|
||||
open_tool(Tool::Sketch);
|
||||
@@ -9963,7 +9975,7 @@ void DesignPanel::on_export_step()
|
||||
return;
|
||||
}
|
||||
wxFileDialog dlg(this, _L("Export STEP"), wxEmptyString, "model.step",
|
||||
"STEP files (*.step;*.stp)|*.step;*.stp",
|
||||
_L("STEP files (*.step;*.stp)|*.step;*.stp"),
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
@@ -11480,20 +11492,20 @@ void DesignPanel::open_tool(Tool t)
|
||||
};
|
||||
switch (t) {
|
||||
case Tool::Sketch: m_hdr_sketch->SetLabel(title(_L("Sketch"))); break;
|
||||
case Tool::Extrude: m_hdr_extrude->SetLabel(title(_L("Extrude"))); break;
|
||||
case Tool::Extrude: m_hdr_extrude->SetLabel(title(_L_CONTEXT("Extrude", "Design"))); break;
|
||||
case Tool::Dressup: m_hdr_dressup->SetLabel(title(
|
||||
m_dressup_type->GetSelection() == 0 ? _L("Fillet") : _L("Chamfer"))); break;
|
||||
case Tool::Hole: m_hdr_hole->SetLabel(title(_L("Hole"))); break;
|
||||
case Tool::Thread: m_hdr_thread->SetLabel(title(_L("Thread"))); break;
|
||||
case Tool::Shell: m_hdr_shell->SetLabel(title(_L("Shell"))); break;
|
||||
case Tool::Shell: m_hdr_shell->SetLabel(title(_L_CONTEXT("Shell", "Design"))); break;
|
||||
case Tool::Revolve: m_hdr_revolve->SetLabel(title(_L("Revolve"))); break;
|
||||
case Tool::Sweep: m_hdr_sweep->SetLabel(title(_L("Sweep"))); break;
|
||||
case Tool::Pattern: m_hdr_pattern->SetLabel(title(_L("Pattern"))); break;
|
||||
case Tool::Pattern: m_hdr_pattern->SetLabel(title(_L_CONTEXT("Pattern", "Design"))); break;
|
||||
case Tool::Plane: m_hdr_plane->SetLabel(title(_L("Plane"))); break;
|
||||
case Tool::Loft: m_hdr_loft->SetLabel(title(_L("Loft"))); break;
|
||||
case Tool::Draft: m_hdr_draft->SetLabel(title(_L("Draft"))); break;
|
||||
case Tool::Boolean: m_hdr_boolean->SetLabel(title(_L("Boolean"))); break;
|
||||
case Tool::Cut: m_hdr_cut->SetLabel(title(_L("Cut"))); break;
|
||||
case Tool::Cut: m_hdr_cut->SetLabel(title(_L_CONTEXT("Cut", "Design"))); break;
|
||||
case Tool::Axis: m_hdr_axis->SetLabel(title(_L("Axis"))); break;
|
||||
case Tool::CoordSys: m_hdr_coordsys->SetLabel(title(_L("Coordinate system"))); break;
|
||||
case Tool::SurfaceExtrude: m_hdr_surf_extrude->SetLabel(title(_L("Surface Extrude"))); break;
|
||||
@@ -11505,8 +11517,8 @@ void DesignPanel::open_tool(Tool t)
|
||||
case Tool::Transform: m_hdr_transform->SetLabel(title(_L("Transform"))); break;
|
||||
case Tool::Mirror: m_hdr_mirror->SetLabel(title(_L("Mirror"))); break;
|
||||
case Tool::Thicken: m_hdr_thicken->SetLabel(title(_L("Thicken"))); break;
|
||||
case Tool::Rib: m_hdr_rib->SetLabel(title(_L("Rib"))); break;
|
||||
case Tool::Project: m_hdr_project->SetLabel(title(_L("Project"))); break;
|
||||
case Tool::Rib: m_hdr_rib->SetLabel(title(_L_CONTEXT("Rib", "Design"))); break;
|
||||
case Tool::Project: m_hdr_project->SetLabel(title(_L_CONTEXT("Project", "Design"))); break;
|
||||
case Tool::DeleteFace: m_hdr_delete_face->SetLabel(title(_L("Delete Face"))); break;
|
||||
case Tool::Helix: m_hdr_helix->SetLabel(title(_L("Helix"))); break;
|
||||
case Tool::Mate: m_hdr_mate->SetLabel(title(_L("Mate"))); break;
|
||||
@@ -11743,7 +11755,7 @@ void DesignPanel::tool_cancel()
|
||||
show_move_card(false);
|
||||
feed_bodies(); // re-render the reverted placement
|
||||
update_action_bar();
|
||||
set_status(StatusKind::Info, _L("Move cancelled"));
|
||||
set_status(StatusKind::Info, _L("Move canceled"));
|
||||
return;
|
||||
}
|
||||
if (m_active == Tool::Insert) { cancel_insert(); return; }
|
||||
@@ -11873,7 +11885,7 @@ void DesignPanel::escape()
|
||||
return;
|
||||
}
|
||||
if (m_ui_mode == UiMode::Sketch) {
|
||||
set_status(StatusKind::Info, _L("Sketch kept — Finish to commit it, Cancel to discard"));
|
||||
set_status(StatusKind::Info, _L("Sketch kept — Confirm to commit it, Cancel to discard"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -11956,8 +11968,10 @@ void DesignPanel::do_undo_redo(bool redo)
|
||||
drop_selection();
|
||||
reset_edit_state();
|
||||
after_tree_edit(true); // refresh tree + viewport meshes + status from the restored doc
|
||||
set_status(StatusKind::Info, wxString::Format(redo ? _L("Redo (%zu more)") : _L("Undo (%zu more)"),
|
||||
redo ? m_doc.redo_depth() : m_doc.undo_depth()));
|
||||
const size_t depth = redo ? m_doc.redo_depth() : m_doc.undo_depth();
|
||||
set_status(StatusKind::Info, wxString::Format(redo ? _L_PLURAL("Redo (%zu more step)", "Redo (%zu more steps)", unsigned(depth))
|
||||
: _L_PLURAL("Undo (%zu more step)", "Undo (%zu more steps)", unsigned(depth)),
|
||||
depth));
|
||||
}
|
||||
|
||||
// --- Document variables panel ---------------------------------------------------------
|
||||
|
||||
@@ -1725,6 +1725,7 @@ void DesignSketchTool::open_polygon_side_editor(int fi)
|
||||
if (f.begin < 0 || f.begin >= int(m_entities.size())) return;
|
||||
const double side = (m_entities[f.begin].p1 - m_entities[f.begin].p0).norm();
|
||||
const wxPoint px(m_last_mouse_x, m_last_mouse_y);
|
||||
// TRN Design sketch: length of one side of a regular polygon (inline value editor title)
|
||||
on_inline_edit(px, side, _u8L("Side"),
|
||||
[this, fi](double v) { set_polygon_side(fi, v); },
|
||||
[]() {});
|
||||
@@ -9844,7 +9845,7 @@ std::string DesignSketchTool::build_readout() const
|
||||
std::string out = b;
|
||||
// Tell the user how to end a polyline chain — there's no other affordance for it.
|
||||
if (m_mode == Mode::Polyline && m_points.size() >= 2)
|
||||
out += " right-click or double-click to finish, click start to close";
|
||||
out += " " + _u8L("right-click or double-click to finish, click start to close");
|
||||
return out;
|
||||
}
|
||||
if (!m_active) return std::string();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Widgets/DialogButtons.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <slic3r/GUI/DeviceManager.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "libslic3r/calib.hpp"
|
||||
@@ -745,9 +746,15 @@ wxArrayString NewCalibrationHistoryDialog::get_all_filaments(const MachineObject
|
||||
std::string nozzle_diameter_str = stream.str();
|
||||
|
||||
if (preset_bundle) {
|
||||
// OrcaSonar's model id is optional; the helper falls back to the selected profile
|
||||
// so the list is never empty.
|
||||
const std::string filament_printer_model = resolve_filament_printer_model(obj->printer_type, preset_bundle);
|
||||
if (filament_printer_model.empty())
|
||||
return filament_items;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << preset_bundle->filaments.size();
|
||||
for (Preset *filament_it : preset_bundle->get_filament_presets_for_machine(
|
||||
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
filament_printer_model, nozzle_diameter_str, obj->is_support_user_preset)) {
|
||||
if (!filament_id_set.insert(filament_it->filament_id).second)
|
||||
continue;
|
||||
const std::string alias = preset_bundle->filaments.get_preset_alias(*filament_it, true);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "CalibUtils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
@@ -67,13 +68,13 @@ static wxString get_default_name(wxString filament_name, CalibMode mode){
|
||||
case Slic3r::CalibMode::Calib_PA_Tower:
|
||||
break;
|
||||
case Slic3r::CalibMode::Calib_Flow_Rate:
|
||||
filament_name += " Flow Rate Calibrated";
|
||||
filament_name = format_wxstr(_L("%1% Flow Rate Calibrated"), filament_name);
|
||||
break;
|
||||
case Slic3r::CalibMode::Calib_Temp_Tower:
|
||||
filament_name += " Temperature Calibrated";
|
||||
filament_name = format_wxstr(_L("%1% Temperature Calibrated"), filament_name);
|
||||
break;
|
||||
case Slic3r::CalibMode::Calib_Vol_speed_Tower:
|
||||
filament_name += " Max Vol Speed Calibrated";
|
||||
filament_name = format_wxstr(_L("%1% Max Vol Speed Calibrated"), filament_name);
|
||||
break;
|
||||
case Slic3r::CalibMode::Calib_VFA_Tower:
|
||||
break;
|
||||
@@ -95,7 +96,7 @@ static wxString get_tray_name_by_tray_id(int tray_id)
|
||||
{
|
||||
wxString tray_name;
|
||||
if (tray_id == VIRTUAL_TRAY_MAIN_ID || tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
tray_name = "Ext";
|
||||
tray_name = _L("Ext");
|
||||
}
|
||||
else {
|
||||
int ams_id = tray_id / 4;
|
||||
|
||||
@@ -777,6 +777,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
|
||||
// sparse_infill_filament_id uses the same logic as in Print::extruders()
|
||||
for (auto el : { "sparse_infill_pattern", "infill_combination", "fill_multiline","infill_direction",
|
||||
"infill_complete_top",
|
||||
"minimum_sparse_infill_area", "sparse_infill_filament_id","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
|
||||
toggle_line(el, have_infill);
|
||||
|
||||
@@ -831,6 +832,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
|
||||
toggle_line("symmetric_infill_y_axis", is_zig_zag || is_cross_zag || is_locked_zig);
|
||||
|
||||
bool is_3Dhoneycomb = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value == InfillPattern::ip3DHoneycomb;
|
||||
toggle_line("infill_complete_top", have_infill && is_3Dhoneycomb);
|
||||
|
||||
bool has_spiral_vase = config->opt_bool("spiral_mode");
|
||||
toggle_line("spiral_mode_smooth", has_spiral_vase);
|
||||
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
|
||||
@@ -840,7 +844,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
bool has_top_shell = has_top_shell_layers && config->option<ConfigOptionPercent>("top_surface_density")->value > 0;
|
||||
bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0;
|
||||
bool has_solid_infill = has_top_shell_layers || has_bottom_shell;
|
||||
toggle_line("sparse_infill_smooth_factor", is_smoothable_infill_pattern(pattern, config->opt_int("fill_multiline")));
|
||||
toggle_line("sparse_infill_smooth_factor", have_infill && is_smoothable_infill_pattern(pattern, config->opt_int("fill_multiline")));
|
||||
toggle_field("top_surface_pattern", has_top_shell);
|
||||
toggle_field("bottom_surface_pattern", has_bottom_shell);
|
||||
toggle_field("top_surface_density", has_top_shell_layers);
|
||||
|
||||
@@ -53,6 +53,8 @@ wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConf
|
||||
}
|
||||
return _L("Undefined");
|
||||
}
|
||||
if (val < 0 || val >= int(names.size()))
|
||||
return _L("Undefined");
|
||||
return from_u8(_utf8(names[val]));
|
||||
}
|
||||
|
||||
@@ -215,6 +217,8 @@ wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig&
|
||||
;
|
||||
}
|
||||
case coEnums: {
|
||||
if (!opt_vector || opt_idx >= opt_vector->size())
|
||||
return _L("Undefined");
|
||||
return get_string_from_enum(pure_key, config,
|
||||
pure_key == "top_surface_pattern" ||
|
||||
pure_key == "bottom_surface_pattern" ||
|
||||
|
||||
@@ -1130,7 +1130,7 @@ void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool mat
|
||||
for (const auto& item : prusa_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
for (const auto& item : other_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
list->append(item.get() == Materials::UNKNOWN ? _L(item.get()) : from_u8(item.get()), &const_cast<std::string&>(item.get()));
|
||||
}
|
||||
|
||||
void PageMaterials::sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data)
|
||||
@@ -1210,13 +1210,13 @@ void PageMaterials::on_activate()
|
||||
}
|
||||
|
||||
|
||||
const char *PageCustom::default_profile_name = "My Settings";
|
||||
const char *PageCustom::default_profile_name = L("My Settings");
|
||||
|
||||
PageCustom::PageCustom(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _L("Custom Printer Setup"), _L("Custom Printer"))
|
||||
{
|
||||
cb_custom = new wxCheckBox(this, wxID_ANY, _L("Define a custom printer profile"));
|
||||
tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name);
|
||||
tc_profile_name = new wxTextCtrl(this, wxID_ANY, _L(default_profile_name));
|
||||
auto *label = new wxStaticText(this, wxID_ANY, _L("Custom profile name:"));
|
||||
|
||||
wxGetApp().UpdateDarkUI(tc_profile_name);
|
||||
@@ -1224,7 +1224,7 @@ PageCustom::PageCustom(ConfigWizard *parent)
|
||||
tc_profile_name->Enable(false);
|
||||
tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
|
||||
if (tc_profile_name->GetValue().IsEmpty()) {
|
||||
if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(default_profile_name); }
|
||||
if (profile_name_prev.IsEmpty()) { tc_profile_name->SetValue(_L(default_profile_name)); }
|
||||
else { tc_profile_name->SetValue(profile_name_prev); }
|
||||
} else {
|
||||
profile_name_prev = tc_profile_name->GetValue();
|
||||
@@ -1463,7 +1463,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
auto *default_bed = def_bed.get_default_value<ConfigOptionInts>();
|
||||
spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
|
||||
|
||||
append_text(_L("Enter the nozzle_temperature needed for extruding your filament."));
|
||||
append_text(_L("Enter the nozzle temperature needed for extruding your filament."));
|
||||
append_text(_L("A rule of thumb is 160 to 230℃ for PLA, and 215 to 250℃ for ABS."));
|
||||
#endif
|
||||
|
||||
@@ -1720,7 +1720,7 @@ void ConfigWizardIndex::msw_rescale()
|
||||
|
||||
// Materials
|
||||
|
||||
const std::string Materials::UNKNOWN = "(Unknown)";
|
||||
const std::string Materials::UNKNOWN = L("(Unknown)");
|
||||
|
||||
void Materials::push(const Preset *preset)
|
||||
{
|
||||
@@ -2188,8 +2188,8 @@ void ConfigWizard::priv::select_default_materials_for_printer_models(Technology
|
||||
}
|
||||
printer_names += "\n\n";
|
||||
std::string message = (technology & T_FFF ?
|
||||
GUI::format(_L("Following printer profiles has no default filament: %1%Please select one manually."), printer_names) :
|
||||
GUI::format(_L("Following printer profiles has no default material: %1%Please select one manually."), printer_names));
|
||||
GUI::format(_L("The following printer profiles have no default filament: %1%Please select one manually."), printer_names) :
|
||||
GUI::format(_L("The following printer profiles have no default material: %1%Please select one manually."), printer_names));
|
||||
MessageDialog msg(q, message, _L("Notice"), wxOK);
|
||||
msg.ShowModal();
|
||||
}
|
||||
@@ -2449,7 +2449,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
}
|
||||
if (!check_unsaved_preset_changes)
|
||||
if ((check_unsaved_preset_changes = install_bundles.size() > 0))
|
||||
header = _L_PLURAL("A new vendor was installed and one of its printers will be activated", "New vendors were installed and one of theirs printers will be activated", install_bundles.size());
|
||||
header = _L_PLURAL("A new vendor was installed and one of its printers will be activated", "New vendors were installed and one of their printers will be activated", install_bundles.size());
|
||||
|
||||
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
||||
bool snapshot = true;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "GUI.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include <wx/types.h>
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@@ -39,7 +39,7 @@ CrealityDiscoveryDialog::CrealityDiscoveryDialog(wxWindow* parent)
|
||||
m_list->AppendColumn(_L("Hostname"), wxLIST_FORMAT_LEFT, 14 * em);
|
||||
m_list->AppendColumn(_L("IP"), wxLIST_FORMAT_LEFT, 14 * em);
|
||||
|
||||
auto* dlg_btns = new DialogButtons(this, {"Scan", "OK", "Cancel"}, "", 1 /*left_aligned*/);
|
||||
auto* dlg_btns = new DialogButtons(this, {L("Scan"), "OK", "Cancel"}, "", 1 /*left_aligned*/);
|
||||
auto* ok_btn = dlg_btns->GetOK();
|
||||
ok_btn->SetLabel(_L("Use Selected"));
|
||||
ok_btn->Disable();
|
||||
@@ -91,7 +91,7 @@ void CrealityDiscoveryDialog::run_discovery()
|
||||
if (!h.model_name.empty())
|
||||
row.model = h.model_name;
|
||||
else if (h.cfs_capable)
|
||||
row.model = "(unknown K-series)";
|
||||
row.model = _u8L("(unknown K-series)");
|
||||
else
|
||||
row.model = "Creality";
|
||||
m_rows.push_back(std::move(row));
|
||||
|
||||
@@ -652,7 +652,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
||||
|
||||
wxString text = _L("Desktop Integration sets this binary to be searchable by the system.\n\nPress \"Perform\" to proceed.");
|
||||
if (can_undo)
|
||||
text += "\nPress \"Undo\" to remove previous integration.";
|
||||
text += "\n" + _L("Press \"Undo\" to remove previous integration.");
|
||||
|
||||
vbox->Add(
|
||||
new wxStaticText( this, wxID_ANY, text),
|
||||
|
||||
@@ -154,7 +154,7 @@ void calib_fail_message(MachineObject* obj, std::string cali_mode, std::string r
|
||||
} else if (reason == "nozzle_diameter is not matched") {
|
||||
info = _L("Selected diameter and machine diameter do not match");
|
||||
} else if (reason == "generate auto filament cali gcode failure") {
|
||||
info = _L("Failed to generate cali gcode");
|
||||
info = _L("Failed to generate calibration G-code");
|
||||
} else {
|
||||
info = wxString(reason);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
@@ -8,6 +9,7 @@
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
@@ -20,6 +22,7 @@
|
||||
#include "DevFilaSystem.h"
|
||||
#include "json_diff.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp" // ORCA_PRINTER_AGENT_ID
|
||||
#include "DevNozzleSystem.h" // DevNozzle / DevNozzleSystem for GetNozzleFlowStringByAmsId
|
||||
|
||||
// TODO: remove this include
|
||||
@@ -62,6 +65,15 @@ void DevAmsTray::UpdateColorFromStr(const std::string& color)
|
||||
}
|
||||
}
|
||||
|
||||
void DevAmsTray::UpdateEmptyState(bool material_fields_present, bool classify_empty)
|
||||
{
|
||||
const auto is_zero_or_empty = [](const std::string& value) {
|
||||
return value.empty() || std::all_of(value.begin(), value.end(), [](char c) { return c == '0'; });
|
||||
};
|
||||
is_empty = material_fields_present && classify_empty && setting_id.empty() && m_fila_type.empty() &&
|
||||
is_zero_or_empty(color) && is_zero_or_empty(tag_uid);
|
||||
}
|
||||
|
||||
void DevAmsTray::reset()
|
||||
{
|
||||
tag_uid = "";
|
||||
@@ -83,6 +95,7 @@ void DevAmsTray::reset()
|
||||
k = 0.0f;
|
||||
n = 0.0f;
|
||||
is_bbl = false;
|
||||
is_empty = false;
|
||||
hold_count = 0;
|
||||
remain = 0;
|
||||
}
|
||||
@@ -682,7 +695,7 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
//std::string type = (*tray_it)["tray_type"].get<std::string>();
|
||||
std::string type = MachineObject::setting_id_to_type(curr_tray->setting_id, (*tray_it)["tray_type"].get<std::string>());
|
||||
// curr_tray->setting_id is our OF id; GFS00/GFS01 are the printer's own support-filament ids.
|
||||
auto* agent = GUI::wxGetApp().getAgent();
|
||||
auto* agent = wxTheApp != nullptr ? GUI::wxGetApp().getAgent() : nullptr;
|
||||
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(curr_tray->setting_id) : curr_tray->setting_id;
|
||||
if (printer_filament_id == "GFS00")
|
||||
{
|
||||
@@ -782,6 +795,8 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
{
|
||||
curr_tray->remain = -1;
|
||||
}
|
||||
curr_tray->UpdateEmptyState(tray_it->contains("tray_info_idx") && tray_it->contains("tray_type"),
|
||||
obj->printer_agent_id == ORCA_PRINTER_AGENT_ID);
|
||||
// The tray objects are reused across status updates. Reset this
|
||||
// state when a previously empty slot receives a filament again.
|
||||
curr_tray->is_slot_placeholder = tray_it->contains("tray_slot_placeholder");
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
wxColour wx_color;
|
||||
bool is_bbl;
|
||||
bool is_exists = false;
|
||||
bool is_empty = false; // Explicitly reported as having no filament.
|
||||
bool is_slot_placeholder = false; // Orca: True for empty tray slots from pull-mode agents
|
||||
int hold_count = 0;
|
||||
int remain = 0; // filament remain: 0 ~ 100
|
||||
@@ -91,13 +92,15 @@ public:
|
||||
// operators
|
||||
bool operator==(DevAmsTray const& o) const
|
||||
{
|
||||
return id == o.id && m_fila_type == o.m_fila_type && filament_setting_id == o.filament_setting_id && color == o.color;
|
||||
return id == o.id && m_fila_type == o.m_fila_type && filament_setting_id == o.filament_setting_id &&
|
||||
color == o.color && is_empty == o.is_empty;
|
||||
}
|
||||
bool operator!=(DevAmsTray const& o) const { return !operator==(o); }
|
||||
|
||||
// setters
|
||||
void reset();
|
||||
void UpdateColorFromStr(const std::string& color);
|
||||
void UpdateEmptyState(bool material_fields_present, bool classify_empty);
|
||||
void set_hold_count() { hold_count = HOLD_COUNT_MAX; }
|
||||
|
||||
// getter
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <wx/app.h>
|
||||
#include <wx/colour.h>
|
||||
#include <tuple>
|
||||
#include <wx/dir.h>
|
||||
@@ -431,6 +432,32 @@ NozzleVolumeType convert_to_nozzle_type(const std::string &str)
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string resolve_filament_printer_model(const std::string& printer_type, PresetBundle* preset_bundle)
|
||||
{
|
||||
if (!preset_bundle)
|
||||
return {};
|
||||
|
||||
// Devices with a shipped printer config (Bambu) resolve directly.
|
||||
if (const std::string display_name = DevPrinterConfigUtil::get_printer_display_name(printer_type); !display_name.empty())
|
||||
return display_name;
|
||||
|
||||
// A vendor model id the device reported (SSDP modelNumber, manual binding).
|
||||
if (!printer_type.empty()) {
|
||||
if (const std::string model_name = preset_bundle->get_printer_model_display_name(printer_type); !model_name.empty())
|
||||
return model_name;
|
||||
}
|
||||
|
||||
// OrcaSonar's model id is optional; a generic device resolves against the selected profile.
|
||||
if (const ConfigOptionString* model = preset_bundle->printers.get_selected_preset().config.opt<ConfigOptionString>("printer_model");
|
||||
model && !model->value.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "resolve_filament_printer_model: device type \"" << printer_type
|
||||
<< "\" has no installed model; using the selected profile \"" << model->value << "\"";
|
||||
return model->value;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
wxString MachineObject::get_printer_type_display_str() const
|
||||
{
|
||||
std::string display_name = DevPrinterConfigUtil::get_printer_display_name(printer_type);
|
||||
@@ -1733,6 +1760,10 @@ int MachineObject::check_resume_condition()
|
||||
}
|
||||
int MachineObject::command_ams_change_filament(bool load, std::string ams_id, std::string slot_id, int old_temp, int new_temp, std::optional<int> extruder_id)
|
||||
{
|
||||
if (!printer_supports_command("print.ams_change_filament")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_change_filament: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
json j;
|
||||
try {
|
||||
auto tray_id = 0;
|
||||
@@ -1776,6 +1807,10 @@ int MachineObject::command_ams_change_filament(bool load, std::string ams_id, st
|
||||
|
||||
int MachineObject::command_ams_user_settings(bool start_read_opt, bool tray_read_opt, bool remain_flag)
|
||||
{
|
||||
if (!printer_supports_command("print.ams_user_setting")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_user_settings: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
json j;
|
||||
j["print"]["command"] = "ams_user_setting";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
@@ -1784,22 +1819,59 @@ int MachineObject::command_ams_user_settings(bool start_read_opt, bool tray_read
|
||||
j["print"]["tray_read_option"] = tray_read_opt;
|
||||
j["print"]["calibrate_remain_flag"] = remain_flag;
|
||||
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectOnInsertEnabled(tray_read_opt);
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectOnPowerupEnabled(start_read_opt);
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectRemainEnabled(remain_flag);
|
||||
ams_user_setting_start = time(nullptr);
|
||||
|
||||
return this->publish_json(j);
|
||||
const int rc = this->publish_json(j);
|
||||
if (rc == 0) {
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectOnInsertEnabled(tray_read_opt);
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectOnPowerupEnabled(start_read_opt);
|
||||
m_fila_system->GetAmsSystemSetting().SetDetectRemainEnabled(remain_flag);
|
||||
ams_user_setting_start = time(nullptr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int MachineObject::command_ams_calibrate(int ams_id)
|
||||
{
|
||||
if (!m_agent) return -1;
|
||||
if (!m_agent->owns_agent(printer_agent_id))
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
return command_with_dialog(m_agent->command_ams_calibrate(get_dev_id(), ams_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
|
||||
}
|
||||
|
||||
bool MachineObject::printer_supports_command(const char* command) const
|
||||
{
|
||||
if (!command || !m_agent)
|
||||
return false;
|
||||
return m_agent->supports_command(printer_agent_id, get_dev_id(), command);
|
||||
}
|
||||
|
||||
bool MachineObject::printer_uses_filament_mapping() const
|
||||
{
|
||||
if (!m_agent)
|
||||
return false;
|
||||
// An unrecorded owner goes through owns_agent() like any other, because
|
||||
// whichever agent is active is also the one formatting the payload. Only a
|
||||
// recorded owner that differs from the active agent is a mismatch.
|
||||
return m_agent->uses_filament_mapping(printer_agent_id);
|
||||
}
|
||||
|
||||
bool MachineObject::printer_supports_feature(const char* feature) const
|
||||
{
|
||||
return feature && m_agent && m_agent->supports_feature(printer_agent_id, get_dev_id(), feature);
|
||||
}
|
||||
|
||||
bool MachineObject::supports_extrusion_cali() const
|
||||
{
|
||||
// Devices with no agent id predate the agent split and keep the Bambu path.
|
||||
return printer_agent_id.empty() || printer_agent_id == BBL_PRINTER_AGENT_ID;
|
||||
}
|
||||
|
||||
int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::string filament_id, std::string setting_id, std::string tray_color, std::string tray_type, int nozzle_temp_min, int nozzle_temp_max)
|
||||
{
|
||||
if (!printer_supports_command("print.ams_filament_setting")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_filament_settings: printer agent does not support slot metadata updates";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
int tag_tray_id = 0;
|
||||
int tag_ams_id = ams_id;
|
||||
int tag_slot_id = slot_id;
|
||||
@@ -1834,6 +1906,10 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s
|
||||
int MachineObject::command_ams_refresh_rfid(int ams_id, int slot_id)
|
||||
{
|
||||
if (!m_agent) return -1;
|
||||
if (!printer_supports_command("print.ams_get_rfid")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_refresh_rfid: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
return command_with_dialog(m_agent->command_ams_refresh_rfid(get_dev_id(), ams_id, slot_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
|
||||
}
|
||||
|
||||
@@ -1847,11 +1923,19 @@ int MachineObject::command_start_camera()
|
||||
int MachineObject::command_ams_select_tray(std::string tray_id)
|
||||
{
|
||||
if (!m_agent) return -1;
|
||||
if (!printer_supports_command("print.ams_change_filament")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_select_tray: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
return command_with_dialog(m_agent->command_ams_select_tray(get_dev_id(), tray_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
|
||||
}
|
||||
|
||||
int MachineObject::command_ams_control(std::string action)
|
||||
{
|
||||
if (!printer_supports_command("print.ams_control")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_control: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
if (action == "resume" && check_resume_condition()) return 0;
|
||||
|
||||
//valid actions
|
||||
@@ -1867,6 +1951,10 @@ int MachineObject::command_ams_control(std::string action)
|
||||
|
||||
int MachineObject::command_ams_drying_stop()
|
||||
{
|
||||
if (!printer_supports_command("print.auto_stop_ams_dry")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "command_ams_drying_stop: printer agent does not support the command";
|
||||
return command_with_dialog(ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED);
|
||||
}
|
||||
json j;
|
||||
j["print"]["command"] = "auto_stop_ams_dry";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
@@ -2817,7 +2905,9 @@ bool MachineObject::is_camera_busy_off()
|
||||
int MachineObject::publish_json(const json& json_item, int qos, int flag)
|
||||
{
|
||||
int rtn = 0;
|
||||
if (is_lan_mode_printer()) {
|
||||
if (m_agent && !m_agent->owns_agent(printer_agent_id)) {
|
||||
rtn = ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED;
|
||||
} else if (is_lan_mode_printer()) {
|
||||
rtn = local_publish_json(json_item.dump(), qos, flag);
|
||||
} else {
|
||||
rtn = cloud_publish_json(json_item.dump(), qos, flag);
|
||||
@@ -2861,6 +2951,7 @@ int MachineObject::local_publish_json(std::string json_str, int qos, int flag)
|
||||
std::string MachineObject::setting_id_to_type(std::string setting_id, std::string tray_type)
|
||||
{
|
||||
std::string type;
|
||||
if (wxTheApp == nullptr) return tray_type;
|
||||
PresetBundle* preset_bundle = GUI::wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
|
||||
@@ -4197,6 +4288,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
vt_slot[0].setting_id = jj["tray_info_idx"].get<std::string>();
|
||||
//vt_tray.type = jj["tray_type"].get<std::string>();
|
||||
vt_slot[0].m_fila_type = setting_id_to_type(vt_slot[0].setting_id, jj["tray_type"].get<std::string>());
|
||||
// The ack carries the whole slot; re-derive empty so the panel flips off
|
||||
// "Empty" without waiting out the hold.
|
||||
vt_slot[0].UpdateEmptyState(true, printer_agent_id == ORCA_PRINTER_AGENT_ID);
|
||||
// delay update
|
||||
vt_slot[0].set_hold_count();
|
||||
} else {
|
||||
@@ -4222,6 +4316,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
tray_it->second->setting_id = jj["tray_info_idx"].get<std::string>();
|
||||
tray_it->second->m_fila_type = setting_id_to_type(tray_it->second->setting_id, jj["tray_type"].get<std::string>());
|
||||
// The ack carries the whole slot; re-derive empty so the panel flips off
|
||||
// "Empty" without waiting out the hold.
|
||||
tray_it->second->UpdateEmptyState(true, printer_agent_id == ORCA_PRINTER_AGENT_ID);
|
||||
// delay update
|
||||
tray_it->second->set_hold_count();
|
||||
} else {
|
||||
@@ -4313,7 +4410,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
info = _L("Selected diameter and machine diameter do not match");
|
||||
}
|
||||
else if (reason == "generate auto filament cali gcode failure") {
|
||||
info = _L("Failed to generate cali G-code");
|
||||
info = _L("Failed to generate calibration G-code");
|
||||
}
|
||||
else {
|
||||
info = reason;
|
||||
@@ -5160,7 +5257,7 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
|
||||
//std::string type = vtray["tray_type"].get<std::string>();
|
||||
std::string type = setting_id_to_type(vt_tray.setting_id, vtray["tray_type"].get<std::string>());
|
||||
// vt_tray.setting_id is our OF id (translated on the way in); the two support ids below are the printer's own.
|
||||
auto* agent = GUI::wxGetApp().getAgent();
|
||||
auto* agent = wxTheApp != nullptr ? GUI::wxGetApp().getAgent() : nullptr;
|
||||
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(vt_tray.setting_id) : vt_tray.setting_id;
|
||||
if (printer_filament_id == "GFS00") {
|
||||
vt_tray.m_fila_type = "PLA-S";
|
||||
@@ -5255,6 +5352,8 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
|
||||
else {
|
||||
vt_tray.remain = -1;
|
||||
}
|
||||
vt_tray.UpdateEmptyState(vtray.contains("tray_info_idx") && vtray.contains("tray_type"),
|
||||
printer_agent_id == ORCA_PRINTER_AGENT_ID);
|
||||
}
|
||||
|
||||
return vt_tray;
|
||||
@@ -5548,11 +5647,11 @@ void MachineObject::parse_new_info2(const json& info)
|
||||
if (capabilities_it == info.end() || !capabilities_it->is_object())
|
||||
return;
|
||||
const auto flags_it = capabilities_it->find("flags");
|
||||
if (flags_it == capabilities_it->end() || !flags_it->is_object())
|
||||
const bool has_flags = flags_it != capabilities_it->end() && flags_it->is_object();
|
||||
if (!has_flags)
|
||||
return;
|
||||
|
||||
const json& flags = *flags_it;
|
||||
BOOST_LOG_TRIVIAL(info) << "parse_new_info2: OrcaSonar capability flags=" << flags.dump();
|
||||
BOOST_LOG_TRIVIAL(info) << "parse_new_info2: capability flags=" << flags.dump();
|
||||
|
||||
auto parse_bool = [&flags](const char* name, bool& target) {
|
||||
const auto it = flags.find(name);
|
||||
|
||||