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 -->
|
||||
|
||||
@@ -262,9 +262,9 @@ and a band is not a "gesture" in the `CadLevel::Gesture` sense because nothing h
|
||||
|---|---|---|---|---|---|
|
||||
| `Move` | — | `update_hover` / `update_solid_hover` (**non-consuming**, returns false) | — | passthrough | hover only asks for a repaint, `:10022-10025` |
|
||||
| `LeftDown` | orbit may begin | latch press, **return false** | — | consume-or-orbit is the canvas's call | `:10058-10063`; consuming here killed orbit once already |
|
||||
| `LeftDrag` > 8 px | — | start + drive band, **consume**, when Shift is held or the left button has no camera action | — | otherwise the left button's drag action (Preferences > Control) | the band is Prepare's Shift+left-drag rectangle selection |
|
||||
| `LeftDrag` > 8 px | — | start + drive band, **consume** | — | no longer orbits in this canvas | middle-drag orbits, right-drag pans (`:10013-10016`) |
|
||||
| `LeftUp` | — | commit pick **or** resolve band | — | — | `:10035-10041` |
|
||||
| `MiddleDrag` / `RightDrag` | — | must not see it | — | the button's drag action (Preferences > Control) | camera gestures never reach the FSM; a right press reaches the tool only once its release shows it was a click |
|
||||
| `MiddleDrag` / `RightDrag` | — | must not see it | — | orbit / pan | camera gestures never reach the FSM |
|
||||
| `RightClick` | — | — | offer menu | — | `snaporca-xmh6` open: a right-click that only clears the sketch selection eats the offer |
|
||||
| `Esc` | — | — | `escape()` ladder | — | one route whatever holds focus (`:4228-4231`) |
|
||||
| `Del` / `Backspace` | — | `delete_selected_or_last_sketch_entity()` | char hook | — | `:4260` |
|
||||
|
||||
@@ -135,7 +135,7 @@ contract between them is stated in code rather than spread across handlers.
|
||||
| --- | --- | --- |
|
||||
| `Transient` | a value field or a popup menu | closes it; the tool stays armed |
|
||||
| `Gesture` | an uncommitted delta — an entity being drawn, a body being dragged | reverts it; committed work is untouched |
|
||||
| `Tool` | a feature card, Sketch waiting for its plane, an armed sketch tool, a constrain session | exits it; drawn entities survive |
|
||||
| `Tool` | a feature card, an armed sketch tool, a constrain session | exits it; drawn entities survive |
|
||||
| `Idle` | nothing transient | clears the selection, a Feature tree or Bodies row included; leaves a sketch session only if it is empty |
|
||||
|
||||
`cad_escape_level()` is a `constexpr` free function over a POD of four booleans rather than a
|
||||
@@ -149,66 +149,26 @@ explicit selection, the sketch ribbon's Cancel, which asks first, or `Ctrl+Z`. A
|
||||
*session* is deliberately not a `Tool` level; it is the environment the `Idle` level lives in,
|
||||
which makes the destructive path unrepresentable rather than merely unlikely.
|
||||
|
||||
A body Move is the one `Gesture` that outlives the press: its gizmo stays up between drags until
|
||||
Confirm keeps the placement or `Esc` or Cancel puts the body back. Until then the selection is
|
||||
held — a click off the gizmo only steers the camera — and undo is refused, since the placement
|
||||
is not in the history. Anything that starts another edit (a feature card, a sketch, placing
|
||||
imported art or text, another body's Move, a rebuild) keeps the placement, as switching gizmos
|
||||
keeps a move in Prepare. The panel ends the Move in one place (`DesignPanel::end_body_move`), so
|
||||
the gizmo, the Move / Rotate card and the ✓/✗ cannot outlive one another.
|
||||
|
||||
Right-click is read at button-up against one budget, 3 px of drift, applied to the whole press
|
||||
rather than to its end points: a press that wandered past the budget at any moment is
|
||||
navigation, even if it comes back to where it started, which is what stops a slow, careful
|
||||
orbit from ending in a menu. There is no time budget — a gesture that means something different
|
||||
when it is slow is exactly what the interaction charter rules out. The raycast uses the press
|
||||
position, not the release. The sketch tool sees a right press only once the release has shown it
|
||||
was a click: the press itself goes to the camera, which may pan or orbit with that button, and the
|
||||
canvas replays it to the tool on a stationary release. A tool that uses the click (to terminate a
|
||||
chain, say) keeps the menu closed. Past either budget the event is navigation, and navigation
|
||||
does not transition the state machine.
|
||||
position, not the release. An armed sketch tool that already consumed the right
|
||||
button (to terminate a chain, say) declines to also open a menu, through a read-and-clear flag.
|
||||
Past either budget the event is navigation, and navigation does not transition the state
|
||||
machine.
|
||||
|
||||
Navigation itself is Prepare's: the camera reads the drag actions set in Preferences > Control
|
||||
for each button, and in the Touchpad camera style a move with Alt held orbits and one with Shift
|
||||
held pans, whatever tool is armed. The left button is shared with picking and drawing, so a tool
|
||||
handle or a press that draws takes it first, as a gizmo does in Prepare; a whole body is swept
|
||||
with a rectangle on plain left-drag only while no camera action is assigned to the left button,
|
||||
and with Shift+left-drag otherwise — Prepare's own rectangle selection.
|
||||
for each button. The left button is shared with picking, so a whole body is swept with a
|
||||
rectangle on plain left-drag only while no camera action is assigned to it, and with
|
||||
Shift+left-drag otherwise — Prepare's own rectangle selection.
|
||||
|
||||
Entering a sketch changes two things at once so the mode is legible: a banner above the
|
||||
Entering a sketch changes three things at once so the mode is legible: a banner above the
|
||||
canvas (a sibling of the canvas, not a child over it — on GTK a child window over a
|
||||
`wxGLCanvas` is a native window and does not reliably stack over GL), and `N` to look normal to
|
||||
the plane. The printer bed stays: there is no sketch grid, so the plate grid is the only ground
|
||||
reference a sketch has. Code that changes either belongs with a change to this section.
|
||||
|
||||
Sketch mode is never entered without a plane under it, so the banner, the sketch keys and the
|
||||
sketch offer always have a session to act on. Sketch on a picked flat face or reference plane opens
|
||||
the session on it at once. With nothing picked it stays in Feature mode and waits for one — an
|
||||
armed `Tool`, left with `Esc` or ✗, and ended by anything that starts another edit — and the
|
||||
reference plane or flat face clicked next opens the session. A picked plane is a selection like a
|
||||
face: the sketch on it uses it up, and `Esc` or a click on nothing lets go of it, so a plane that
|
||||
can no longer be seen never decides where the next sketch goes.
|
||||
|
||||
The reference planes — XY, XZ and YZ through the modeling origin, with their half-axes — are
|
||||
drawn on demand, because three translucent squares over every model are noise once they are not
|
||||
the thing being picked. Sketch brings them up while it waits for a plane, which is exactly when
|
||||
they are picked, and the session the pick opens takes them away; a live session draws none. The
|
||||
Feature tree's Origin row keeps them up outside a sketch. Its state is a view preference in
|
||||
AppConfig rather than part of the recipe, so it costs the project format nothing. The Plane tool
|
||||
keeps its own rule: the planes and the datums as Offset bases, and nothing for the other methods,
|
||||
where a click on a plane would rewrite the datum's references. The `P` and `A` keys are a
|
||||
separate, unpickable view helper and do not follow the Origin row.
|
||||
|
||||
The Bed row, under the Origin row, is the printer bed's switch in the same way: it draws or hides
|
||||
the bed and its plate grid in every mode. It is a view preference in AppConfig too, and the bed is
|
||||
shown until it is turned off.
|
||||
|
||||
The two rows are view switches, not history, and the tree says so: they sit unframed on the Feature
|
||||
tree's card, above the features' own framed list, and stay put while the features scroll. A click
|
||||
never selects either row, since a selected Origin or Bed would have nothing to edit, move or
|
||||
delete; the eye and the right-click menu are the only targets, and a row's label dims while its
|
||||
thing is hidden, as a hidden body's does. Because the block never takes the focus, `Ctrl+Shift+O`
|
||||
and `Ctrl+Shift+B` flip the Origin and the Bed from the keyboard.
|
||||
`wxGLCanvas` is a native window and does not reliably stack over GL), the printer bed muted so
|
||||
a plate grid is never read as a sketch grid, and `N` to look normal to the plane. Code that
|
||||
changes any of the three belongs with a change to this section.
|
||||
|
||||
## Rendering the bodies
|
||||
|
||||
@@ -229,15 +189,6 @@ degenerate edges are left out (`GeometryEngine::display_edges`), and the polylin
|
||||
once per shape, keyed by its `TShape`, because a recompute that leaves a body unchanged is the
|
||||
common case.
|
||||
|
||||
While a feature card is open, its preview ghost is the whole model the candidate would produce,
|
||||
drawn translucent over the bodies, so every face the feature leaves alone is in both at the same
|
||||
depth. The ghost is drawn with a depth bias that pushes it back (`GLVolume::depth_bias`), so on a shared face
|
||||
the body always wins instead of the two copies z-fighting, and the ghost shows only where the
|
||||
result reaches past the bodies. Material a feature removes lies inside the old solid and would not
|
||||
show at all, so the tools whose result mostly coincides with the body — Fillet/Chamfer, Draft,
|
||||
Hole and the Mate hover — hide the bodies once the preview is valid and draw the result alone,
|
||||
opaque.
|
||||
|
||||
## Showing what is selected
|
||||
|
||||
A selection is drawn on the faces it names, never as a tint over the body: a translucent
|
||||
@@ -263,8 +214,7 @@ replay costs up to a recompute, so the panel finds the faces once per row and to
|
||||
generation, off the UI thread, and only while no feature card is open. One selection is live at
|
||||
a time: a viewport pick clears the feature row and a feature row clears the viewport pick, as the
|
||||
Feature tree and Bodies list do between themselves. `Esc`, a click on empty space and an
|
||||
empty rubber band all let go of it, whichever list or pick made it — except while a body Move is
|
||||
open, which holds the selection until it ends (see the interaction contract).
|
||||
empty rubber band all let go of it, whichever list or pick made it.
|
||||
|
||||
## Following the app
|
||||
|
||||
@@ -282,18 +232,8 @@ The tab is a page of Orca's main window and answers to the same settings as Prep
|
||||
constraint-row buttons are Orca's self-painted `Button`, because a native button cannot take a
|
||||
hover background on macOS. The Feature tree and Bodies lists are a custom-drawn
|
||||
`DesignRowList` rather than a `wxTreeCtrl`, so each row carries its own actions — Edit,
|
||||
Show/hide and Delete on a feature, Move, Show/hide and Delete on a body, and only Show/hide on
|
||||
the Origin and Bed rows, a separate non-selectable list above the features — and the eye shows
|
||||
Show/hide and Delete on a feature, Move, Show/hide and Delete on a body — and the eye shows
|
||||
whether that row is hidden.
|
||||
- **Plates.** This is the one thing the tab does not follow. The canvas has a bed of its own at
|
||||
the printer bed's home position, whichever plate Prepare has current, and a new document's
|
||||
modeling origin is that bed's centre. A bed that followed the current plate would slide out
|
||||
from under a design: the origin is fixed once per document, baked into every sketch plane and
|
||||
saved in the recipe, while the current plate can change between visits. Commit to Plate does
|
||||
not need it either, since the committed object is placed on an empty spot of the current
|
||||
plate. What the canvas does read from the plate is moved onto its bed: the exclude areas, the
|
||||
plate box the camera orbits about when nothing is picked (`GLCanvas3D::_current_plate_box`),
|
||||
and the first view, which starts as a copy of Prepare's camera.
|
||||
- **Viewport text.** The status line and the active tool's values are drawn by the canvas in
|
||||
its ImGui pass, so they go with the canvas: a top-level window over GL does not follow its
|
||||
frame and was left floating over other applications.
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 9 14 9 22 15 10 15Z"/><path d="M2 9V11L10 17H22V15M10 15V17"/><path d="M6 9 14 15M10 9 18 15M6 12H18"/></svg>
|
||||
|
Before Width: | Height: | Size: 301 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3.15 19.57V4.43A1.28 1.28 0 0 1 4.43 3.15H17.05L20.85 6.95V19.57A1.28 1.28 0 0 1 19.57 20.85H4.43A1.28 1.28 0 0 1 3.15 19.57ZM8.2 3.15V5.68A1.28 1.28 0 0 0 9.48 6.95H14.52A1.28 1.28 0 0 0 15.8 5.68V3.15M17.05 20.85V15.8A1.28 1.28 0 0 0 15.8 14.53H8.2A1.28 1.28 0 0 0 6.95 15.8V20.85"/></svg>
|
||||
|
Before Width: | Height: | Size: 481 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M8.08,10.74 8.08,5.4M8.08,10.74 15.55,4.87M17.15,10.2 15.55,4.87M4.88,8.6 8.62,20.87M17.15,10.2 20.89,16.07M8.08,10.74 12.78,15.54 17.15,10.2M17.15,19.8 12.78,15.54 8.62,20.87M4.88,8.6 8.08,10.74 17.15,10.2 19.82,7M8.08,10.74 8.62,20.87M17.15,10.2 17.15,19.8" fill="none" stroke="#949494" stroke-width="0.6" stroke-linecap="round" stroke-linejoin="round"/><polygon points="4.88,8.6 8.08,5.4 15.55,4.87 19.82,7 20.89,16.07 17.15,19.8 8.62,20.87 4.35,17.14" fill="none" stroke="#949494" stroke-width="0.85" stroke-linejoin="round"/><polygon points="2.84,4.68 7.32,4.68 7.32,2.84 11.29,5.86 7.32,8.88 7.32,7.04 2.84,7.04" style="fill:#009688;stroke:#009688;stroke-width:0.4;stroke-linejoin:round"/></svg>
|
||||
|
Before Width: | Height: | Size: 794 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3.15" y="3.15" width="17.7" height="17.7" rx="1.28"/><path d="M7.6 12H16.4M12 7.6V16.4"/></svg>
|
||||
|
Before Width: | Height: | Size: 284 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 1.5 23 23"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#949494" stroke="none"/><polygon points="2.72,5.99 7.02,5.99 7.02,4.22 10.82,7.12 7.02,10.01 7.02,8.25 2.72,8.25" style="fill:#009688;stroke:#009688;stroke-width:0.38;stroke-linejoin:round"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#949494" stroke="none"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -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)); });
|
||||
}
|
||||
|
||||
@@ -147,7 +147,6 @@ def seed_datadir(datadir):
|
||||
# ~90% case expects. A ladder whose result depends on the developer's own preferences is not
|
||||
# a gate.
|
||||
app["auto_close_sketch_loops"] = True
|
||||
app["design_show_bed"] = True
|
||||
# SILENCE THE NETWORK PLUGIN PROMPT. Without this, GUI_App::post_init() re-raises "Bambu
|
||||
# Network Plug-in Required" from an IDLE event — after any modal sweep this driver does at
|
||||
# startup — and ShowModal() then runs a nested event loop. The app is alive, its window is
|
||||
@@ -481,14 +480,10 @@ def type_into_open_field(value, mark):
|
||||
def enter_sketch(timeout=180):
|
||||
"""Open a real sketch on a real plane, and PROVE it with the socket before drawing anything.
|
||||
|
||||
THE SEQUENCE MATTERS AND IT IS NOT OBVIOUS. The reference planes are hidden until Shift+S,
|
||||
with nothing picked, brings them up to choose from, so the plane is clicked AFTER it. That
|
||||
click opens the session on the plane and pops the tool offer, and a click outside a popup
|
||||
menu only closes it, so the spot is clicked twice, apart: the second click closes the offer,
|
||||
and inside the session it is empty canvas. No Escape — with no offer up, Escape in an empty
|
||||
sketch leaves it. Then a tool letter arms the tool. check-gui-sketching.py does the same
|
||||
steps. This ladder once did two of them — Design tab, then Shift+S — and went straight to
|
||||
the tool letters.
|
||||
THE SEQUENCE MATTERS AND IT IS NOT OBVIOUS. Shift+S enters sketch MODE and pops the plane
|
||||
offer; the offer must be dismissed; and the plane itself is chosen by clicking it in the
|
||||
viewport BEFORE Shift+S. check-gui-sketching.py has always done all four steps. This ladder
|
||||
did two of them — Design tab, then Shift+S — and went straight to the tool letters.
|
||||
|
||||
That intermediate state is the trap. `is_sketching` reads 1, every tool key is accepted and
|
||||
traced, and not one click draws anything, because there is no plane under them. The ladder
|
||||
@@ -504,10 +499,10 @@ def enter_sketch(timeout=180):
|
||||
click(132, 53) # Design tab
|
||||
time.sleep(2.0)
|
||||
dismiss_modals()
|
||||
key("shift+s", 1.0) # nothing picked: the reference planes come up
|
||||
click(*PLANE_PX, pause=0.8) # picks the plane IN THE VIEWPORT: session open, offer up...
|
||||
click(*PLANE_PX) # ...and this closes the offer
|
||||
key("p", 0.6) # arms Point in the session
|
||||
click(*PLANE_PX) # pick the plane IN THE VIEWPORT — before Shift+S
|
||||
key("shift+s", 1.0)
|
||||
key("Escape", 0.5) # entering sketch mode pops the offer; dismiss it
|
||||
key("p", 0.6) # any sketch tool starts the session on that plane
|
||||
if try_call("sketch_describe") is not None:
|
||||
# NO Escape here. Every rung already opens with one to drop whatever tool the last
|
||||
# one left armed, and Escape in the Design tab walks a LIFO: first press drops the
|
||||
@@ -516,15 +511,14 @@ def enter_sketch(timeout=180):
|
||||
# reported all eleven checks failed with "nothing opened" — the tools were arming
|
||||
# into an empty Feature-mode document.
|
||||
return
|
||||
die("no sketch opened after Shift+S + plane click within "
|
||||
die("no sketch opened after plane click + Shift+S within "
|
||||
f"{timeout}s — sketch_describe never answered on {A.sock} (trace {TRACE})")
|
||||
|
||||
|
||||
# tool key, the clicks that draw it, and one distinct value per queued field. The values are
|
||||
# deliberately nothing like the as-drawn size, so a committed prefill cannot coincide with them.
|
||||
# Where the plane label sits in the viewport once Shift+S has brought the planes up. Same
|
||||
# constant the gesture ladder uses; it is a label on the 3D view, not a widget, so it moves only
|
||||
# if the camera does.
|
||||
# Where the plane label sits in the viewport before a sketch is open. Same constant the gesture
|
||||
# ladder uses; it is a label on the 3D view, not a widget, so it moves only if the camera does.
|
||||
PLANE_PX = (913, 359)
|
||||
|
||||
# Every coordinate below stays inside 1000..1400 x 500..760 — the box check-gui-sketching.py's
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -229,20 +229,7 @@ CHROME_DY = int(os.environ.get("ORCA_CAD_CHROME_DY", "26"))
|
||||
|
||||
DESIGN_TAB = (128, 29 + CHROME_DY)
|
||||
|
||||
# The ribbon's document group lost its Bed checkbox and label when the bed toggle moved to the
|
||||
# Feature tree, and every group to its right moved left by their width: wxLEFT 6 + the 18 px
|
||||
# CheckBox + wxLEFT 4 + the "Bed" label (~24 px) + wxRIGHT 4, about 56 px. The x of
|
||||
# CONSTRUCTION_CHECKBOX, CON_BTN and CON_BTN_SKETCH below is the one measured before that, plus
|
||||
# this. DERIVED, NOT MEASURED: re-measure on the rig and fold it in. CONFIRM_BTN sits after the
|
||||
# ribbon's stretch spacer and did not move.
|
||||
RIBBON_DX = -56
|
||||
|
||||
# Feature-tree rows, measured on the rig at 1920x1080: the list's first row is centred at y=215,
|
||||
# then 23 px apart. The first two rows are the Origin and the Bed, unframed on the card above the
|
||||
# features' own framed list, so the first FEATURE row sits two rows and TREE_GAP further down;
|
||||
# reset_document and the reopening double-click both need a feature. TREE_GAP is the list's
|
||||
# 12 px top margin plus its 1 px frame, derived rather than measured -- re-measure on the rig, like
|
||||
# RIBBON_DX.
|
||||
# Feature-tree rows, measured on the rig at 1920x1080: first row centre, then 23 px apart.
|
||||
# x=300, not the label: a second click ON the label opens the inline rename, and Delete then
|
||||
# edits the text instead of removing the feature.
|
||||
#
|
||||
@@ -250,8 +237,7 @@ RIBBON_DX = -56
|
||||
# unshifted click lands 26 px BELOW the first row -- just past its 23 px height -- so the row is
|
||||
# never selected, Delete does nothing, and reset_document spends 40 rounds on it before dying
|
||||
# with "could not empty the feature tree". That names the feature tree, which is not the fault.
|
||||
TREE_GAP = 12 + 1
|
||||
TREE_ROW0 = (300, 215 + 2 * 23 + TREE_GAP + CHROME_DY)
|
||||
TREE_ROW0 = (300, 215 + CHROME_DY)
|
||||
|
||||
|
||||
def go_design():
|
||||
@@ -280,21 +266,18 @@ def reset_document():
|
||||
def enter_sketch(tool_key, plane_px=(913, 359)):
|
||||
"""Enter a sketch the way the design law says: pick the plane in the viewport, then the tool.
|
||||
|
||||
leave_sketch()'s Escapes let go of every pick, so Shift+S finds nothing to sketch on and only
|
||||
brings the reference planes up; the click on one opens the session on it and pops the tool
|
||||
offer. A click outside a popup menu only closes it, so the same spot is clicked again, apart:
|
||||
inside the session it is empty canvas, so it selects nothing if the offer is slow to appear.
|
||||
No Escape: with no offer up, Escape in an empty sketch leaves it. The tool letter then arms
|
||||
the tool. All of it is real input — nothing here goes through the socket.
|
||||
Shift+S enters sketch MODE and pops the offer; Escape dismisses it; the tool letter then
|
||||
starts the session on the plane the click selected. All four steps are real input — nothing
|
||||
here goes through the socket.
|
||||
"""
|
||||
leave_sketch()
|
||||
click(*plane_px)
|
||||
key("shift+s", 0.8)
|
||||
click(*plane_px, pause=0.8) # picks the plane: the session opens and the offer pops...
|
||||
click(*plane_px) # ...and this closes the offer
|
||||
key("Escape", 0.4) # entering sketch mode pops the offer; dismiss it
|
||||
key("p", 0.6)
|
||||
if try_call("sketch_describe") is None:
|
||||
shot("/shots/gl-enter-failed.png")
|
||||
die("no sketch opened after Shift+S + plane click (see /shots/gl-enter-failed.png)")
|
||||
die("no sketch opened after plane click + Shift+S (see /shots/gl-enter-failed.png)")
|
||||
calibrate_here() # THIS sketch's own camera map, on THIS sketch's own plane
|
||||
key(tool_key, 0.6)
|
||||
|
||||
@@ -754,7 +737,7 @@ def corner_pair(ents):
|
||||
die("no adjacent pair in what should be a rectangle")
|
||||
|
||||
|
||||
CONSTRUCTION_CHECKBOX = (419 + RIBBON_DX, 75 + CHROME_DY)
|
||||
CONSTRUCTION_CHECKBOX = (419, 75 + CHROME_DY)
|
||||
|
||||
|
||||
def draw_line(x0, y0, x1, y1, length, angle):
|
||||
@@ -883,7 +866,7 @@ def rung_extend():
|
||||
# because the ladder only ever clicks "perpendicular" (3) and "equal" (5), both of which sit
|
||||
# before the first insertion. The next rung to use "tangent" would have clicked "collinear".
|
||||
CON_BTN_Y = 76 + CHROME_DY
|
||||
CON_BTN = {n: (449 + RIBBON_DX + 42 * i, CON_BTN_Y) for i, n in enumerate(
|
||||
CON_BTN = {n: (449 + 42 * i, CON_BTN_Y) for i, n in enumerate(
|
||||
["horizontal", "vertical", "parallel", "perpendicular", "coincident", "equal",
|
||||
"equal_radius", "collinear", "concentric", "tangent", "midpoint", "symmetric",
|
||||
"sym_v", "sym_h", "angle", "radius", "diameter", "fix", "dist_x", "dist_y"])}
|
||||
@@ -897,7 +880,7 @@ CON_BTN = {n: (449 + RIBBON_DX + 42 * i, CON_BTN_Y) for i, n in enumerate(
|
||||
# which already carries CHROME_DY), but the X start depends on how wide the sketch toolbar to the
|
||||
# left of this group renders, and this fork keeps mainline's top row. Re-measure before trusting
|
||||
# D11 here: screenshot in sketch mode and detect the icon columns, do not derive it by offset.
|
||||
CON_BTN_SKETCH = {n: (677 + RIBBON_DX + 42 * i, CON_BTN_Y) for i, n in enumerate(
|
||||
CON_BTN_SKETCH = {n: (677 + 42 * i, CON_BTN_Y) for i, n in enumerate(
|
||||
["horizontal", "vertical", "parallel", "perpendicular", "coincident", "equal",
|
||||
"equal_radius", "collinear", "concentric", "tangent", "midpoint", "symmetric",
|
||||
"sym_v", "sym_h", "angle", "radius", "diameter", "fix", "dist_x", "dist_y"])}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -229,11 +229,11 @@
|
||||
"none"
|
||||
],
|
||||
"needs": {},
|
||||
"refusal": "Select a flat face or a reference plane to sketch on",
|
||||
"refusal": "Click a face or a reference plane in the viewport, then a sketch tool",
|
||||
"gui": true,
|
||||
"action": "key:S+S",
|
||||
"icon": "design_sketch",
|
||||
"hint": "Sketch on the selected flat face or plane, or click one next"
|
||||
"hint": "Click a face or a reference plane, then pick a drawing tool"
|
||||
},
|
||||
{
|
||||
"id": "extrude",
|
||||
|
||||
@@ -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.
|
||||
@@ -367,10 +370,6 @@ void AppConfig::set_defaults()
|
||||
// restores the conventional CAD representation for users who expect it (x0kd).
|
||||
if (get("design_connector_face_glyph").empty())
|
||||
set_bool("design_connector_face_glyph", true);
|
||||
|
||||
// Design tab: draw the printer bed and its plate grid (the Feature tree's Bed row).
|
||||
if (get("design_show_bed").empty())
|
||||
set_bool("design_show_bed", true);
|
||||
#endif
|
||||
|
||||
//#ifdef SUPPORT_SHOW_HINTS
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -310,7 +310,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
, force_native_color(false)
|
||||
, force_neutral_color(false)
|
||||
, force_sinking_contours(false)
|
||||
, depth_bias(false)
|
||||
, picking(false)
|
||||
, tverts_range(0, size_t(-1))
|
||||
{
|
||||
@@ -1333,17 +1332,11 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
|
||||
shader->set_uniform("view_normal_matrix", view_normal_matrix);
|
||||
if (volume.first->depth_bias) {
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(1.0f, 1.0f));
|
||||
}
|
||||
//BBS: add outline related logic
|
||||
if (volume.first->selected && shader_can_outline && GUI::wxGetApp().show_outline())
|
||||
volume.first->render_with_outline(cnv_size);
|
||||
else
|
||||
volume.first->render();
|
||||
if (volume.first->depth_bias)
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
if (use_environment_texture)
|
||||
|
||||
@@ -232,10 +232,6 @@ public:
|
||||
bool force_neutral_color : 1;
|
||||
// Whether or not to force rendering of sinking contours
|
||||
bool force_sinking_contours : 1;
|
||||
// Orca: draw this volume with a positive depth bias (glPolygonOffset, pushed away from the
|
||||
// camera), so on a surface it shares with another volume the other volume wins the depth
|
||||
// test instead of z-fighting it
|
||||
bool depth_bias : 1;
|
||||
// Is render for picking
|
||||
bool picking : 1;
|
||||
// slice error
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "slic3r/GUI/Camera.hpp" // N: look down the sketch plane normal
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/PartPlate.hpp" // the current plate's origin: the first view's offset
|
||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
||||
#include "slic3r/GUI/GLToolbar.hpp"
|
||||
#include "slic3r/GUI/Event.hpp"
|
||||
@@ -92,7 +91,7 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
m_canvas->enable_plate_chrome(false);
|
||||
m_canvas->enable_labels(false);
|
||||
m_canvas->enable_sinking_contours(false); // they would be sliced from the plater's meshes
|
||||
m_canvas->set_design_canvas(true); // home-position bed, triad and CAD grid at the modeling origin
|
||||
m_canvas->set_axes_at_bed_center(true); // triad at bed centre = modeling origin
|
||||
|
||||
m_canvas->set_design_sketch_tool(&m_sketch_tool);
|
||||
m_sketch_tool.on_commit = [this](const SketchProfile& prof, const SketchPlane& pl) {
|
||||
@@ -191,11 +190,8 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
refresh_bed();
|
||||
|
||||
// The view this canvas opens on. Built lazily, on the way into the Design tab, so this
|
||||
// is the view the user is looking at right now — moved off the current plate onto the
|
||||
// Design bed, which stays at the printer bed's home whichever plate is current.
|
||||
// is the view the user is looking at right now.
|
||||
m_parked_camera = wxGetApp().plater()->get_camera();
|
||||
if (PartPlate* plate = wxGetApp().plater()->get_partplate_list().get_curr_plate())
|
||||
m_parked_camera.translate_world(-plate->get_origin());
|
||||
|
||||
// Before any of this class's own Binds below: wx calls dynamically bound handlers in
|
||||
// reverse order of binding, and GLCanvas3D swallows several events without skipping them —
|
||||
@@ -382,14 +378,10 @@ void DesignCanvas::reload(bool keep_view)
|
||||
v->set_color(c);
|
||||
}
|
||||
} else if (obj_idx == 1) {
|
||||
// The ghost is the whole resulting model, normally drawn as a faint blue overlay on
|
||||
// the visible bodies. Every face the feature leaves alone is in both, at the same
|
||||
// depth, so the ghost is drawn with a depth bias: the bodies win on those faces instead
|
||||
// of the two copies z-fighting, and the ghost shows only where the result reaches past
|
||||
// the bodies. In preview-only mode it IS the result (base bodies hidden), so render it
|
||||
// opaque so it reads as a finished solid rather than a see-through hint.
|
||||
// The ghost is normally a faint blue overlay on the visible body. In preview-only
|
||||
// mode it IS the result (base bodies hidden), so render it opaque so it reads as a
|
||||
// finished solid rather than a see-through hint.
|
||||
v->set_color(m_body_hidden ? ColorRGBA(0.40f, 0.82f, 1.0f, 1.0f) : ghost);
|
||||
v->depth_bias = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +406,6 @@ void DesignCanvas::set_bodies(const std::vector<TriangleMesh>* body_meshes,
|
||||
if (body_meshes == nullptr || body_meshes->empty()) { clear_mesh(); return; }
|
||||
|
||||
m_body_meshes = body_meshes;
|
||||
m_sketch_tool.refresh_body_edges(); // of the bodies set_solid_pick() pointed the tool at
|
||||
m_lit_faces = m_sketch_tool.selected_faces();
|
||||
rebuild_bodies();
|
||||
reload(!m_first_frame);
|
||||
@@ -424,8 +415,6 @@ void DesignCanvas::clear_mesh()
|
||||
{
|
||||
m_body_meshes = nullptr;
|
||||
m_volumes.clear();
|
||||
// No solid, so no edge lines, pick or hover either, as after a rebuild that leaves no body.
|
||||
m_sketch_tool.set_solid_pick(nullptr, nullptr, nullptr, nullptr);
|
||||
if (!m_model.objects.empty()) {
|
||||
m_model.delete_object((size_t)0);
|
||||
reload(true);
|
||||
@@ -481,6 +470,13 @@ void DesignCanvas::begin_sketch(const SketchPlane& plane, DesignSketchTool::Mode
|
||||
if (m_canvas_widget) m_canvas_widget->Refresh();
|
||||
}
|
||||
|
||||
void DesignCanvas::set_sketch_plane(const SketchPlane& plane)
|
||||
{
|
||||
m_sketch_tool.set_plane(plane); // keeps the 2D entities; only the carrier plane changes
|
||||
if (m_canvas) m_canvas->set_as_dirty();
|
||||
if (m_canvas_widget) m_canvas_widget->Refresh();
|
||||
}
|
||||
|
||||
void DesignCanvas::edit_sketch(const std::vector<SketchEntity>& entities,
|
||||
const std::vector<SketchEntityConstraintDef>& constraints,
|
||||
const SketchPlane& plane)
|
||||
@@ -554,7 +550,6 @@ void DesignCanvas::refresh_bed()
|
||||
double printable_height = 100.0;
|
||||
const auto* ph_opt = config->opt<ConfigOptionFloat>("printable_height");
|
||||
if (ph_opt) printable_height = ph_opt->value;
|
||||
// No position: the Design bed stays at the printer bed's home, whichever plate is current.
|
||||
m_bed.set_shape(bed_shape_opt->values, printable_height, {}, {}, "", false); // mainline added extruder_areas/heights params
|
||||
}
|
||||
|
||||
@@ -736,6 +731,8 @@ void DesignCanvas::clear_move_gizmo()
|
||||
request_repaint();
|
||||
}
|
||||
|
||||
bool DesignCanvas::moving_body() const { return m_sketch_tool.moving_body(); }
|
||||
|
||||
void DesignCanvas::set_on_body_move_changed(std::function<void(int, const Transform3d&)> cb)
|
||||
{
|
||||
m_sketch_tool.on_body_move_changed = std::move(cb);
|
||||
@@ -1028,11 +1025,6 @@ void DesignCanvas::set_on_datum_base_picked(std::function<void(int)> cb)
|
||||
m_sketch_tool.on_datum_base_picked = std::move(cb);
|
||||
}
|
||||
|
||||
void DesignCanvas::set_selected_base(std::function<int()> cb)
|
||||
{
|
||||
m_sketch_tool.selected_base = std::move(cb);
|
||||
}
|
||||
|
||||
void DesignCanvas::set_on_sketch_exit(std::function<void()> cb)
|
||||
{
|
||||
m_sketch_tool.on_exit = std::move(cb);
|
||||
@@ -1055,18 +1047,15 @@ void DesignCanvas::set_on_context_menu(std::function<void(const wxPoint&)> cb)
|
||||
return;
|
||||
m_ctx_bound = true;
|
||||
// Bound AFTER GLCanvas3D's own handlers, so this runs first and can consume the event.
|
||||
// It only consumes when it actually opens the offer.
|
||||
// Right-drag may pan or orbit (Preferences > Control). Without remembering where the press
|
||||
// landed, every such drag ended by popping the offer over wherever the camera stopped — the
|
||||
// menu appearing as the reward for moving the view. A right-click is the release of a
|
||||
// STATIONARY press (kCadRightClickDriftPx); only that reaches the sketch tool or the offer.
|
||||
// It only consumes when it actually opens the offer; every other right-click still falls
|
||||
// through to the polyline-chain end and the move gizmo, which were there first.
|
||||
// Right-drag pans. Without remembering where the press landed, every pan ended by popping
|
||||
// the offer over wherever the camera stopped — the menu appearing as the reward for moving
|
||||
// the view. The offer is the release of a STATIONARY right-click (kCadRightClickDriftPx).
|
||||
m_canvas_widget->Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent& e) {
|
||||
m_ctx_press = e.GetPosition();
|
||||
m_ctx_travelled = false;
|
||||
// Drop any press the tool still keeps (only a click's release takes it, so a pan's stays)
|
||||
// before the canvas offers it this one, which ImGui may take instead.
|
||||
m_sketch_tool.drop_right_click();
|
||||
e.Skip(); // the canvas still needs the press to seed a pan or an orbit
|
||||
e.Skip(); // the canvas still needs the press to seed the orbit
|
||||
});
|
||||
m_canvas_widget->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) {
|
||||
if (e.RightIsDown()) {
|
||||
@@ -1077,13 +1066,12 @@ void DesignCanvas::set_on_context_menu(std::function<void(const wxPoint&)> cb)
|
||||
});
|
||||
m_canvas_widget->Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent& e) {
|
||||
const wxPoint d = e.GetPosition() - m_ctx_press;
|
||||
// Click, or navigation? A press that travelled panned or orbited; one that did not, did not.
|
||||
// Always read-and-clear, even when another guard already rules the offer out, or a
|
||||
// terminator recorded under one condition would still be pending under the next.
|
||||
const bool terminated = m_sketch_tool.take_right_consumed();
|
||||
// Click, or navigation? A press that travelled orbited; one that did not, did not.
|
||||
const bool is_click = !m_ctx_travelled && std::max(std::abs(d.x), std::abs(d.y)) <= kCadRightClickDriftPx;
|
||||
// Ending a chain or abandoning an anchor uses the click up.
|
||||
const bool terminated = is_click && m_canvas && m_sketch_tool.take_right_click(*m_canvas);
|
||||
if (terminated)
|
||||
m_canvas->set_as_dirty(); // drawn by the canvas's own RightUp (e.Skip below) or at idle
|
||||
else if (m_on_context_menu && !inline_busy() && is_click) {
|
||||
if (m_on_context_menu && !terminated && !inline_busy() && is_click) {
|
||||
// The menu belongs to what you POINTED AT — and pointing happened at the PRESS, not
|
||||
// at the release, so the raycast uses the press position. Within a 3 px budget the
|
||||
// two are the same pixel in practice; using the press is what makes that a
|
||||
@@ -1179,6 +1167,9 @@ void DesignCanvas::set_readout(const std::string& text)
|
||||
if (m_canvas) m_canvas->set_as_dirty(); // drawn by the next frame (the tool feeds this from one)
|
||||
}
|
||||
|
||||
// Clear of the view cube and the two round view buttons, which own the bottom-left corner.
|
||||
static constexpr float kStatusHudLeftInset = 190.f;
|
||||
|
||||
void DesignCanvas::set_status_text(const wxString& text, const wxColour& colour)
|
||||
{
|
||||
if (text == m_status_hud_last && colour == m_status_hud_colour) return;
|
||||
@@ -1213,11 +1204,8 @@ void DesignCanvas::render_hud()
|
||||
ImGuiWrapper::pop_common_window_style();
|
||||
};
|
||||
if (!m_status_hud_last.IsEmpty()) {
|
||||
// Past the view cube and the round view buttons, which own the bottom-left corner. Asked
|
||||
// of the canvas, which lays them out: they follow the monitor's DPI on Windows, where `em`
|
||||
// does not, so a fixed inset in `em` let them cover the start of the line at 150%.
|
||||
// A sentence can be a sentence: it wraps to the room left of the readout chip.
|
||||
const float left = m_canvas->get_canvas_toolbar_right() + margin;
|
||||
const float left = kStatusHudLeftInset * em;
|
||||
const ImVec4 col = m_status_hud_colour.IsOk()
|
||||
? ImVec4(m_status_hud_colour.Red() / 255.f, m_status_hud_colour.Green() / 255.f,
|
||||
m_status_hud_colour.Blue() / 255.f, 1.f)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <vector>
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/Color.hpp"
|
||||
#include <utility>
|
||||
#include <wx/colour.h>
|
||||
@@ -49,11 +48,10 @@ public:
|
||||
|
||||
// Multi-body display: one GLVolume per body, each coloured distinctly (per-body colour).
|
||||
// `visible` (optional, indexed by body) hides bodies whose flag is false. `body_meshes` is kept
|
||||
// by address (a stable panel member) and read again whenever the selection changes. The
|
||||
// bodies' edge lines are refreshed with them (DesignSketchTool::refresh_body_edges).
|
||||
// by address (a stable panel member) and read again whenever the selection changes.
|
||||
void set_bodies(const std::vector<TriangleMesh>* body_meshes,
|
||||
const std::vector<bool>& visible = {});
|
||||
void clear_mesh(); // no bodies: drops their volumes and the tool's edge lines, pick and selection
|
||||
void clear_mesh();
|
||||
|
||||
void set_preview_mesh(const TriangleMesh& mesh);
|
||||
void clear_preview();
|
||||
@@ -68,6 +66,7 @@ public:
|
||||
const std::vector<SketchEntityConstraintDef>& constraints,
|
||||
const SketchPlane& plane);
|
||||
void set_sketch_tool(DesignSketchTool::Mode mode);
|
||||
void set_sketch_plane(const SketchPlane& plane); // re-plane the live sketch when a reference plane is clicked in 3D
|
||||
void set_sketch_construction(bool c);
|
||||
// Flip the sketch selection between construction and real geometry; returns the
|
||||
// number of entities changed (0 = nothing selected, caller falls back to the mode).
|
||||
@@ -84,8 +83,6 @@ public:
|
||||
void finish_sketch();
|
||||
bool is_sketching() const;
|
||||
void refresh_bed(); // re-sync the bed to the current printer (call on tab activation)
|
||||
// Centre of the Design bed: the printer bed at its home position, whichever plate is current.
|
||||
Vec2d bed_center() const { return m_bed.build_volume().bed_center(); }
|
||||
// The Camera is Plater-owned and shared with Prepare/Preview/Assemble; GLCanvas3D has no
|
||||
// per-canvas camera, so every orbit here would otherwise overwrite what the editor tabs
|
||||
// show. Exactly one of the two views is live at a time, so entering and leaving are the
|
||||
@@ -160,6 +157,7 @@ public:
|
||||
void begin_move_body(int body, const Vec3d& pivot, const Transform3d& base_xform,
|
||||
double body_radius);
|
||||
void clear_move_gizmo();
|
||||
bool moving_body() const;
|
||||
void set_on_body_move_changed(std::function<void(int, const Transform3d&)> cb);
|
||||
// Visual Fillet/Chamfer radius gizmo: when a solid edge is picked, anchor a radius arrow on
|
||||
// it; drag/edit fire the radius callback. Returns false if no edge is currently picked.
|
||||
@@ -236,7 +234,6 @@ public:
|
||||
std::vector<std::string> labels = {}); // clickable labelled reference planes
|
||||
void clear_base_pick();
|
||||
void set_on_datum_base_picked(std::function<void(int)> cb);
|
||||
void set_selected_base(std::function<int()> cb); // the reference plane drawn selected, or -1
|
||||
void set_on_sketch_exit(std::function<void()> cb); // Esc -> exit the tool
|
||||
void set_on_sketch_exit_refused(std::function<void()> cb); // Esc declined: sketch has work
|
||||
void set_on_sketch_notice(std::function<void(const std::string&, bool)> cb); // tool refusals/side effects
|
||||
|
||||
@@ -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("Select a flat face or a reference plane to sketch on"), 0x00000403u, 0, 0, false, false, nullptr, "design_sketch", L("Sketch on the selected flat face or plane, or click one next")},
|
||||
{"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;
|
||||
|
||||
|
||||