Skip to content

Add color engine, Mandelbulb 3D explorer, render resolution, and cursor-lock zoom - #1

Draft
Jasonilization wants to merge 8 commits into
mainfrom
worktree-deep-zoom-pro
Draft

Add color engine, Mandelbulb 3D explorer, render resolution, and cursor-lock zoom#1
Jasonilization wants to merge 8 commits into
mainfrom
worktree-deep-zoom-pro

Conversation

@Jasonilization

Copy link
Copy Markdown
Owner

Summary

  • Render Resolution picker (0.5x-2x internal scale) for the live canvas, plus a selectable Save Image export resolution.
  • Lock Cursor to Detail toggle: manual scroll/pinch zoom snaps its pivot onto nearby detailed structure instead of requiring pixel-perfect aim.
  • Color engine: escape-time (smooth/banded)/distance-estimation/orbit-trap (circle/line/cross/custom) color modes, a relief-shading pass, and a full Palette Editor (add/remove/reposition stops, HSB + color picker, save custom palettes) -- all implemented consistently across the float32, double-double, and CPU perturbation tiers, plus new Classic Rainbow/Ice/Neon/Monochrome/Scientific presets.
  • Mandelbulb Explorer: a new tab alongside the 2D explorer with a real-time ray-marched 3D Mandelbulb -- orbit camera, quality tiers, lighting/AO/soft shadows, power/variant presets, and screenshot export.
  • GitHub profile link + Help sheet (already present from a prior commit) verified intact through the new tab-bar restructuring.

Test plan

  • swift build clean, no errors
  • Full existing preset + zoom-depth-ladder regression (PRESET_PREVIEW_DIR) -- all 24 renders across all 3 precision tiers succeed, no regressions
  • New COLOR_MODE_TEST_DIR headless diagnostic -- every color mode/orbit trap shape/shading combination verified visually correct on both GPU and perturbation tiers (caught and fixed 4 real bugs along the way: gradient-cycle-speed regression, distance-estimation's absolute-epsilon clamp breaking at deep zoom, and orbit trap's series-approximation-skip derivative/baseline seeding)
  • New MANDELBULB_TEST_DIR headless diagnostic -- power/variant/quality combinations render correctly
  • Verified end-to-end in the actual windowed app: launched the built binary, used the accessibility API to switch between the Mandelbrot and Mandelbulb tabs and back, confirmed both render correctly and state is preserved
  • Built and launched the packaged, ad-hoc-signed .app release bundle successfully

🤖 Generated with Claude Code

Jasonilization and others added 6 commits July 10, 2026 17:29
…ion tier

Series approximation lets every pixel skip the leading iterations that are
still identical (within tolerance) across the whole frame instead of looping
from n=0, roughly halving to quartering CPU time at extreme zoom in testing
(cross-validated against the non-SA reference orbit; smooth mu values match
to 5-6 significant figures). The first pass now also tiles the image and
streams completed tiles back to the iteration texture as they land, so a
slow deep-zoom render visibly fills in instead of freezing on the old frame
until the whole buffer is ready.

Also folds in previously-uncommitted local work (async shader pipeline
compilation with a loading overlay, idle-frame GPU savings, a basic
auto-zoom toggle) that was sitting in the working tree, and fixes a
pre-existing bug in the headless preset-preview diagnostic tool where it
didn't wait for shader compilation before capturing, spuriously failing
every GPU-tier preset on a fresh build.
Auto-zoom now steers toward visually rich boundary structure instead of
diving straight into whatever's centered: it periodically reads back the
just-rendered frame, scores a coarse grid by interior/exterior boundary
presence and iteration-count variation (AutoZoomScoring), and eases the
zoom pivot toward the best cell. When nothing on screen clears the boredom
threshold (a flat interior lake, a featureless exterior) it backs off the
zoom and sweeps the target around nearby instead of plunging into it.

