Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Status toast messages (`GUI::show_message` / `ezy.msg`) are also written to the **Log** window.
- WASM (OCCT 7.9.3): stop forcing a near-white AIS `SetColor` after every material apply. That OCCT 8 GLES workaround had been running on all Emscripten builds and made steel, gold, and other presets look identical (glass still showed transparency).
- WASM: clear AIS `OwnColor` when applying a Shape List material so presets are not stuck on a forced color. Prefer the **OCCT 7.9.3** wasm kit — OCCT 8.x GLES still has known shading regressions (`docs/building-occt.md`).

### Cross-section

- Added a temporary **Shape cross-section** preview tool for selected solids. Entering the tool with solids already selected updates the preview immediately; changing the selection while in the tool also updates it. Options choose a shared local **XY**, **XZ**, or **YZ** plane (first-selected axes, selection-bbox center), **Invert normal** (flips the annotation arrow and positive-offset direction while keeping the plane in place), **Hide back side** (on by default; display-only AIS clip opposite the yellow arrow), a bounding-box **Offset** slider (Ctrl+click to type; auto-updates preview) in project units, and **Clip** (half-space cut: keeps the positive-normal side, deletes the originals, adds clipped replacements; undoable). A translucent yellow plane and normal arrow annotate the cut. Solids the plane misses are skipped; the preview clears only if nothing in the selection is cut.
- Added a temporary **Shape cross-section** preview tool for selected solids. Entering the tool with solids already selected updates the preview immediately; changing the selection while in the tool also updates it. Options choose a shared local **XY**, **XZ**, or **YZ** plane (first-selected axes, selection-bbox center), **Invert normal** (flips the annotation arrow and positive-offset direction while keeping the plane in place), **Hide back side** (on by default; display-only AIS clip opposite the yellow arrow), **Show section outline** (off by default; toggles the cyan intersection wires), a bounding-box **Offset** slider (Ctrl+click to type; auto-updates preview) in project units, **Clip** (half-space cut: keeps the positive-normal side, deletes the originals, adds clipped replacements; solids fully on the discarded side are removed with no replacement; undoable), and **Cross section sketch** (new sketch on the cutting plane with imported line/circle section edges; undoable). A translucent yellow plane and normal arrow annotate the cut. Solids the plane misses are skipped; the preview clears only if nothing in the selection is cut.
- Desktop preview sections multiple selected solids in parallel (worker pool); WASM remains single-threaded for the per-solid pool. Bounding-box culling skips solids that cannot meet the plane. Offset/plane dragging updates the yellow plane immediately and runs section wires asynchronously (cancel + latest-pending) so the Options slider stays responsive.
- Shapes now carry a bbox-centered local frame that follows baked move/rotate/scale transforms and persists in project JSON and shape undo records.

Expand Down
4 changes: 2 additions & 2 deletions agents/drafts/prs/active/gh-219-cross-section-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Add temporary shape cross-section preview tool

## Summary

- Temporary shape cross-section preview (local XY/XZ/YZ + offset, Invert normal, Hide back side, Clip).
- Temporary shape cross-section preview (local XY/XZ/YZ + offset, Invert normal, Hide back side, Show section outline, Clip, Cross section sketch).
- Async section wires + desktop parallel pool; WASM serial (see `wasm-multithreading.md`).
- Shape local frames for plane orientation / persistence.

Expand All @@ -28,5 +28,5 @@ Add temporary shape cross-section preview tool

- [ ] Desktop Release build
- [ ] `EzyCad_tests --gtest_filter=Shp_cross_section.*:Shp_test.Cross_section*`
- [ ] Manual enter / selection / plane / offset / Hide back / Clip / Esc
- [ ] Manual enter / selection / plane / offset / Hide back / Show section outline / Clip / Cross section sketch / Esc
- [ ] Docs sync checklist
22 changes: 9 additions & 13 deletions agents/plans/sketch-from-shape-section.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: deferred
status: partial
topic: sketch-from-shape
depends_on:
- shp-origin-orientation
Expand All @@ -12,12 +12,7 @@ depends_on:

## Status

**Deferred.** Needs experimentation first:

1. [shp-origin-orientation.md](shp-origin-orientation.md) — persistent shape frame (origin + orientation).
2. [cross-section-tool.md](cross-section-tool.md) — prototype section display and OCCT curve discovery.

Do **not** implement sketch import until the cross-section prototype has findings (curve types, plane UX, fail cases).
**Partial.** Cross-section Options **Cross section sketch** imports cached section **line** and **circle** edges into a new sketch (`Occt_view::create_sketch_from_cross_section`). Remaining work: weld/dedup for boolean sections, ellipse/B-spline densify, script binding.

## Goal (end state)

