Releases: alfu32/k3d
Release list
android, object picking and DXF fixes
Patched the three regressions.
- Android Ctrl/copy mode
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolController.kt
- Android now syncs copyMode from the live Ctrl modifier every frame for tools that support copy mode.
- This avoids relying on libGDX receiving raw Ctrl key events, which Android was intercepting.
- Android render save
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- core/src/main/kotlin/com/github/alfu32/sketch/render/RenderController.kt
- Save on Android now opens the system ACTION_CREATE_DOCUMENT picker and writes the rendered PNG through SAF.
- It no longer silently saves to app-local storage instead of showing the dialog.
- Object picking inside larger object bounds
- core/src/main/kotlin/com/github/alfu32/sketch/tools/SelectTool.kt
- Removed the coarse AABB fallback from click-picking.
- Object click-pick now requires an actual face or line hit, so an outer object no longer wins just because the ray passes through its bounds.
DXF import/export is fixed at the core path.
-
core/src/main/kotlin/com/github/alfu32/sketch/export/MeshIo.kt
- DXF import now parses:
- LINE
- LWPOLYLINE
- POLYLINE / VERTEX / SEQEND
- 3DFACE
- SOLID
- TRACE
- DXF export now writes both:
- LINE
- 3DFACE
- added DXF-specific geometry APIs so lines are no longer lost on import/export- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- added MeshSegment
- added createImportedPrototype(...) so imported objects can contain both faces and explicit linework
- existing createMeshPrototype(...) now delegates to it
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- DXF import now places both imported faces and imported lines into the created prototype
- DXF export now exports both world triangles and world segments
- selected export works for line segments too
- export label changed from DXF 3DFACE (.dxf) to DXF LINE + 3DFACE (.dxf)
- export status text is now generic enough for non-triangle DXF entities
Checked:
- GRADLE_USER_HOME=.gradle-user ./gradlew :core:compileKotlin -q
Current behavior:
- DXF import still follows the existing mesh-import flow:
- create prototype
- click to place object
- DXF export emits explicit lines plus triangle faces
- if a DXF has faces but no explicit lines, triangle edges are generated only when no DXF line entities were imported
Remaining limitation:
- exporter writes LINE + 3DFACE only; it does not try to recreate original DXF higher-level entities like LWPOLYLINE on export.
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
Global Illumination/Raytracing
changelog from 4.8.9 to 4.9.1:
- Improved the new offline renderer preview and output stability.
- Fixed saved render images being vertically flipped.
- Fixed render framing/camera mapping so the offline render matches the current view much more closely.
- Fixed directional/sun light contribution in offline rendering.
- Fixed path-tracing striping/banding artefacts.
- Fixed stale previous-pass/block artefacts in progressive rendering.
- Fixed render-window preview sizing so the preview no longer grows to the full internal buffer size.
- Added render size presets while keeping the preview window at a manageable fixed display size.
- Added a global render light level control for both raytrace and GI/path-trace.
- Added camera light intensity control.
- Added an option to disable tile pruning, with pruning now off by default.
- Improved transparent-material rendering so transmitted rays:
- pass through transparent surfaces,
- are tinted by material color,
- and are dimmed according to alpha.
- Added a new Rendering toolbar and render window.
- Added two offline rendering modes:
- Raytrace
- GI / Pathtrace
- Added progressive rendering passes from coarse to fine, ordered from image center toward the borders.
- Added Stop and Save controls for offline rendering.
- Added live compositing of the offline render over the captured GPU framebuffer.
- Added blur and blend controls for the render overlay.
- Added simple glass transmission/refraction support.
- Added desktop/Android parallel tile rendering with worker pool support.
- Kept web rendering on the single-threaded fallback path.
- Added worker-count control in the render window.
- Added resolution display in the render window.
Shorter store/update-note version:

- Introduced a new offline rendering window with Raytrace and GI/Pathtrace modes.
- Added progressive center-out rendering, save/stop controls, worker-count control, size presets, blur/blend controls, and adjustable lighting.
- Improved transparency handling for glass-like materials.
- Fixed multiple offline renderer issues including framing, lighting, vertical save flip, striping, and progressive pass artefacts.