FractalRecorder drives that same steered path offline at a fixed
per-frame timestep (independent of how long each frame actually takes to
render) and encodes straight to H.264 .mov via AVAssetWriter, with
adjustable resolution/fps/duration and an optional zoom-level overlay.
Recording takes over the live viewport for its duration (interaction and
the live auto-zoom toggle are locked) so the two never fight over the same
camera state; the on-screen view mirrors progress. Cross-validated with a
headless recording+thumbnail-extraction test (RECORDING_TEST_PATH) that
caught and fixed a real frame-orientation bug in the overlay compositing.
HelpView is a built-in, beginner-friendly-but-accurate tutorial (toolbar
Help button): what the Mandelbrot set is (z -> z^2 + c, inside vs. outside,
how color encodes escape speed), controls, why deep zoom needs perturbation
theory + series approximation, and what each of the three rendering modes
does. A GitHub toolbar button and an About tab link the profile
(github.com/Jasonilization) alongside "Created by Jason".

The sidebar now also surfaces the live precision budget (~digits from the
arbitrary-precision term count) and, in perturbation mode, how many
iterations series approximation skipped -- and the status bar gets a
color-coded GPU/GPU-DD/CPU rendering-mode chip so the active tier is
visible without opening the sidebar. Verified with a from-scratch debug
and release build plus the full headless preset/ladder/recording
diagnostic suite (all match prior runs byte-for-byte on the GPU tiers).
- Render Resolution picker (0.5x-2x internal scale, independent of window
  size) plus a selectable Save Image export resolution.
- "Lock Cursor to Detail" toggle: manual scroll/pinch zoom snaps its pivot
  onto nearby detailed structure instead of requiring pixel-perfect aim,
  reusing auto-zoom's boundary/gradient scoring locally.
- Color engine: positioned gradient stops (add/remove/reposition, HSB +
  ColorPicker, live preview) via a new Palette Editor sheet, with custom
  palettes persisted to disk; new Classic Rainbow/Ice/Neon/Monochrome/
  Scientific presets alongside the existing ones.
- Three color modes -- escape time (smooth or banded), distance estimation,
  and orbit trap (circle/line/cross/custom point) -- plus a cheap relief
  shading pass, all implemented consistently across the float32, double-
  double, and CPU perturbation tiers. Fixed several correctness bugs found
  via a new COLOR_MODE_TEST_DIR headless diagnostic along the way: a
  gradient-cycle-speed regression, distance estimation's absolute-epsilon
  clamp breaking at deep zoom, and orbit trap's series-approximation-skip
  derivative/baseline seeding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New "Mandelbulb Explorer" tab, alongside (not replacing) the 2D Mandelbrot
explorer, sharing a native macOS tab bar and toolbar:

- Standard power-N Mandelbulb distance estimator (spherical-coordinate
  triplex formula), ray-marched in a single Metal compute kernel with
  analytic normals, ambient occlusion, and soft self-shadows.
- Orbit camera: click-drag to rotate, scroll/pinch to zoom, smoothly eased.
- Quality tiers (Low/Medium/High/Ultra) controlling ray-march steps, AO/
  shadows, and resolution scale; renders at reduced quality while the
  camera moves and refines once it settles, mirroring the 2D explorer's
  progressive-rendering approach.
- Power/iteration/hollow-variant controls, adjustable lighting, reset
  camera, and screenshot/render export.
- Presets: Classic Mandelbulb, Seahorse-like (power 4), Hollow Forms (an
  abs-transform variant), Spiky Forms (power 12).
- A new Mandelbulb topic in the Help sheet, and a MANDELBULB_TEST_DIR
  headless diagnostic mirroring the existing preset-preview tooling.

Verified end-to-end in the actual windowed app (not just headless): both
tabs render correctly and switching between them preserves each one's
state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Julia Set Explorer is a new tab sharing the exact same rendering engine
as the Mandelbrot explorer (GPU tiers, CPU perturbation with series
approximation, progressive tiled rendering, color pipeline, export,
recording) -- FractalRenderer is now parameterized by FractalKind
(Mandelbrot vs. Julia) rather than duplicated. The reference-orbit and
series-approximation math generalizes to support the Julia case (c
fixed, z varies) alongside the Mandelbrot case (c varies, z starts at
0), preserving perturbation-tier deep zoom for Julia sets too.