Expand All @@ -43,18 +38,19 @@ Earlier draft used world XY/XZ/YZ through bbox center — superseded once shape
3. Weld nodes → `rebuild_faces()` → one undo commit.

**Reuse:** `to_2d`/`to_3d`, `add_sketch`, `Sketch_edges`, `Sketch_op_recorder`.
**New:** section→sketch importer after prototype validates section quality.
**Shipped MVP:** Options button on the cross-section tool; caches section plane with compound.

## Phases (after prerequisites)

- **MVP:** One solid; plane from shape frame; line+circle; polyline fallback; undo; tests.
- **Later:** Multi-shape; densify; script; face-pick plane; optional `originating_shape_id`.
- **MVP:** One solid; plane from shape frame; line+circle; undo; tests. *(Delivered via cross-section tool button; multi-solid uses shared plane.)*
- **Later:** densify; script; face-pick plane; optional `originating_shape_id`; weld overlapping boolean edges.

## Acceptance (when un-deferred)

- [ ] Editable sketch edges from section; undo works; fail closed on miss.
- [ ] Docs + CHANGELOG; curve rules match cross-section findings.
- [x] Editable sketch edges from section; undo works; fail closed on miss.
- [x] Docs + CHANGELOG; curve rules match cross-section findings (line + circle first).
- [ ] Weld/dedup for fused solids; densify unsupported curves.

## Out of scope until unblocked

C++ sketch import; changing planar-face behavior.
Changing planar-face behavior.
2 changes: 1 addition & 1 deletion docs/scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ ezy.Shp
| Method | Purpose |
| ------------------------------- | ---------------------------------------------- |
| `ezy.log(msg)` | Print to the console and the main **Log** pane |
| `ezy.msg(text)` | Show a status message |
| `ezy.msg(text)` | Status toast and append to the main **Log** pane |
| `ezy.get_mode()` | Current application mode name (string) |
| `ezy.set_mode(name)` | Switch mode by name |
| `ezy.save_occt_view_settings()` | Write settings JSON (including view colors) |
Expand Down
2 changes: 1 addition & 1 deletion docs/usage-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Under **Material**:

- Document preset for new solids that do not inherit from a clicked shape (toolbar **Box**, **polar duplicate** output, and similar). To change material on an existing solid, use the [Shape List](usage.md#shape-list).

For other non-sketch Options content, see the matching tool section in the usage guide. In particular, **Shape cross-section** provides local **XY/XZ/YZ**, **Invert normal**, **Hide back side**, a bbox-ranged **Offset** slider (Ctrl+click to type), **Clip** (half-space cut, replaces selection), and automatic preview updates when the selection changes; see [Shape cross-section tool](usage.md#shape-cross-section-tool).
For other non-sketch Options content, see the matching tool section in the usage guide. In particular, **Shape cross-section** provides local **XY/XZ/YZ**, **Invert normal**, **Hide back side**, **Show section outline**, a bbox-ranged **Offset** slider (Ctrl+click to type), **Clip** (half-space cut, replaces selection), **Cross section sketch** (new sketch from section outline), and automatic preview updates when the selection changes; see [Shape cross-section tool](usage.md#shape-cross-section-tool).

### Sketch tools

Expand Down
8 changes: 4 additions & 4 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ EzyCad (Easy CAD) is an open-source CAD application for hobbyist machinists to d
6. **Log Window**
- View operation history
- Check for errors and warnings
- Monitor system status
- Monitor system status (including status toast messages)

### Help menu

Expand Down Expand Up @@ -599,13 +599,13 @@ The polar duplicate tool allows you to create multiple copies of selected shapes

![Cross-section Tool](res/icons/Curves_ExtractSubshape.png)

Use the cross-section tool to inspect where a plane cuts one or more selected solids. The result is a temporary cyan wire preview. A translucent yellow rectangle outlines the cutting plane, with an arrow showing its positive normal direction. These annotations do not modify the solids or create a sketch.
Use the cross-section tool to inspect where a plane cuts one or more selected solids. The result is a temporary cyan wire preview (optional; **Show section outline** is off by default). A translucent yellow rectangle outlines the cutting plane, with an arrow showing its positive normal direction. These annotations do not modify the solids until you **Clip** or **Cross section sketch**.

1. Select one or more solids.
2. Click **Shape cross-section** in the toolbar. If solids were already selected, the preview updates immediately.
3. In **Options**, choose **Local XY**, **Local XZ**, or **Local YZ**. Use **Invert normal** to flip the yellow arrow (and the positive-offset direction); the cut stays in place. **Hide back side** is on by default and preview-clips the selected solids so geometry on the negative-normal side of the plane is not drawn (opposite the yellow arrow); turn it off to show the full solids.
3. In **Options**, choose **Local XY**, **Local XZ**, or **Local YZ**. Use **Invert normal** to flip the yellow arrow (and the positive-offset direction); the cut stays in place. **Hide back side** is on by default and preview-clips the selected solids so geometry on the negative-normal side of the plane is not drawn (opposite the yellow arrow); turn it off to show the full solids. **Show section outline** is off by default; turn it on for cyan intersection wires. The yellow plane annotation stays visible either way.
4. Drag **Offset** (or Ctrl+click to type) along that plane's local normal. The slider range follows the selected solids' bounding box in the current project unit. The yellow plane updates immediately while you drag; cyan section wires catch up asynchronously so the control stays responsive.
5. Click **Clip** to keep the positive-normal half of each selected solid as a new shape and delete the originals (undoable). Leave the tool (or clear the selection) to remove the preview and any hide-back display clip.
5. Click **Clip** to keep the positive-normal half of each selected solid as a new shape and delete the originals (undoable). Solids that lie entirely on the discarded side are removed with no replacement. Click **Cross section sketch** to create a new sketch on the cutting plane and import the section outline as editable line and circle edges (ellipses and other curve types are skipped). Leave the tool (or clear the selection) to remove the preview and any hide-back display clip.
6. Changing the selection or **Section plane** (or dragging **Offset**) updates the preview automatically. If nothing is selected, Options shows a bold prompt to select one or more shapes.

Each solid has a local frame used for orientation. New solids start with a world-aligned frame at the center of their bounding box; moving, rotating, or scaling a solid updates that frame. With multiple selected solids, all of them share one cutting plane: orientation from the first selected solid's local axes, and origin at the selection bounding-box center. Offset moves that shared plane along its normal.
Expand Down
6 changes: 4 additions & 2 deletions src/doc/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Tests use `sketch_left_click` to simulate sketch LMB without ImGui mouse positio
| `Move` / `Rotate` / `Scale` | `options_*_mode_` (constraints, axis, material) |
| `Shape_chamfer` / `Shape_fillet` | mode + radius/distance |
| `Shape_polar_duplicate` | angle, count, rotate/combine, **Dup** button |
| `Shape_cross_section` | local XY/XZ/YZ, invert normal, hide back side, bbox-ranged offset, Clip (replace) |
| `Shape_cross_section` | local XY/XZ/YZ, invert normal, hide back side, show section outline, bbox-ranged offset, Clip, Cross section sketch |
| `Sketch_inspection_mode` | `options_sketch_common_` |
| Each sketch tool mode | Matching `options_sketch_*_mode_` |
| `Sketch_operation_axis` | Mirror / Revolve / Clear axis |
Expand All @@ -208,6 +208,8 @@ Shared sketch controls (snap, midpoint nodes, place-from-center) live in `option
| 8 | `add_*_dialog_` | Primitive / sketch creation popups |
| 9 | `log_window_`, consoles, `settings_`, `dbg_` | Log, Lua/Python, Settings |

`GUI::show_message` drives the transient status toast (`message_status_window_`) and also appends via `log_message`. `show_error_dialog` logs `title: message` once and toasts the title only.

Sketch List expand **Faces**: each face row supports **`E`** and right-click **Extrude** via `GUI::sketch_list_extrude_face_` (`set_mode(Sketch_face_extrude)` + `Occt_view::begin_sketch_face_extrude` / `Shp_extrude::begin_face_extrude`). Hovering a **Faces**, **Edges**, or **Nodes** row calls `Occt_view::set_sketch_list_hover_{face,edge,node}` (temporarily displays the AIS when hidden outside sketch modes; uses `Graphic3d_ZLayerId_Topmost` so solids do not occlude the highlight).

**Shape List outliner:** `shape_list_` draws a tree of document shapes/groups via `shape_children(0)` and recursive `TreeNodeEx` rows. Fixed-width vis/disp/mat columns are on the left; the name column stretches on the right with tree indent (`IndentEnable` on name only). An empty pad row after the last item is a drag-drop target for document root (`reparent_shape(..., 0)`); it shows a "Move to root" hint while dragging. Groups support expand/collapse (`ui.shapeList.expanded`), drag-drop reparent (`EZY_SHAPE_ID` payload), Group / New group / Ungroup, and cascade delete. Clicking a group sets `Occt_view::current_group_id` (including empty groups) and selects descendant solids; clicking a solid selects it and sets current group to its parent. New primitives/extrudes/revolves parent under the current group. Ctrl+click multi-selects. Hover uses `set_shape_list_hover` on leaf solids only. `ui.shapeList.currentGroupId` is persisted in `.ezy`.
Expand Down Expand Up @@ -235,7 +237,7 @@ Toolbar buttons hold `std::variant<Mode, Command>`. `Command` (`Shape_cut`, `Sha

Mode buttons call `set_mode`. Active state tracks `m_mode`.

The cross-section toolbar button enters `Mode::Shape_cross_section`. Entering the mode snapshots any selected solids first (selection-mode and sketch-faint redisplay Erase AIS selection), restores them after that sync, and calls `Shp_cross_section::preview` (blocking) with the snapshot. While the mode is active, Options updates the yellow plane annotation immediately on plane/offset/hide-back changes (`request_preview`), then `poll`s a background section job (desktop `std::async`; WASM chunks one solid per frame). At most one running job plus one pending (latest only); moving the slider cancels/coalesces work so the UI stays responsive. **Hide back side** attaches a temporary per-shape `Graphic3d_ClipPlane` for display only. **Clip** runs a half-space `BRepAlgoAPI_Common`, deletes the input solids, and adds clipped replacements (`Shape_replace_delta`). Temporary AIS and jobs are cleared when the mode is left, when the selection becomes empty, after a successful **Clip**, or on `clear()`.
The cross-section toolbar button enters `Mode::Shape_cross_section`. Entering the mode snapshots any selected solids first (selection-mode and sketch-faint redisplay Erase AIS selection), restores them after that sync, and calls `Shp_cross_section::preview` (blocking) with the snapshot. While the mode is active, Options updates the yellow plane annotation immediately on plane/offset/hide-back changes (`request_preview`), then `poll`s a background section job (desktop `std::async`; WASM chunks one solid per frame). At most one running job plus one pending (latest only); moving the slider cancels/coalesces work so the UI stays responsive. **Hide back side** attaches a temporary per-shape `Graphic3d_ClipPlane` for display only. **Clip** runs a half-space `BRepAlgoAPI_Common`, deletes the input solids, and adds clipped replacements (`Shape_replace_delta`). **Cross section sketch** imports cached section line/circle edges into a new sketch (`Sketch_struct_delta::Add`) and switches to sketch inspection. Temporary AIS and jobs are cleared when the mode is left, when the selection becomes empty, after a successful **Clip**, or on `clear()`.

## Typical developer usage

Expand Down
2 changes: 1 addition & 1 deletion src/doc/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Aliases: global `view` == `ezy.view`; `Shp` == `ezy.Shp`; `view.add_sketch` / `f
| Method | C++ delegate |
| ------------------------------- | -------------------------------------- |
| `ezy.log(msg)` | `GUI::log_message` + console history |
| `ezy.msg(text)` | `GUI::show_message` |
| `ezy.msg(text)` | `GUI::show_message` (toast + Log) |
| `ezy.get_mode()` | `GUI::get_mode()` -> mode name string |
| `ezy.set_mode(name)` | `GUI::set_mode(mode_from_string(...))` |
| `ezy.save_occt_view_settings()` | `GUI::save_occt_view_settings()` |
Expand Down
2 changes: 1 addition & 1 deletion src/doc/shape.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Protected helpers used by all operation classes:
| `shp_fillet.h` | `Shp_fillet` | `add_fillet(..., Fillet_mode)` -- `BRepFilletAPI_MakeFillet`; modes: Shape, Face, Wire, Edge (`mode.h`). |
| `shp_chamfer.h` | `Shp_chamfer` | `add_chamfer(..., Chamfer_mode)` -- diagonal distance converted to setback (`dist/sqrt(2)`). |
| `shp_polar_dup.h` | `Shp_polar_dup` | Arm on sketch plane; `dup()` copies selection at polar steps; options: rotate copies, combine into one solid. |
| `shp_cross_section.h` | `Shp_cross_section` | Shared cutting-plane preview: immediate yellow plane AIS; cyan section wires via async job (desktop `std::async` + per-solid pool; WASM one-solid-per-`poll` chunks); running+latest-pending cancel/coalesce; optional hide-back AIS clip; **Clip** half-space-commons and replaces inputs. |
| `shp_cross_section.h` | `Shp_cross_section` | Shared cutting-plane preview: immediate yellow plane AIS; cyan section wires via async job (desktop `std::async` + per-solid pool; WASM one-solid-per-`poll` chunks); running+latest-pending cancel/coalesce; optional hide-back AIS clip; **Show section outline** (default off) toggles cyan wires without recompute; **Clip** half-space-commons and replaces inputs (fully discarded solids are removed only); **Cross section sketch** imports cached section line/circle edges into a new sketch. |
| `shp_info.h` | `namespace shp_info` | `collect(TopoDS_Shape, Display_meta*)` -> labeled lines for Shape info dialog. |

## Input routing (from UI / `Occt_view`)
Expand Down
Loading
Loading