UX/jar plugins/dimension bounds
4.8.9
In Short:
- Hotspot dragging now previews the resulting geometry in wireframe and shows live movement measurements.
- Array tool previews now show far more copies.
- Desktop now supports precompiled jar plugins.
- Plugin loading is disabled on unsupported Web/Android runtimes.
- Dimension selection bounds are now tight and correct.
TLDR
- Added live hotspot drag feedback:
- shows the hotspot movement line
- shows measurement feedback while dragging
- shows a wireframe preview of the future geometry driven by the hotspot
- Increased array/multiplier preview capacity:
- ghost/wireframe preview limit raised from 32 to 10000
- Added desktop support for precompiled plugin jars:
- plugin manager can now load .jar plugins in addition to Groovy script plugins
- jar plugins can expose their entry point through:
- META-INF/services/com.github.alfu32.sketch.plugin.Plugin
- or manifest Plugin-Class / K3D-Plugin-Class / Octodraw-Plugin-Class
- Restricted plugins to supported runtimes:
- plugin loading is now disabled on Web and Android
- avoids unsupported runtime/classloading paths on those targets
- Fixed dimension bounding boxes:
- selection/frame bounds now use the actual rendered dimension geometry
- extension lines, dimension line, and slash markers are all accounted for
- bounds are now tight instead of oversized/wrong
staged loading/fixes/maintenance
- Added staged model loading with visible progress instead of a long blind startup stall.
- File loading now initializes the app scene first, then loads the model in steps.
- Added a modal load dialog with progress feedback and an Abort button.
- Scene application during load is now chunked across frames, so the UI stays responsive while loading.
- Opening a file now creates a .bak copy as part of the staged load flow.
- Aborting a load no longer overwrites the target file or the previously opened file.
- Camera is reset to a valid default position immediately when a load starts.
- Added Edit Selected Object action for entering object edit mode more directly.
- Fixed object click-picking regression where objects were not selected correctly by direct click.
- Fixed orbit camera target auto-glue on faces, including root faces and object faces.
- Fixed hotspot-driven object geometry refresh so stale face shells are no longer left visible after deformation.
4.8.7
- Added a numeric Circle segments setting to Model Settings.
- Circle preview and generated geometry now use the configured segment count.
- Default circle segment count is now 24.
- Added internal optimizations for model delete/change handling.
- Added asynchronous/background spatial indexing work for model/query structures.
- Reduced excessive render/status feedback caused by indexing progress spam.
- Fixed linear/multiple translate array multiplication spacing faults.
- Disabled Snap bundle generation from the default desktop bundle pipeline to stop unrelated desktop builds from failing on Snap packaging.
4.8.6 -> 4.8.8 overall
- Major work on model loading, scene application, and persistence safety.
- Better responsiveness and feedback when opening large files.
- Safer cancel behavior during load.
- Better object editing workflow.
- Fixes for object picking, camera face snapping, and hotspot-driven geometry updates.

array tools
feat(tools): arrays and array hotspots

implemented the array tools and fixed the existing translate-multiple behavior.
What changed
- core/src/main/kotlin/com/github/alfu32/sketch/tools/MultipleCopyTools.kt
- fixed CopyMultipleTool
- step direction now comes from a - o
- copy count now comes from the span of b - o projected onto that step direction
- the old tool was using the final span direction and projecting the measure onto it, which was the wrong geometry
- added PlanarTranslateMultipleTool
- repeats in the xoz plane
- offsets are built from the measured step components on x and z
- added VolumetricTranslateMultipleTool
- repeats in x, y, and z
- offsets are built from the measured step components on all three axes
- added proper preview rendering for planar/volumetric translated arrays
- fixed CopyMultipleTool
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- fixed hotspot multiplier behavior to match the same semantics
- for hotspots:
- o = referencePosition
- a = hotspot definition position
- b = runtime target / instance value
- MULTIPLY_LINEAR now uses the measured step vector from o -> a
- MULTIPLY_VOLUMETRIC now uses per-axis step ratios from o -> a and o -> b
- I did not invent a new planar hotspot operation; planar hotspot behavior is the ny = 0 case of volumetric multiplication
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolId.kt
- added:
- PLANAR_TRANSLATE_MULTIPLE
- VOLUMETRIC_TRANSLATE_MULTIPLE
- added:
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- registered both new tools
- added them to the built-in tool command palette list
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- added both tools to the modification toolbar
- added icon-name/color mapping fallbacks
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolInputProcessor.kt
- added both tools to the M-cycle with the other transform tools
feat(tools): arrays and array hotspots
implemented the array tools and fixed the existing translate-multiple behavior.
What changed
- core/src/main/kotlin/com/github/alfu32/sketch/tools/MultipleCopyTools.kt
- fixed CopyMultipleTool
- step direction now comes from a - o
- copy count now comes from the span of b - o projected onto that step direction
- the old tool was using the final span direction and projecting the measure onto it, which was the wrong geometry
- added PlanarTranslateMultipleTool
- repeats in the xoz plane
- offsets are built from the measured step components on x and z
- added VolumetricTranslateMultipleTool
- repeats in x, y, and z
- offsets are built from the measured step components on all three axes
- added proper preview rendering for planar/volumetric translated arrays
- fixed CopyMultipleTool
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- fixed hotspot multiplier behavior to match the same semantics
- for hotspots:
- o = referencePosition
- a = hotspot definition position
- b = runtime target / instance value
- MULTIPLY_LINEAR now uses the measured step vector from o -> a
- MULTIPLY_VOLUMETRIC now uses per-axis step ratios from o -> a and o -> b
- I did not invent a new planar hotspot operation; planar hotspot behavior is the ny = 0 case of volumetric multiplication
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolId.kt
- added:
- PLANAR_TRANSLATE_MULTIPLE
- VOLUMETRIC_TRANSLATE_MULTIPLE
- added:
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- registered both new tools
- added them to the built-in tool command palette list
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- added both tools to the modification toolbar
- added icon-name/color mapping fallbacks
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolInputProcessor.kt
- added both tools to the M-cycle with the other transform tools
fix(tools): arrays and array hotspots
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:5987
- PLANAR_TRANSLATE_MULTIPLE now maps to multiple-copy-translate-planar
- VOLUMETRIC_TRANSLATE_MULTIPLE now maps to multiple-copy-translate-volumetric
fix(snapcraft): release
-
Moved the Snapcraft project file to the repo root:
- snapcraft.yaml- Removed the old nested config:
- snap/snapcraft.yaml
- Kept the GUI assets under:
- snap/gui/octodraw.desktop
- snap/gui/icon.png
Gradle change
- lwjgl3/build.gradle
- bundleLinuxSnap now copies:
- root snapcraft.yaml into the Snapcraft project root
- snap/ into the staged snap/ directory
- local Snap build still uses Snapcraft, not mksquashfs
- bundleLinuxSnap now copies:
CI change
- .github/workflows/release.yml
- Linux CI no longer installs Snapcraft
- Linux CI no longer runs :lwjgl3:bundleLinuxSnap
- Linux CI no longer uploads *.snap
- Removed the old nested config:
extra array tools
feat(tools): arrays and array hotspots
implemented the array tools and fixed the existing translate-multiple behavior.
What changed
- core/src/main/kotlin/com/github/alfu32/sketch/tools/MultipleCopyTools.kt
- fixed CopyMultipleTool
- step direction now comes from a - o
- copy count now comes from the span of b - o projected onto that step direction
- the old tool was using the final span direction and projecting the measure onto it, which was the wrong geometry
- added PlanarTranslateMultipleTool
- repeats in the xoz plane
- offsets are built from the measured step components on x and z
- added VolumetricTranslateMultipleTool
- repeats in x, y, and z
- offsets are built from the measured step components on all three axes
- added proper preview rendering for planar/volumetric translated arrays
- fixed CopyMultipleTool
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- fixed hotspot multiplier behavior to match the same semantics
- for hotspots:
- o = referencePosition
- a = hotspot definition position
- b = runtime target / instance value
- MULTIPLY_LINEAR now uses the measured step vector from o -> a
- MULTIPLY_VOLUMETRIC now uses per-axis step ratios from o -> a and o -> b
- I did not invent a new planar hotspot operation; planar hotspot behavior is the ny = 0 case of volumetric multiplication
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolId.kt
- added:
- PLANAR_TRANSLATE_MULTIPLE
- VOLUMETRIC_TRANSLATE_MULTIPLE
- added:
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- registered both new tools
- added them to the built-in tool command palette list
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- added both tools to the modification toolbar
- added icon-name/color mapping fallbacks
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolInputProcessor.kt
- added both tools to the M-cycle with the other transform tools
feat(tools): arrays and array hotspots
implemented the array tools and fixed the existing translate-multiple behavior.
What changed
- core/src/main/kotlin/com/github/alfu32/sketch/tools/MultipleCopyTools.kt
- fixed CopyMultipleTool
- step direction now comes from a - o
- copy count now comes from the span of b - o projected onto that step direction
- the old tool was using the final span direction and projecting the measure onto it, which was the wrong geometry
- added PlanarTranslateMultipleTool
- repeats in the xoz plane
- offsets are built from the measured step components on x and z
- added VolumetricTranslateMultipleTool
- repeats in x, y, and z
- offsets are built from the measured step components on all three axes
- added proper preview rendering for planar/volumetric translated arrays
- fixed CopyMultipleTool
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- fixed hotspot multiplier behavior to match the same semantics
- for hotspots:
- o = referencePosition
- a = hotspot definition position
- b = runtime target / instance value
- MULTIPLY_LINEAR now uses the measured step vector from o -> a
- MULTIPLY_VOLUMETRIC now uses per-axis step ratios from o -> a and o -> b
- I did not invent a new planar hotspot operation; planar hotspot behavior is the ny = 0 case of volumetric multiplication
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolId.kt
- added:
- PLANAR_TRANSLATE_MULTIPLE
- VOLUMETRIC_TRANSLATE_MULTIPLE
- added:
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- registered both new tools
- added them to the built-in tool command palette list
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- added both tools to the modification toolbar
- added icon-name/color mapping fallbacks
- core/src/main/kotlin/com/github/alfu32/sketch/ui/ToolInputProcessor.kt
- added both tools to the M-cycle with the other transform tools
fix(tools): arrays and array hotspots
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:5987
- PLANAR_TRANSLATE_MULTIPLE now maps to multiple-copy-translate-planar
- VOLUMETRIC_TRANSLATE_MULTIPLE now maps to multiple-copy-translate-volumetric
fix(snapcraft): release
-
Moved the Snapcraft project file to the repo root:
- snapcraft.yaml- Removed the old nested config:
- snap/snapcraft.yaml
- Kept the GUI assets under:
- snap/gui/octodraw.desktop
- snap/gui/icon.png
Gradle change
- lwjgl3/build.gradle
- bundleLinuxSnap now copies:
- root snapcraft.yaml into the Snapcraft project root
- snap/ into the staged snap/ directory
- local Snap build still uses Snapcraft, not mksquashfs
- bundleLinuxSnap now copies:
CI change
- .github/workflows/release.yml
- Linux CI no longer installs Snapcraft
- Linux CI no longer runs :lwjgl3:bundleLinuxSnap
- Linux CI no longer uploads *.snap
- Removed the old nested config:
performance improvements
- 34f88e2 fix(model): indexing
- b269c60 feat(model): indexing
- 88d7c5b fix(snap): platform detection failure
- 9e73f83 improve(model): pick handling, line-to-line intersect
- c0db7bd fix(snap-build): snap package faults
- 62623f4 fix(snap-build): snap package faults
- 86b3461 fix(snap-build): snap package faults
fix(model): indexing
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding - core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding
fix(snap): permissions
feat(model): indexing
-
core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding
relevant fixes are:
-
no per-frame frustum/index render path
-
no mutation-path index rebuild while drawing lines
-
no indexed prefilter on group volume selection
-
group face bundles now invalidate on transform changes
-
deleted group bundles are synced out every frame
fix(snap): platform detection failure
patched lwjgl3/build.gradle.
What changed
- Snap app command now goes through a wrapper:
- command: bin/octodraw-launch
- The build now generates bin/octodraw-launch inside the snap
- The wrapper sets pragmatic desktop env fallbacks before execing the real launcher:
- XDG_RUNTIME_DIR=/run/user/$(id -u) if missing
- WAYLAND_DISPLAY=wayland-0 if that socket exists
- DISPLAY=:0 if /tmp/.X11-unix/X0 exists
- then exec "$SNAP/octodraw-jre" "$@"
improve(model): pick handling, line-to-line intersect
plan:
- Replace the current category-based pick arbitration with a unified nearest-geometry pick path.
- Test active-group faces/edges and child object instance faces/edges in the same hit pass.
- Choose the nearest geometric hit in world space.
- Only then resolve what it belongs to: object, wall, slab, frame, etc.
- This avoids both failure modes:
- walls/slabs behind objects winning
- objects behind walls/slabs winning
- Wire the line spatial index into segment insertion and auto-split.
- Compute the new segment AABB.
- Query indexed candidate segments from that AABB.
- Run exact intersectSegments(...) only on those candidates.
- Do the same for endpoint snapping using a small point-radius AABB.
- Add coarse render culling at scene/group level.
- Frustum-test world AABBs of groups/objects before drawing.
- Use this as the first render optimization pass before any chunked cubic culling.
Implemented the three changes.
- Geometry-first object picking:
- core/src/main/kotlin/com/github/alfu32/sketch/tools/SelectTool.kt
- basic pick resolution now uses a unified nearest-hit pool for voxels/faces/edges/dimensions/texts/child objects instead of category arbitration
- architecture/HVAC root picks are now gated so a nearer child object instance can win over walls/slabs behind it
- I did not switch category priority; the decision is still based on geometric hit distance
- Indexed segment insertion / auto-split:
- core/src/main/kotlin/com/github/alfu32/sketch/model/DraftLineStore.kt
- endpoint snapping now queries a small indexed AABB around the point
- segment insertion now queries indexed candidate segments from the new segment AABB before running exact intersectSegments(...)
- that removes the full-scan path from the normal insertion/split workflow
- Coarse render culling:
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- added queryGroupsByFrustum(...) using the group spatial index plus frustum/AABB filtering
- child object/group faces are now rendered through per-group mesh bundles and only submitted when in view
- child group draft lines are also culled by the same visible-group set
Important scope note:
- this is scene/group-level culling, not cubic chunk culling
- root geometry is still rendered as one root mesh; the culling now applies to child object/group geometry first, which is what we agreed to do before any finer chunking
fix(snap-build): snap package faults
- added install badges
fix(snap-build): snap package faults
- added install badges
fix(snap-build): snap package faults
- added install badges
4.8.2: fix(snap-store): snap package pipelines
- 34f88e2 fix(model): indexing
- b269c60 feat(model): indexing
- 88d7c5b fix(snap): platform detection failure
- 9e73f83 improve(model): pick handling, line-to-line intersect
- c0db7bd fix(snap-build): snap package faults
- 62623f4 fix(snap-build): snap package faults
- 86b3461 fix(snap-build): snap package faults
fix(model): indexing
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding - core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding
fix(snap): permissions
feat(model): indexing
-
core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- computeGroupFaceMeshVisualStamp(...) already now includes the group world transform, so moved instances invalidate correctly
- I also added syncGroupFaceBundles() on every render pass, not only during root face-mesh rebuild
- that forces dead/deleted child-group mesh bundles to be dropped even when the root face mesh itself did not need rebuilding
relevant fixes are:
-
no per-frame frustum/index render path
-
no mutation-path index rebuild while drawing lines
-
no indexed prefilter on group volume selection
-
group face bundles now invalidate on transform changes
-
deleted group bundles are synced out every frame
fix(snap): platform detection failure
patched lwjgl3/build.gradle.
What changed
- Snap app command now goes through a wrapper:
- command: bin/octodraw-launch
- The build now generates bin/octodraw-launch inside the snap
- The wrapper sets pragmatic desktop env fallbacks before execing the real launcher:
- XDG_RUNTIME_DIR=/run/user/$(id -u) if missing
- WAYLAND_DISPLAY=wayland-0 if that socket exists
- DISPLAY=:0 if /tmp/.X11-unix/X0 exists
- then exec "$SNAP/octodraw-jre" "$@"
improve(model): pick handling, line-to-line intersect
plan:
- Replace the current category-based pick arbitration with a unified nearest-geometry pick path.
- Test active-group faces/edges and child object instance faces/edges in the same hit pass.
- Choose the nearest geometric hit in world space.
- Only then resolve what it belongs to: object, wall, slab, frame, etc.
- This avoids both failure modes:
- walls/slabs behind objects winning
- objects behind walls/slabs winning
- Wire the line spatial index into segment insertion and auto-split.
- Compute the new segment AABB.
- Query indexed candidate segments from that AABB.
- Run exact intersectSegments(...) only on those candidates.
- Do the same for endpoint snapping using a small point-radius AABB.
- Add coarse render culling at scene/group level.
- Frustum-test world AABBs of groups/objects before drawing.
- Use this as the first render optimization pass before any chunked cubic culling.
Implemented the three changes.
- Geometry-first object picking:
- core/src/main/kotlin/com/github/alfu32/sketch/tools/SelectTool.kt
- basic pick resolution now uses a unified nearest-hit pool for voxels/faces/edges/dimensions/texts/child objects instead of category arbitration
- architecture/HVAC root picks are now gated so a nearer child object instance can win over walls/slabs behind it
- I did not switch category priority; the decision is still based on geometric hit distance
- Indexed segment insertion / auto-split:
- core/src/main/kotlin/com/github/alfu32/sketch/model/DraftLineStore.kt
- endpoint snapping now queries a small indexed AABB around the point
- segment insertion now queries indexed candidate segments from the new segment AABB before running exact intersectSegments(...)
- that removes the full-scan path from the normal insertion/split workflow
- Coarse render culling:
- core/src/main/kotlin/com/github/alfu32/sketch/model/GroupScene.kt
- core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- added queryGroupsByFrustum(...) using the group spatial index plus frustum/AABB filtering
- child object/group faces are now rendered through per-group mesh bundles and only submitted when in view
- child group draft lines are also culled by the same visible-group set
Important scope note:
- this is scene/group-level culling, not cubic chunk culling
- root geometry is still rendered as one root mesh; the culling now applies to child object/group geometry first, which is what we agreed to do before any finer chunking
fix(snap-build): snap package faults
- added install badges
fix(snap-build): snap package faults
- added install badges
fix(snap-build): snap package faults
- added install badges
4.8.1: feat(ui): topdown and leftright layout,png export
fix(android): ctrl key
Android Ctrl handling is now stateful instead of relying only on transient meta flags:
- core/src/main/kotlin/com/github/alfu32/sketch/InputModifiers.kt:11
- android/src/main/kotlin/com/github/alfu32/sketch/android/AndroidLauncher.kt:30
- this keeps Ctrl active while dragging for move-copy / rotate-copy, and clears it on focus loss/destroy
fix(ui-toolbars): auto resizing issue
- Non-resizable toolbars now ignore stale saved width/height and repack from content:
- core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:5029
- so changing toolbar button size now resizes the toolbar windows correctly
feat(ui): resizable text
- Added UI text scaling presets:
- 1x, 1.5x, 2x
- wired in UI Settings
- persisted in prefs
- applied to VisUI skin fonts and relaid out live
fix(ui): resize effect propagation failure
patched the UI scale refresh path in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt.
- rebuildToolbarsForUiScaleChange() now finishes with a full UI refresh instead of only marking layout dirty, so icon-size changes reflow immediately too: core/src/main/kotlin/com/
github/alfu32/sketch/ui/SketchUiOverlay.kt:4523 - Added recursive layout invalidation/validation for the full actor tree, not just top-level widgets: core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4550
- Rebuilds the right dock scroll container on scale changes, resets the cached dock width, and immediately repositions/revalidates panels and toolbars: core/src/main/kotlin/com/
github/alfu32/sketch/ui/SketchUiOverlay.kt:4568 - refreshUiForTextScaleChange() now forces an immediate full relayout/repack of dock panels, input widgets, toolbars, and floating UI instead of waiting for a later collapse/expand or
frame pass: core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4592
fix(ui): resize effect propagation failure
patched the UI scale refresh path in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt.
- rebuildToolbarsForUiScaleChange() now finishes with a full UI refresh instead of only marking layout dirty, so icon-size changes reflow immediately too: core/src/main/kotlin/com/
github/alfu32/sketch/ui/SketchUiOverlay.kt:4523 - Added recursive layout invalidation/validation for the full actor tree, not just top-level widgets: core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4550
- Rebuilds the right dock scroll container on scale changes, resets the cached dock width, and immediately repositions/revalidates panels and toolbars: core/src/main/kotlin/com/
github/alfu32/sketch/ui/SketchUiOverlay.kt:4568 - refreshUiForTextScaleChange() now forces an immediate full relayout/repack of dock panels, input widgets, toolbars, and floating UI instead of waiting for a later collapse/expand or
frame pass: core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4592
fix(ui): resize effect toolbars failure
The remaining bug was exactly where the toolbar windows are positioned.
- positionTopFlowToolbars() was still reapplying saved width/height even for non-resizable toolbars, so after icon-size changes the rebuilt toolbar content grew but the host window
got forced back to the old size and clipped the buttons. - I fixed that in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4997 so non-resizable toolbars always use current prefWidth/prefHeight, same as
applyToolbarState() already did.
fix(ui): resize effect toolbars failure
The remaining bug was exactly where the toolbar windows are positioned.
- positionTopFlowToolbars() was still reapplying saved width/height even for non-resizable toolbars, so after icon-size changes the rebuilt toolbar content grew but the host window
got forced back to the old size and clipped the buttons. - I fixed that in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt:4997 so non-resizable toolbars always use current prefWidth/prefHeight, same as
applyToolbarState() already did.
fix(ui): toolbar relayout bug
Fixed in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt.
- Hover expand/collapse no longer marks all toolbars for global re-layout, so moving the mouse does not snap toolbars back to their saved positions.
- Toggling Auto-collapse toolbars no longer calls positionPanels(), so manual toolbar placement is preserved.
- Collapsed toolbars now force their width from the visible button/content width instead of the title width, so plugin toolbars collapse to button-sized hosts too.
fix(ui): toolbar persistence bug
• The problem was in the persistence trigger.
- Toolbar state was only being saved from the window touchUp listener.
- With auto-collapse enabled, that is not reliable enough:
- collapse/expand changes happen without a drag save
- collapsed toolbars can end up with state changes that never hit that listener
- Result: the runtime layout looked correct, but the stored x/y stayed stale, so after restart you got old positions.
patched core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt.
feat(ui): linewidth and UI layout helpers
- UI Settings now has a Normal line width slider in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- wired to core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- it drives the non-thick 2D ShapeRenderer.ShapeType.Line overlay batches: selection rect, tutorial arrow, hotspot outlines
- this is best-effort on Android/Desktop because glLineWidth support is driver-dependent
- Toolbar padding and layout spacing were reduced in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- button cell padding: 0
- toolbar content padding: 0
- toolbar layout margin/gap reduced to 4f
- Added two toolbar arrangement buttons in UI Settings:
- Flow L->R
- lays toolbars left-to-right, wraps top-to-down, and stops before the right dock
- Flow T->D
- lays toolbars top-to-down, wraps left-to-right from the left edge
- both save the resulting positions immediately
- Flow L->R
- Existing toolbar persistence stays active
- toolbar positions are continuously persisted in act(...), so explicit arrangement and manual repositioning both survive restart
fix(ui): toolbars collapsed width glitch
- The collapsed toolbar width logic was already shrinking the content area.
- But the VisWindow title label still kept its full text, so the window pref width stayed title-driven on initial render and in auto-collapse mode.
I fixed it in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt.
- CollapsibleWindow now stores its base title and can switch to a compact-title mode
- when a toolbar is auto-collapsed:
- the title text is set to ""
- when expanded:
- the full title is restored
That makes the collapsed window width depend on the visible button, not on the title text.
fix(ui): toolbars collapsed width glitch
- Collapsed toolbars no longer use an empty title.
- They now use the first 4 characters of the original title:
- baseTitle.take(4)
feat(ui): topdown and leftright layout,png export
improve the layout l-r procedure:
- the toolbars should be sorted in such a way that they fill a maximum of space on each line
and layout t-d procedure: - the toolbars should be sorted by width : the largest on top to the smallest on the bottom
the png exporter will export a screenshot of the rendering buffer without the UI
- Added PNG Screenshot (*.png) to the exporter list in core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- desktop, web, and Android export flows now accept PNG
- PNG export captures the rendered scene before UI/2D overlay drawing
- desktop save, web download, and Android SAF write are all wired through the same pending export path- Fixed the desktop export dialog integration in core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
- it now maps the selected desktop dialog option back to the real export option
- dialog wording is generic now, not mesh-only
- direct file export also handles .png correctly
- Improved toolbar L->R layout in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- uses first-fit decreasing row packing
- fills each row more tightly instead of naive sequential wrap
- the default top-flow placement for unsaved toolbars uses the same strategy now
- Kept T->D width-driven in core/src/main/kotlin/com/github/alfu32/sketch/ui/SketchUiOverlay.kt
- visible toolbars are sorted by width descending
- largest go first/top, then wrap to the next column
- Fixed the desktop export dialog integration in core/src/main/kotlin/com/github/alfu32/sketch/Main.kt
feat(snap-store): snap package pipelines
What changed
- lwjgl3/build.gradle
- added bundleLinuxSnap
- it builds from dist/octodraw-dist--linux
- generates:
- meta/snap.yaml
- meta/gui/octodraw.desktop
- meta/gui/icon.png
- packs the snap with mksquashfs to:
- dist/octodraw-dist--linux.snap
- Linux local pipelines now include it:
- :lwjgl3:bundleLocal depends on bundleLinuxSnap on Linux
- :lwjgl3:dev depends on bundleLinuxSnap on Linux
- .github/workflows/release.yml
- Linux CI now installs squashfs-tools
- Linux build job now runs:
- :lwjgl3:bundleLinuxTar
- :lwjgl3:bundleLinuxSnap
- :lwjgl3:jarLinux
- :core:pluginApiJar
- uploaded Linux artifacts now include:
- dist/*.snap
- release upload now includes *.snap
fix(snap-store): snap package pipelines
Fixed.
Cause:
...
UI improvements
changelog short
- ui settings improvements:
- settings for thin and thick line width
- font size selection (1x,1.5x,2x)
- ui improvements:
- toolbars autocollapse into a single button-width toolbar, autoexpands when hovered, optional through ui settings
- autoalign toolbars left-to right/top-down or top-to-down/left-to-right
- set selection color directly in the selection panel
- fix android control key
changelog detailed
fix(android): ctrl key
Android Ctrl handling is now stateful instead of relying only on transient meta flags
- this keeps Ctrl active while dragging for move-copy / rotate-copy, and clears it on focus loss/destroy
fix(ui-toolbars): auto resizing issue
- Non-resizable toolbars now ignore stale saved width/height and repack from content
- changing toolbar button size now resizes the toolbar windows correctly
- Added UI text scaling presets:
- 1x, 1.5x, 2x
- wired in UI Settings
- persisted in prefs
- applied to VisUI skin fonts and relaid out live
fix(ui): resize effect propagation failure
- rebuildToolbarsForUiScaleChange() now finishes with a full UI refresh instead of only marking layout dirty, so icon-size changes reflow immediately too
- Added recursive layout invalidation/validation for the full actor tree, not just top-level widgets
- Rebuilds the right dock scroll container on scale changes, resets the cached dock width, and immediately repositions/revalidates panels and toolbars
- refreshUiForTextScaleChange() now forces an immediate full relayout/repack of dock panels, input widgets, toolbars, and floating UI instead of waiting for a later collapse/expand or frame pass
fix(ui): resize effect toolbars failure
The remaining bug was exactly where the toolbar windows are positioned.
- positionTopFlowToolbars() was still reapplying saved width/height even for non-resizable toolbars, so after icon-size changes the rebuilt toolbar content grew but the host window
got forced back to the old size and clipped the buttons. - I fixed that so non-resizable toolbars always use current prefWidth/prefHeight, same as
applyToolbarState() already did.
fix(ui): toolbar relayout bug
- Hover expand/collapse no longer marks all toolbars for global re-layout, so moving the mouse does not snap toolbars back to their saved positions.
- Toggling Auto-collapse toolbars no longer calls positionPanels(), so manual toolbar placement is preserved.
- Collapsed toolbars now force their width from the visible button/content width instead of the title width, so plugin toolbars collapse to button-sized hosts too.
fix(ui): toolbar persistence bug
• The problem was in the persistence trigger.
- Toolbar state was only being saved from the window touchUp listener.
- With auto-collapse enabled, that is not reliable enough:
- collapse/expand changes happen without a drag save
- collapsed toolbars can end up with state changes that never hit that listener
- Result: the runtime layout looked correct, but the stored x/y stayed stale, so after restart you got old positions.
feat(ui): linewidth and UI layout helpers
- UI Settings now has a Normal line width slider
- drives the non-thick 2D ShapeRenderer.ShapeType.Line overlay batches: selection rect, tutorial arrow, hotspot outlines
- this is best-effort on Android/Desktop because glLineWidth support is driver-dependent
- Toolbar padding and layout spacing were reduced
- button cell padding: 0
- toolbar content padding: 0
- toolbar layout margin/gap reduced to 4f
- Added two toolbar arrangement buttons in UI Settings:
- Flow L->R
- lays toolbars left-to-right, wraps top-to-down, and stops before the right dock
- Flow T->D
- lays toolbars top-to-down, wraps left-to-right from the left edge
- both save the resulting positions immediately
- Flow L->R
- Existing toolbar persistence stays active
- toolbar positions are continuously persisted in act(...), so explicit arrangement and manual repositioning both survive restart
fix(ui): toolbars collapsed width glitch
-
The collapsed toolbar width logic was already shrinking the content area.
-
But the VisWindow title label still kept its full text, so the window pref width stayed title-driven on initial render and in auto-collapse mode.
-
CollapsibleWindow now stores its base title and can switch to a compact-title mode
-
when a toolbar is auto-collapsed:
- the title text is set to ""
-
when expanded:
- the full title is restored
That makes the collapsed window width depend on the visible button, not on the title text.



