Design tab: translatable offer, one vocabulary, reports in the status line

- Offer table: user-facing strings carry the L() marker so xgettext
  extracts them; DesignOffer.hpp, DesignSketchTool.cpp and
  SketchInlineEditor.cpp are listed in localization/i18n/list.txt.
- Offer: model-mode Constrain sits in the same row as the sketch one;
  Interference is wired; Rib shows its R key; a verb that accepts the
  selection but is blocked by the document stays greyed with its reason
  instead of vanishing from the submenu; one refusal wording per verb.
- Extrude infers Join when the profile touches a solid (new
  CadDocument::body_touching_sketch) and on face push/pull; New body in
  free space. Revolve/Sweep/Loft/Boolean use the same result words.
- Interference and volume/area reports go to the status line in mm3/mm2
  instead of modal dialogs; Delete Body no longer asks (it is undoable).
- New feature names match the card header ("Extrude 3"), translated;
  "Coordinate system", "Angle (°)", center/color spelling, translated
  face and length readouts, slot hints say width.
- CAD gizmos in Prepare are selectable only with the CAD feature on; the
  sketch auto-close setting is stored per design.
- Docs: confirm/cancel rules, enabling the feature and MCP in
  design_tab.md; drift-only right-click in interaction-model.md; the
  portability note rewritten to describe the integration as it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
This commit is contained in:
Claude
2026-09-30 08:34:25 +00:00
parent 829124982d
commit c74ddf8bb5
24 changed files with 662 additions and 582 deletions
+38 -5
View File
@@ -14,10 +14,15 @@ already ships for STEP import.
## Getting started
The Design tab is experimental. It exists only in builds with `SLIC3R_CAD` on, and appears
after **Preferences → CAD feature (experimental)** is ticked and the app restarted. Switched off,
Prepare and every project behave exactly as without it; a project that carries a design keeps
its recipe untouched.
1. Open the **Design** tab.
2. Click a face or a reference plane in the viewport, then press `Shift+S` (Sketch). The offer
opens with the sketch tools on it.
3. Draw a closed profile, then press **✓ Confirm** in the floating action bar.
3. Draw a closed profile, then press **✓ Confirm** in the floating action bar (or `Enter`).
4. With the sketch selected, press `Shift+E` (Extrude).
5. Press **Commit to Plate** to hand the solid to Prepare.
@@ -59,7 +64,8 @@ sketch tools — the app hands you the tools directly.
Repeat, Transform, Reference, Modify.
- A family with at least one applicable verb shows it. Several applicable verbs collapse
into a submenu under the family name.
into a submenu under the family name. A verb that is about the selection but blocked by the
document (no body yet, no second sketch) stays in that submenu, greyed, with its reason.
- A family with nothing applicable is **shown greyed in place, with the reason** — e.g.
*"Create — Click a face or a reference plane in the viewport, then a sketch tool"*. It is
not hidden. A control that cannot be used still says what it is and what you would have to
@@ -72,6 +78,22 @@ Redo, Variables, Section view, Origin planes, World axes. They live in the toolb
---
## Confirm and cancel
One rule everywhere, in the feature cards and in the sketch alike:
- `Enter` does what **✓** does; `Esc` does what **✗** does. Neither is ever the only way.
- `Esc` steps back exactly one level — a value field, then the gesture in progress, then the
armed tool, then the selection. It never deletes, discards a sketch or rolls back a feature.
- A click on empty space clears the selection. It never applies anything: a pending fillet,
offset, move or array waits for `Enter` or **✓**.
- Right-click on a tool with something pending drops that gesture; with nothing pending it
opens the offer, like anywhere else.
- Switching to another tool applies a pending operation that is complete and valid, then arms
the new tool.
- A value typed into a field that the geometry cannot take (a zero-length side, an arc of more
than 360°) is refused in place: the field stays open and says why.
## Keyboard
Single letters drive sketch tools **while a sketch is open**; Shift+letter drives feature
@@ -100,8 +122,10 @@ selected by the mode, not by whether a sketch session is running.
| `H` | Chamfer — pick two lines, set the distance |
| `K` | Constrain — finish the live sketch and enter constrain |
| `Q` | Construction toggle — draw the next entity as construction geometry |
| `Del` | Delete the selected sketch entity |
| `Esc` | Cancel the live tool |
| `Del` | Delete the selected sketch entities (nothing selected: nothing happens) |
| `Enter` | Apply what is pending; otherwise end the chain; otherwise drop the tool |
| `Esc` | Undo one level: close the field, drop the gesture, drop the tool, clear the selection |
| `Ctrl+Z` / `Ctrl+Y` | Undo / redo the last sketch edit |
### Feature (when no sketch is open)
@@ -151,7 +175,9 @@ kept. With no section on, `F` is Place on Face — lay the picked face flat on t
## Sketching
A sketch is a closed (or open) 2D profile on a plane or on a flat face of an existing body.
Press `Shift+S`, click the face or plane you want to sketch on, and draw. The toolbar and
Pick the face or plane and press `Shift+S` — the offer opens with the sketch tools. Pressing
`Shift+S` with nothing picked also works: the status line asks for the face or plane, and the
first tool you arm sketches on it. The toolbar and
the offer both carry the sketch tools.
**Entities:** line, polyline, rectangle (corner / centre / oblique / rounded), circle
@@ -290,6 +316,13 @@ edit; the importer warns before you commit to it.
inside the 3MF, so reopening the project restores the editable model rather than a frozen
mesh.
**MCP control** lets an external agent drive the tab through the same kernel the GUI uses.
Start the app with `ORCA_CAD_MCP=1` (socket `/tmp/orca-cad-mcp.sock`) or
`ORCA_CAD_MCP=/path/to.sock`; the CAD feature must be enabled too. Linux and macOS only. A
command that would change the document is refused while the Design tab is busy with it — a
rebuild, an open feature card or a sketch session — so the agent and the user never edit the
same thing at once.
---
## View controls
+4 -3
View File
@@ -20,13 +20,14 @@ design intent that both steps discard. A part modified after slicing should retu
feature history, not to a mesh. Keeping the CAD model inside the slicer preserves that
loop — the nozzle diameter, the build volume and the material are known at design time.
For the integration case in full: [design_tab_upstream_portability.md](docs/design_tab_upstream_portability.md).
For the integration case in full: [design_tab_upstream_portability.md](design_tab_upstream_portability.md).
## How it is built
The `SLIC3R_CAD` CMake flag (default ON) gates the entire tab. With it OFF the tab is not
compiled and the deps prefix matches upstream exactly — the dependency diff is one line in
OCCT's CMake: `BUILD_MODULE_ModelingAlgorithms=OFF → ON`.
OCCT's CMake: `BUILD_MODULE_ModelingAlgorithms=OFF → ON`. At run time the tab is off until
Preferences → CAD feature (experimental) is enabled.
Measured cost table: [cad_dependency_weight.md](docs/cad_dependency_weight.md).
@@ -55,7 +56,7 @@ circle-line tangency solver abort and the internal-thread reference) are fixed.
The vendored solver in `src/libslic3r/slvs/` is **GPL-3.0** (see `src/libslic3r/slvs/LICENSE`),
not LGPL. The combined work is distributable under AGPL-3.0. See the Licensing section of
[design_tab_upstream_portability.md](docs/design_tab_upstream_portability.md) for the
[design_tab_upstream_portability.md](design_tab_upstream_portability.md) for the
AGPLv3/GPLv3 compatibility argument; this point should be confirmed with upstream explicitly.
## Not verified
+56 -141
View File
@@ -1,162 +1,77 @@
# Design (CAD) tab — upstream integration brief
# Design (CAD) tab — integration surface
**Question:** can the Design tab (sketch-first parametric CAD: sketch → constrain →
extrude/revolve/fillet/hole/thread/shell, multi-body, undo, 3MF persistence) land in
mainline OrcaSlicer?
What the Design tab adds to OrcaSlicer, what it touches that upstream already has, and the
constraints that come with it. The dependency cost is measured separately in
[cad_dependency_weight.md](cad_dependency_weight.md); this page does not repeat its numbers.
**Answer: yes, and the ask is far smaller than previously believed.** OCCT is *already*
an OrcaSlicer dependency. We are not asking upstream to adopt a new library; we are
asking it to widen one it already builds, at a measured cost of **3.77 MiB on Windows**.
## Two gates
> ### Corrections to the 2026-06-21 assessment
> That revision was written before the persistence work landed and got two load-bearing
> facts wrong. Both are corrected here from direct measurement of the branch:
>
> 1. **"The real blocker: OCCT … a dependency mainline OrcaSlicer has never carried."**
> **False.** `deps/OCCT/` exists at the merge-base and upstream links it from
> `Format/STEP.cpp`, `Format/svg.cpp`, and `Shape/TextShape.cpp`. Our entire
> dependency diff is **one line**: `BUILD_MODULE_ModelingAlgorithms=OFF → ON`.
> 2. **"vendored SolveSpace solver … LGPL."** **False.** `src/libslic3r/slvs/LICENSE` is
> **GPL-3.0**, not LGPL. This is fine (see Licensing) but must not be misstated.
>
> It also claimed "no changes to Model" — no longer true; 3MF recipe persistence adds one
> `std::string` to `Model`.
- **Compile time: `SLIC3R_CAD`** (`CMakeLists.txt`, default ON in this tree). OFF builds no
CAD code at all and leaves the OCCT deps prefix identical to upstream. ON also turns on
OCCT's `ModelingAlgorithms` module (`deps/OCCT/OCCT.cmake`).
- **Run time: Preferences → CAD feature (experimental)** (`enable_cad_feature`, default OFF).
Off, the Design page and the MCP control socket are not created, the Prepare-toolbar Sketch
and Primitive gizmos cannot be selected, and the Design-only preferences are hidden: Prepare
behaves as it does without the feature. A project that carries a CAD recipe still loads,
keeps the recipe and writes it back unchanged.
## Measured shape of the change
## Dependencies
Against merge-base `449a4cf9fc` (34 commits ahead):
OCCT is already an upstream dependency (STEP import, SVG, text shapes). The Design tab adds no
library; it widens the OCCT build by one module, which builds three extra toolkits — TKFillet,
TKOffset and TKFeat — of which the first two are linked. The module flag is all-or-nothing, so
TKFeat is built although nothing references it.
| | files | lines |
|---|---:|---:|
| **New files** | 138 | +61,720 |
| **Modified upstream files** | 23 | +457 / −75 |
| **Deleted upstream files** | 0 | — |
The constraint solver is vendored in `src/libslic3r/slvs/`: a self-contained subset of
SolveSpace (`libslvs`), GPL-3.0, no external dependencies. GPLv3 §13 and AGPLv3 §13 permit the
combination with OrcaSlicer's AGPL-3.0.
The 62 kLOC headline is inflated by localization. The feature itself:
## Footprint in upstream files
| area | LOC | files |
|---|---:|---:|
| kernel (`src/libslic3r/`) | 15,828 | 37 |
| GUI (`src/slic3r/`) | 19,544 | 14 |
| tests (Catch2) | 2,567 | 6 |
| i18n (unrelated; strip from the CAD PR) | 23,438 | 77 |
Almost all of the change is new files under `src/libslic3r/CAD/`, `src/slic3r/GUI/CAD/`,
`src/libslic3r/slvs/` and `tests/`. The hooks into existing code are small and null-guarded:
**99.3 % of the diff is new files.** The negotiable surface is 457 added lines across 23
files, and nothing upstream is deleted. The largest single hook is `GLCanvas3D.cpp`
(+110/−2): an `m_design_sketch_tool` member plus render/mouse/key hooks, **every one
already null-guarded** — which is why the compile-time gate below is cheap.
- `GLCanvas3D` — a `DesignSketchTool` pointer plus render, mouse and key hooks, each a no-op
when no Design canvas owns it.
- `MainFrame` — the Design page and the Edit menu routing Undo/Redo to it while it is shown.
- `Plater` — the "no geometry" warning distinguishes a recipe-only project.
- `Model` — one `std::string cad_recipe`, empty for non-CAD projects.
- `bbs_3mf` — reading and writing that string (below).
- `Preferences`, `GUI_App` — the two preferences.
No changes to the slicing pipeline (Print/PrintObject/Layer/GCode), Tab, or the
printer-profile/config system.
Nothing in the slicing pipeline (Print, PrintObject, Layer, GCode), Tab, or the profile and
config system changes.
## The dependency ask, precisely
Not "adopt OCCT" — **widen the existing OCCT build**:
```diff
- -DBUILD_MODULE_ModelingAlgorithms=OFF
+ -DBUILD_MODULE_ModelingAlgorithms=ON
```
Cost, measured from the shipped Windows artifact (42 OCCT DLLs, 45.43 MiB total):
| toolkit | size | note |
|---|---:|---|
| `TKFillet.dll` | 2.02 MiB | only exists with the flag ON |
| `TKOffset.dll` | 1.75 MiB | only exists with the flag ON |
| **delta** | **3.77 MiB** | Windows only (OCCT is Shared on Win, Static elsewhere) |
`TKBool` is *not* part of the delta — upstream's `DataExchange` already pulls it in
transitively. On macOS/Linux OCCT links statically, so the cost is only the code actually
referenced, not a 3.77 MiB floor.
**Unmeasured, and we should measure before the call:** clean-deps build-time delta with
the flag ON vs OFF, and the resulting CI runner-minute cost. Do not guess these at him.
## Licensing
- Vendored solver `src/libslic3r/slvs/` — **GPL-3.0**, 9,339 LOC, © Jonathan Westhues,
a self-contained subset of SolveSpace (`libslvs`). No external dependencies.
- OrcaSlicer — **AGPL-3.0** (`LICENSE.txt`).
GPLv3 §13 expressly permits combining a GPLv3 work with an AGPLv3 work; AGPLv3 §13 grants
the converse. The combined work is distributable under AGPL-3.0 with the solver's GPLv3
terms preserved. This is a favourable direction (GPLv3 → into an AGPLv3 project), but it
is a point to **confirm explicitly with upstream**, not to assert unilaterally.
Open question for SoftFever: keep the solver **vendored** (current: pinned, no submodule,
no external build) or move it to `deps/` as a fetched external? Vendoring costs us
upstream-sync burden; `deps/` costs build complexity.
## The one irreversible decision: the 3MF format
Persistence adds an **optional** archive entry and one field:
```cpp
// Model.hpp
std::string cad_recipe; // empty for non-CAD projects
```
## The 3MF entry
```
Metadata/orca_cad.bin // written only when cad_recipe is non-empty
Metadata/orca_cad.bin written only when cad_recipe is non-empty
Metadata/SnapOrca_cad.bin legacy name: read, never written; loses to orca_cad.bin
```
Readers that do not know the entry ignore it; writers skip it entirely when empty. So
existing projects are bit-identical and old readers are unaffected. Good.
Readers that do not know the entry ignore it and writers skip it when the recipe is empty, so
projects without a design are byte-identical and older readers are unaffected. The reader caps
the entry at 1 GiB.
**But the moment upstream ships this, it owns forward-compatibility forever.** Three
things should be settled *before* the first release, because none can be changed after:
The entry is a cereal `PortableBinaryArchive`, portable across endianness and word size. Its
layout is versioned:
1. **Name.** Renamed to `Metadata/orca_cad.bin`.
2. **Encoding.** The recipe is an opaque **cereal `PortableBinaryArchive`** blob whose
layout is the field order of `CadFeature::serialize`. Portable across endianness and
word size — *not* across a field reorder. Append-only is currently a convention held by
discipline, not by any check.
3. **Embedded BRep.** `Import` features embed OCCT's ASCII BRep for the imported solid,
which couples saved project files to an OCCT BRep revision. Alternative: re-import from
the source STEP and store only a reference. Worth deciding deliberately.
- **v5 and later** frame every feature separately, so fields are appended to
`CadFeature::serialize` without a version bump and an older reader skips what it does not
know. Optional document-level data (variables, body names, origin, colours, the
auto-close setting) follows in trailing blocks that a reader stops at cleanly when absent.
- **v4** is the flat layout, read by the frozen `CadFeature::load_flat_v4`, which must never
change.
**Concrete gap we should close before the call.** `test_caddocument.cpp` covers the
in-memory round-trip and correctly refuses a version-999 blob — but there is **no
checked-in v1 fixture on disk**. A reordered field in `CadFeature::serialize` would pass
the entire suite while silently breaking every previously-saved project. Ship a golden
`.bin` fixture generated today plus a test that loads it; that is the only thing that will
hold the format still once real users have files.
Checked-in fixtures `tests/data/cad_recipe_v3.bin`, `cad_recipe_v4.bin` and
`cad_recipe_v5.bin` hold the format still: `test_caddocument.cpp` loads them and checks what
they contain, so a reordered field fails the suite instead of breaking saved projects.
## Proposed PR decomposition
`Import` features embed OCCT's ASCII BRep of the imported solid, which ties a saved project to
a BRep revision OCCT can read.
35 kLOC in one PR is not reviewable. Behind the flag, slices 1–4 are behaviour-neutral for
existing users:
## Undo
1. **Build gate + OCCT flag + Windows packaging guard.** `-DSLIC3R_CAD=ON/OFF`, default
**OFF**. Flips `ModelingAlgorithms=ON`. Includes the guard that asserts every linked
OCCT toolkit has a shipped DLL (already on both forks: `546cef5f42`). ← *this is what
makes SoftFever's "parallel build" a one-line CI matrix entry.*
2. **Vendored `slvs` solver** + its Catch2 tests. No GUI, no OCCT.
3. **CAD kernel** (`CadDocument`, `SketchEngine`, `GeometryEngine`, `Sketch*`) + kernel
tests. Headless, no GUI.
4. **3MF recipe persistence** + golden-fixture regression test.
5. **GUI Design tab** (`DesignPanel`, `DesignCanvas`, `DesignSketchTool`, `GLGizmoSketch`)
+ the 23 upstream hooks.
## Agenda for the call
Questions only SoftFever can answer:
- Does OrcaSlicer *want* to be a CAD-integrated slicer? (Strategic; everything else is mechanical.)
- Default of `SLIC3R_CAD` at merge time, and when it flips ON.
- Vendored solver vs `deps/` external; and confirmation of the GPLv3/AGPLv3 combination.
- Project-file format: neutral name, encoding, embedded-BRep policy, and who owns v1 forward-compat.
- Undo/redo: the Design tab has its own stack; integrate with Orca's snapshot system or keep separate?
- Does he want the i18n work (Romanian, +23 kLOC) as a wholly separate PR? (Yes, almost certainly.)
## Verdict
Portability **high**. The prior "does upstream want OCCT" framing was wrong — OCCT is
already there. What remains is a 3.77 MiB dependency widening, a compile-time gate that
the existing null-guards make cheap, and one file-format decision that must be made before
the first release rather than after.
---
*Revised 2026-07-10 from direct measurement of `cad-mainline` @ `546cef5f42` vs upstream
merge-base `449a4cf9fc`. Supersedes the 2026-06-21 read-only assessment.*
The Design tab keeps its own history (`CadDocument` checkpoints), separate from the Plater's
snapshot stack. The Edit menu's Undo/Redo drive whichever of the two belongs to the page on
screen.
+7 -5
View File
@@ -72,10 +72,11 @@ which is what makes the destructive path unrepresentable rather than merely unli
own handlers so it can consume the event before them:
```cpp
RIGHT_DOWN: remember the press position and the clock, then Skip() // the canvas still seeds the orbit
RIGHT_DOWN: remember the press position, clear `travelled`, then Skip() // the canvas still seeds the orbit
MOTION: travelled |= drift from the press > 3 px // an orbit that came back still orbited
RIGHT_UP: terminated = sketch_tool.take_right_consumed(); // read-and-clear, always
is_click = drift <= 3 px && dt <= 200 ms; // both budgets, or it was navigation
is_click = !travelled && drift <= 3 px; // otherwise it was navigation
if (callback && !terminated && !inline_busy && is_click) {
select_at_screen(press.x, press.y); // raycast at the PRESS, not the release
on_context_menu(ClientToScreen(press));
@@ -84,8 +85,9 @@ RIGHT_UP: terminated = sketch_tool.take_right_consumed(); // read-and
Skip(); // orbit / pan / the handlers underneath
```
Two independent budgets because the two failure modes are independent: drift alone still popped a
menu at the end of a slow, careful orbit. `take_right_consumed()` is how a right-click that
Only distance decides, never time (charter §6.2: no timing-dependent gestures): a slow, deliberate
right-click is still a click. The drift is tracked over the whole press, not just at release, so a
careful orbit that ends where it started does not pop a menu. `take_right_consumed()` is how a right-click that
already meant something to the armed sketch tool (terminate a chain, drop an edit-op) declines to
also mean "open a menu".
@@ -107,7 +109,7 @@ also mean "open a menu".
| **Transient — popup menu** | run the entry | — | close the menu | run the highlighted entry |
| **any** | — | — | *never* deletes, discards or rolls back | — |
Right-hold-and-drag is not in the table on purpose: past 3 px or 200 ms it is navigation, and
Right-hold-and-drag is not in the table on purpose: once it has travelled past 3 px it is navigation, and
navigation does not transition the state machine.
## 4. Visual scaffolding
+15 -5
View File
@@ -42,6 +42,12 @@ def cstr(s):
return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"'
def tstr(s):
# User-facing text: wrapped in the no-op L() marker so xgettext puts it in the catalogue.
# The DesignPanel translates it at use; without the marker it compiles and never translates.
return "nullptr" if s is None else "L(" + cstr(s) + ")"
def main():
A = json.load(open(ATLAS, encoding="utf-8"))
sels = [s["id"] for s in A["selections"]]
@@ -61,6 +67,10 @@ def main():
"",
"#include <cstdint>",
"",
"#ifndef L",
"#define L(s) s // gettext marker, as in slic3r/GUI/I18N.hpp",
"#endif",
"",
"namespace Slic3r { namespace GUI {",
"",
"// What the viewport has selected. Ordered as in tool_atlas.json; the bitmask in",
@@ -83,7 +93,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); translated at use with wxGetTranslation",
" const char* name; // drawing-office word (L10); marked L(); translated at use",
" int row; // 0..7, the ratified index — NEVER reorder",
" const char* key; // shortcut shown in the row, or nullptr",
" const char* action;",
@@ -106,7 +116,7 @@ def main():
"static const char* const kOfferRowNames[] = {",
]
for s in A["slots"]:
lines.append(f' "{s["label"]}",')
lines.append(f' {tstr(s["label"])},')
lines += [
"};",
f"static const int kOfferRowCount = {len(slots)};",
@@ -131,12 +141,12 @@ 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"]), cstr(v["name"]), slots.index(v["slot"]),
cstr(v.get("key")), cstr(v.get("action")), cstr(v.get("refusal")),
cstr(v["id"]), tstr(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",
"true" if v.get("mode") == "sketch" else "false",
cstr(v.get("family")), cstr(v.get("icon")), cstr(v.get("hint"))))
tstr(v.get("family")), cstr(v.get("icon")), tstr(v.get("hint"))))
lines += [
"};",
f"static const int kOfferVerbCount = {len(A['verbs'])};",
+38 -36
View File
@@ -326,7 +326,7 @@
"id": "rib",
"name": "Rib",
"slot": "add",
"key": null,
"key": "R",
"feature": "Rib",
"mcp": "rib",
"accepts": [
@@ -343,7 +343,7 @@
},
{
"id": "boolean",
"name": "Union",
"name": "Join",
"slot": "add",
"key": "Shift+B",
"feature": "Boolean",
@@ -354,7 +354,7 @@
"needs": {
"bodies": 2
},
"refusal": "Boolean needs two bodies — create or import a second solid",
"refusal": "Boolean needs two bodies — add or import a second one",
"gui": true,
"action": "btn:bool#0",
"icon": "design_boolean",
@@ -373,7 +373,7 @@
"needs": {
"bodies": 2
},
"refusal": "Boolean needs two bodies — create or import a second solid",
"refusal": "Boolean needs two bodies — add or import a second one",
"gui": true,
"action": "btn:bool#1",
"mode": null,
@@ -393,7 +393,7 @@
"needs": {
"bodies": 2
},
"refusal": "Boolean needs two bodies — create or import a second solid",
"refusal": "Boolean needs two bodies — add or import a second one",
"gui": true,
"action": "btn:bool#2",
"mode": null,
@@ -483,7 +483,7 @@
"needs": {
"sheet": true
},
"refusal": "target is not a sheet body",
"refusal": "Thicken Surface needs a surface body — make one with a Surface tool first",
"gui": true,
"action": "fly:surface#5",
"icon": "design_thicken",
@@ -507,7 +507,7 @@
"gui": true,
"action": "key:S+H",
"icon": "design_hole",
"hint": "Drill a hole, centred on a picked face or placed on a plane"
"hint": "Drill a hole, centerd on a picked face or placed on a plane"
},
{
"id": "thread",
@@ -543,7 +543,7 @@
"needs": {
"bodies": 1
},
"refusal": "Shell needs a solid body",
"refusal": "Shell needs a solid body — add or import one first",
"gui": true,
"action": "key:S+K",
"icon": "design_shell",
@@ -569,7 +569,7 @@
"needs": {
"bodies": 1
},
"refusal": "Create a solid body to cut first",
"refusal": "Cut needs a solid body — add or import one first",
"gui": true,
"action": "key:S+X",
"icon": "design_cut",
@@ -594,7 +594,7 @@
"needs": {
"bodies": 1
},
"refusal": "Split needs a solid body",
"refusal": "Split needs a solid body — add or import one first",
"gui": false,
"action": null,
"icon": null,
@@ -677,7 +677,7 @@
"needs": {
"sheet": true
},
"refusal": "target is not a sheet body",
"refusal": "Surface Offset needs a surface body — make one with a Surface tool first",
"gui": true,
"action": "fly:surface#4",
"icon": "design_offset",
@@ -699,7 +699,7 @@
"needs": {
"bodies": 1
},
"refusal": "Create a solid body to pattern first",
"refusal": "Pattern needs a solid body — add or import one first",
"gui": true,
"action": "btn:pat#0",
"icon": "design_array",
@@ -721,7 +721,7 @@
"needs": {
"bodies": 1
},
"refusal": "Create a solid body to pattern first",
"refusal": "Pattern needs a solid body — add or import one first",
"gui": true,
"action": "btn:pat#1",
"mode": null,
@@ -768,7 +768,7 @@
"needs": {
"bodies": 1
},
"refusal": "Pattern on curve needs a body and a curve",
"refusal": "Pattern on Curve needs a body and a curve",
"gui": false,
"action": null,
"icon": null,
@@ -816,11 +816,11 @@
"needs": {
"bodies": 2
},
"refusal": "A mate needs two coordinate systems",
"refusal": "Mate needs two coordinate systems — create them first",
"gui": true,
"action": "fly:placement#2",
"icon": "design_c_coincident",
"hint": "Assembly: align two CoordSys features (fastened, planar, revolute, slider, cylindrical)"
"hint": "Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)"
},
{
"id": "align",
@@ -835,7 +835,7 @@
"needs": {
"bodies": 1
},
"refusal": "Align needs a body",
"refusal": "Align needs a body — add or import one first",
"gui": false,
"action": null,
"icon": null,
@@ -885,7 +885,7 @@
},
{
"id": "coordsys_v",
"name": "Coord Sys",
"name": "Coordinate system",
"slot": "reference",
"key": "Shift+C",
"feature": "CoordSys",
@@ -972,7 +972,7 @@
},
{
"id": "mass_props",
"name": "Mass",
"name": "Volume and area",
"slot": "reference",
"key": null,
"feature": null,
@@ -1003,14 +1003,15 @@
"feature": null,
"mcp": "check_interference",
"accepts": [
"body_solid",
"bodies_2"
],
"needs": {
"bodies": 2
},
"refusal": null,
"gui": false,
"action": null,
"refusal": "Interference needs at least two bodies",
"gui": true,
"action": "btn:interference",
"icon": null,
"hint": "Check whether two bodies overlap — reports, changes nothing"
},
@@ -1082,7 +1083,7 @@
},
{
"id": "colour",
"name": "Colour",
"name": "Color",
"slot": "modify",
"key": null,
"feature": null,
@@ -1104,7 +1105,7 @@
"gui": true,
"action": "btn:colour",
"icon": "color_palette",
"hint": "Set the selected body's display colour"
"hint": "Set the selected body's display color"
},
{
"id": "delete",
@@ -1227,7 +1228,7 @@
},
{
"id": "sk_rect_center",
"name": "Centre rectangle",
"name": "Center rectangle",
"slot": "create",
"key": null,
"feature": "Sketch",
@@ -1296,7 +1297,7 @@
},
{
"id": "sk_circle",
"name": "Centre circle",
"name": "Center circle",
"slot": "create",
"key": "C",
"feature": "Sketch",
@@ -1411,7 +1412,7 @@
},
{
"id": "sk_arc_center",
"name": "Centre-point arc",
"name": "Center-point arc",
"slot": "create",
"key": null,
"feature": "Sketch",
@@ -1453,7 +1454,7 @@
"action": "key:S",
"family": "Slot",
"icon": "design_slot",
"hint": "Slot — two centerline ends, then end radius"
"hint": "Slot — two centerline ends, then the width"
},
{
"id": "sk_slot_arc",
@@ -1567,7 +1568,7 @@
"mode": "sketch",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Triangle — click centre, then a vertex"
"hint": "Triangle — click center, then a vertex"
},
{
"id": "sk_poly_4",
@@ -1590,7 +1591,7 @@
"mode": "sketch",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Square — click centre, then a vertex"
"hint": "Square — click center, then a vertex"
},
{
"id": "sk_poly_5",
@@ -1613,7 +1614,7 @@
"mode": "sketch",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Pentagon — click centre, then a vertex"
"hint": "Pentagon — click center, then a vertex"
},
{
"id": "sk_polygon",
@@ -1636,7 +1637,7 @@
"action": "btn:poly#6",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Hexagon — click centre, then a vertex"
"hint": "Hexagon — click center, then a vertex"
},
{
"id": "sk_poly_8",
@@ -1659,7 +1660,7 @@
"mode": "sketch",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Octagon — click centre, then a vertex"
"hint": "Octagon — click center, then a vertex"
},
{
"id": "sk_poly_12",
@@ -1682,7 +1683,7 @@
"mode": "sketch",
"family": "Polygon",
"icon": "design_polygon",
"hint": "Dodecagon — click centre, then a vertex"
"hint": "Dodecagon — click center, then a vertex"
},
{
"id": "sk_poly_inscribed",
@@ -2048,7 +2049,7 @@
{
"id": "constrain",
"name": "Constrain sketch",
"slot": "modify",
"slot": "reference",
"key": null,
"feature": "Sketch",
"mcp": null,
@@ -2067,7 +2068,8 @@
"note": [
"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."
"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."
]
},
{