fix(capi): cube node shape rendered black canvas (issue #47)#48
Merged
Conversation
…mplements 3D draw hooks) CUBE node shape (LP_SHAPE_TYPE == 6) rendered an empty/black canvas via the C API: CDasherViewSquare drives cube rendering through Screen()->DrawCube / Draw3DLabel / DrawProjectedRectangle / FinishRender3D, and CommandScreen (the C-API screen adapter) never overrode them — the base-class defaults are all no-ops, so zero draw commands were emitted for cube nodes (only the clear-screen fired). Fixes issue #47. Fix (Option A from the issue, localized to the C-API screen): override the 3D hooks in CommandScreen to translate cube drawing into the existing flat command format — DrawCube emits opcode 4 (filled front-face rect) + opcode 3 (outline); Draw3DLabel emits opcode 5 (text); DrawProjectedRectangle emits opcode 4 (cube-mode crosshair bar). FinishRender3D stays the base no-op (nothing to composite in 2D). The flat buffer carries no 3D extrusion, so cube mode renders as flat shaded rectangles over this API. DrawProjectedRectangle previously took no colour and CDasherScreen has no palette access, so it could not render the crosshair bar correctly. Added a colour argument (supplied by the view from its palette); no in-tree overrides existed and the only caller is the cube path. Out-of-tree 3D screens that override it need a one-line update to accept the colour. Documented opcode 6 (set line width, already emitted but undocumented) and the cube-mode command mapping in dasher.h. Test: test_cube_geometry.cpp confirms cube mode now emits filled rects (cube faces) and text labels instead of just clear-screen; fails on the unpatched code (0 labels, ~1 decorative rect/frame). Signed-off-by: will wade <willwade@gmail.com>
This was referenced Jul 25, 2026
willwade
added a commit
that referenced
this pull request
Jul 25, 2026
#49) Cube mode (LP_SHAPE_TYPE == 6) reached the C API in #48 but looked identical to flat rectangles: DrawCube discarded the CubeDepthLevel and emitted one opcode-4 rect per node. Worse, the multi-face shaded-rectangle emulation I tried first does not actually work — Dasher renders depth-first, so each child's flat front face paints over the parent's shaded top/right faces, leaving only leaf nodes with any visible 3D. The flat command buffer (painter's algorithm only, no depth buffer) cannot replicate GTK/Windows cube mode. Fixes issue #49 (revised per review). Fix (Option B from the issue): DrawCube now emits a dedicated opcode-7 record carrying the raw cube data — [7, x1, y1, x2, y2, extrusionLevel, fillARGB, outlineARGB, thickness] (9 ints) — so the depth info survives the buffer intact. Frontends render cube mode in two passes: (1) opcodes 0-6 as the flat layout, (2) opcode-7 cubes composited as a 3D overlay on top with their own graphics API. Opcode 7 is the only variable-length command (9 ints vs 6); parsers read the opcode first and advance accordingly. Docs: dasher.h and docs/C_API.md document opcode 7, the variable-length parse rule, and the two-pass rendering model (with a parse example). Updated the 'divide by 6' notes. Tests: cube nodes now arrive as opcode 7 (not opcode 4); labels as opcode 5. Added a case verifying the per-node extrusionLevel varies across the tree (>=2 distinct values) — i.e. the depth data is no longer discarded. The parse helper handles the 9-int stride. Signed-off-by: will wade <willwade@gmail.com>
willwade
added a commit
that referenced
this pull request
Jul 25, 2026
#49) Cube mode (LP_SHAPE_TYPE == 6) reached the C API in #48 but looked identical to flat rectangles: DrawCube discarded the CubeDepthLevel and emitted one opcode-4 rect per node. A shaded-rectangle emulation does not work either — Dasher renders depth-first, so each child's flat front face paints over the parent's shaded faces, leaving only leaf nodes with any visible 3D. The flat command buffer (painter's algorithm only, no depth buffer) cannot replicate GTK/Windows cube mode. Fixes issue #49 (revised per review). Fix (Option B from the issue): DrawCube emits a dedicated opcode-7 record carrying the raw cube data so the depth info survives the buffer intact. Frontends render cube mode in two passes: (1) opcodes 0-6 as the flat layout, (2) opcode-7 cubes composited as a 3D overlay on top via the frontend's own graphics API. Encoding: opcode 7 occupies TWO 6-int slots (12 ints), both prefixed with 7 — slot 1: [7, x1, y1, x2, y2, extrusionLevel], slot 2: [7, fillARGB, outlineARGB, thickness, 0, 0]. This keeps the buffer uniformly 6-int divisible: a parser advancing 6 ints per slot never desyncs, and frontends that don't know opcode 7 just see two unknown slots per cube and skip them (graceful degradation). Cost is 3 unused ints per cube — negligible (~50-200 visible nodes in a >=10K-int buffer). Docs: dasher.h and docs/C_API.md document opcode 7, the two-slot layout, the two-pass rendering model, and a parse example. Draw3DLabel still emits opcode 5 and DrawProjectedRectangle opcode 4 (crosshair bar), so the flat-layout pass already has labels + crosshair. Tests: cube nodes arrive as opcode 7 (not opcode 4); labels as opcode 5. Added cases verifying (a) the buffer stays 6-int divisible (cc % 6 == 0) and (b) the per-node extrusionLevel varies across the tree (>=2 distinct values) — i.e. the depth data is no longer discarded. The parse helper advances 12 ints for opcode 7. Signed-off-by: will wade <willwade@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When
LP_SHAPE_TYPE == CUBE(6), the canvas went black via the C API — only the background clear-screen fired, no nodes visible. The app didn't crash.Root cause
CDasherViewSquarein cube mode drives rendering throughScreen()->DrawCube(per node face),Screen()->Draw3DLabel(text),Screen()->DrawProjectedRectangle(crosshair bar), andScreen()->FinishRender3D(composite). All four are virtual onCDasherScreenwith empty no-op defaults (DasherScreen.h:98-106).The C API's
CommandScreen(CAPI.cpp) — the screen adapter that translates DasherCore rendering into the flatint[]command buffer consumed by every frontend — never overrode them. So zero draw commands were emitted for cube nodes. Confirmed by opcode histogram in cube mode:0:10 2:10 4:10 6:10per 10 frames (just clear-screen + one decorative rect/line), with zero labels.Same class of bug as the circle crashes (#46) — a rendering mode the C-API path was never tested with.
Fix (Option A from the issue, localized to the C-API screen)
Override the 3D hooks in
CommandScreento translate cube drawing into the existing flat command format (no new opcodes):DrawCube→ opcode 4 (filled front-face rect) + opcode 3 (outline when requested)Draw3DLabel→ opcode 5 (text), reusingDrawStringDrawProjectedRectangle→ opcode 4 (the cube-mode crosshair bar)FinishRender3D→ keeps the base no-op (nothing to composite in 2D)The flat buffer has no 3D backend, so cube mode renders as flat shaded rectangles over this API (the issue explicitly accepts losing the 3D extrusion: "simpler but loses the cube visual").
One clean interface change
DrawProjectedRectanglepreviously took no colour, andCDasherScreenhas no palette access (only the view has). I added acolorargument supplied by the view from its palette. No in-tree overrides existed and the only caller is the cube path (verified by full-repo grep + clean build of all targets). Out-of-tree 3D screens that override it need a one-line update to accept the colour.Why not Option B (capability check + flat fallback)?
Both approaches produce flat rectangles via the command buffer; the only real difference is where the fallback lives. Option A is localized to
CAPI.cpp(no core view logic change), reuses the cube path's blanking/margin behaviour, and keeps the interface improvement (passing the crosshair colour).Docs
dasher.h: documented opcode 6 (set line width — already emitted byPolylinebut undocumented) and noted that cube mode maps to opcode 3/4/5.Test
test_cube_geometry.cpp— drives cube mode via the C API and asserts that filled rects (cube faces) and text labels are emitted. Fails on unpatched code (0 labels, ~1 decorative rect/frame) and passes after.Verification
--Werrordry-run)dasher_draw_snapshot_tests(golden) unaffected — default-shape rendering unchanged