Double-clicking a point in the Mandelbrot tab (or the sidebar's "Open
Julia Set at Center" button) opens that point's Julia set. The Julia
sidebar adds c-parameter controls (live-updating fields/sliders, random,
presets, favorites) while reusing the shared rendering/color/export
sections via a new FractalCommonSidebarSections.

Also fixes a black-frame flash: the perturbation tier's iteration
texture used to blank to a flat interior color whenever its render
size changed (at the start/end of every interaction), showing briefly
before progressive tiles filled back in. It's now seeded with a
bilinear resample of the previous frame instead, so the transition
reads as an instant, blurry continuation rather than a black flash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Jasonilization

Copy link
Copy Markdown
Owner Author

Added in this update:

  • Julia Set Explorer — a new tab sharing the exact same rendering engine as the Mandelbrot tab (GPU tiers, CPU perturbation with series approximation, progressive rendering, color pipeline, export, recording). Double-click any point in the Mandelbrot tab (or use the sidebar button) to open its Julia set. Julia sidebar adds live-updating c controls, Random Julia, presets, and persisted favorites.
  • Progressive rendering fix — the CPU perturbation tier used to briefly flash a flat interior-colored frame whenever its render resolution changed (start/end of every interaction). It now seeds the resized texture with a bilinear resample of the previous frame instead, so movement always shows a continuous (if briefly blurry) image rather than a black flash.

Verified via headless diagnostics (full Mandelbrot preset/zoom-depth ladder to 1e80 across all three tiers, new Julia-mode GPU/perturbation tests, and a direct arbitrary-precision cross-check of the Julia perturbation math) plus manual GUI testing of tab switching, the Julia sidebar controls, and the Mandelbrot→Julia double-click handoff.

While panning/zooming, maxIterations is temporarily cut to keep frame
rate smooth. A point that hasn't escaped by that cut budget might be
genuinely interior, or might just need more iterations than the current
frame allows -- those are indistinguishable without iterating further.
The renderer previously guessed "interior" and painted the flat interior
color, which read as incorrect black holes punched into boundary detail
during any interaction at real zoom depth (verified: cutting the budget
from 2000 to 250 iterations at a typical deep-zoom boundary view wrongly
painted 100% of the genuinely-exterior pixels needing >250 iterations as
solid interior).

Adds a previewMode flag (GPU kernels + CPU perturbation) that colors an
unresolved point the same as an escaping point at the cap instead of
guessing "interior," so it blends into the surrounding gradient until a
full-iteration pass resolves it for real. Also changes the animated
iteration cut from a fixed number to one proportional to the view's real
maxIterations (with a floor), so deep zooms keep meaningfully more live
detail during interaction instead of collapsing to a near-fixed floor
regardless of depth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Jasonilization

Copy link
Copy Markdown
Owner Author

Follow-up fix on top of the previous update: found and fixed a real correctness bug in the interactive-zoom quality reduction.

The bug: while panning/zooming, maxIterations is temporarily cut to keep frame rate smooth. A point that hasn't escaped by that cut budget might be genuinely interior, or might just need more iterations than the current frame allows — those two cases were indistinguishable, and the renderer guessed "interior," painting the flat interior color. At real zoom depth this reads as incorrect black holes punched into boundary detail during any interaction. Verified numerically: cutting the budget from 2000 to 250 iterations at a typical deep-zoom boundary view wrongly classified 100% of the genuinely-exterior pixels needing more than 250 iterations as solid interior.

The fix: a previewMode flag (threaded through both GPU kernels and the CPU perturbation path) that colors an unresolved point the same as an escaping point at the iteration cap instead of guessing "interior" — it blends into the surrounding gradient and resolves correctly once a full-iteration pass completes. Also changed the animated iteration cut from a fixed number (220/300) to one proportional to the view's actual maxIterations with a floor, so deep zooms keep meaningfully more live detail during interaction instead of collapsing to a near-fixed floor regardless of depth.

Added a PREVIEW_MODE_VERIFY headless regression check that renders the same view at full/cut-without-fix/cut-with-fix and reports the misclassification count — confirms 0 wrongly-classified pixels with the fix vs. 100% without. Also reran the full existing regression suite (preset/zoom-ladder to 1e80, color-mode tests) with no changes to full-quality output.

Replaces the zoomed-detail Fire-palette icon with the classic full-set
view (not zoomed into anything in particular) rendered with the color
dials turned down, composited into a dark glass squircle: a circular
panel with a soft drop shadow, a bright rim-light along its top edge,
and a specular highlight, approximating macOS Tahoe's Liquid Glass
material with plain CoreGraphics gradients since the app ships a flat
PNG rather than a layered Icon Composer asset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant