spacr.qt.widgets.fractal_travel¶
The spaceout fractal: a GPU shader when there is one, Numba otherwise.
Ported from fractal_travel.py v2.1.0. Two renderers, not one engine with a
switch:
GPU – VisPy/gloo with a GLSL fragment shader, four spatial samples per physical pixel, and a detail loop that adapts from sampled GPU time.
CPU – a cheaper orbit-fold fractal in Numba, evaluated at animation rate with a four-position temporal 2x2 jitter and a rolling four-frame window. No keyframes and no crossfades: every displayed frame is new.
THREE THINGS THIS FILE DOES THAT THE SCRIPT DID NOT.
vispy is not installed in the shipped environment, so backend='auto'
resolves to CPU today and to GPU the day it is. Nothing else changes.
It is PySide6. The script was PyQt6, and importing that binding inside this application would put two Qt bindings in one process, which does not raise – it segfaults.
And it WINDS DOWN. A backdrop eating cores while a segmentation runs is the
opposite of what it is for, so pause() stops the render loop and leaves the
last frame on screen. See CpuFractalWidget.pause().
Exceptions¶
The GPU renderer could not be built. Always caught by |
Classes¶
Where the GPU field is looking, at one instant. |
|
How far along the trajectory the camera is, as a number that only grows. |
|
CPU capacity used to choose a conservative automatic render quality. |
|
The four-frame temporal window, and nothing else. |
|
Where the pointer is, and whether it is pushing. |
|
Floats the camera between the coordinates worth looking at. |
|
What the user can move while it is running. |
|
What the picture is made of. Every field is a Preferences row. |
Functions¶
|
Push the saved settings into every running backdrop. |
|
Return |
|
Build the fractal backdrop, GPU when there is one. |
|
A tour over the committed regions, or an empty one without them. |
|
Whether the GPU renderer can be built at all. |
|
Speed the descent up or slow it down. |
|
The pattern that can actually be drawn here. |
|
Whether this Qt platform can host a GL canvas at all. |
|
Which renderer will actually run, given what is installed. |
|
How many Numba workers to take, leaving the application some. |
|
Resolve |
|
Send every running backdrop back to the surface. |
|
The camera at |
|
How many pixels to shade for a widget of this size. |
Module Contents¶
- exception spacr.qt.widgets.fractal_travel.GpuBackendError[source]¶
Bases:
RuntimeErrorThe GPU renderer could not be built. Always caught by
auto.Initialize self. See help(type(self)) for accurate signature.
- class spacr.qt.widgets.fractal_travel.CameraState[source]¶
Where the GPU field is looking, at one instant.
- Parameters:
t – the time the state was computed for, in seconds.
depth – distance travelled along the trajectory, divided by 12.
tx – horizontal drift offset in shader coordinates, scaled by the dream amount.
ty – vertical drift offset in shader coordinates, scaled by the dream amount.
rotation – rotation of the view in radians.
shear_x – upper off-diagonal term of the 2 × 2 stretch-and-shear matrix applied after the rotation.
shear_y – lower off-diagonal term of that matrix.
stretch_x – horizontal diagonal term of that matrix; 1.0 leaves the axis unstretched.
stretch_y – vertical diagonal term of that matrix; 1.0 leaves the axis unstretched.
palette_phase – offset added to the colour palette’s phase.
- class spacr.qt.widgets.fractal_travel.DepthPhase[source]¶
How far along the trajectory the camera is, as a number that only grows.
SPEED MUST CHANGE THE RATE, NOT THE POSITION. Depth used to be
t * speed, so a scroll that doubled the speed doubled the depth in the same instant: measured at t=60s, speed 1 -> 2 moved the camera 5.0 units, which is 3,600 frames of ordinary travel arriving in one. That is the jump reported as “it ruins the immersion when it jumps”.Integrating instead –
phase += dt * speed– makes a speed change continuous by construction. The camera is exactly where it was; only how fast it leaves matters.Kept as a small object rather than two floats on the widget because the invariant is worth naming:
valuenever decreases.Create the depth phase at rest, with no previous timestamp.
- advance(t: float, speed: float) float[source]¶
Move the phase to wall-clock
tatspeed, and return it.A t that goes BACKWARDS – a restart, a clock reset – re-bases rather than rewinding: the phase is the distance travelled, and travel does not un-happen.
- Parameters:
t – wall-clock time in seconds; the first call, or a time earlier than the last one, only re-bases.
speed – travel rate in phase units per second; negative values count as 0.
- class spacr.qt.widgets.fractal_travel.HardwareProfile[source]¶
CPU capacity used to choose a conservative automatic render quality.
- Parameters:
logical_cpus – logical processors available to the application.
- static detect() HardwareProfile[source]¶
Read what this machine can offer the renderer.
FLOORED AT ONE CPU.
os.cpu_countreturns None on some platforms, and a worker pool sized from None is a crash rather than a slow backdrop.- Returns:
the profile.
- class spacr.qt.widgets.fractal_travel.OrbitEngine(thread_count: int)[source]¶
The four-frame temporal window, and nothing else.
Holds no keyframes: the only state is the ring of the last four jitter phases, which is what the antialiasing needs and all it needs.
Four is the published 2x2.
samples, set by the widget from the Supersampling setting, makes it N x N phases walked over N * N frames; one a side keeps no history at all.- Parameters:
thread_count – worker threads to render with. Clamped to at least one, so a caller that computed zero from an unavailable CPU count still renders.
Create the orbit engine without allocating its buffers yet.
- Parameters:
thread_count – worker threads to render with; clamped to at least one.
- render(width: int, height: int, t: float, speed: float, dream: float, iterations: int, pointer_x: float = 0.0, pointer_y: float = 0.0, pull: float = 0.0, push: float = 0.0) numpy.ndarray[source]¶
Render one frame of the orbit.
- Parameters:
width – the frame’s width in pixels.
height – its height in pixels.
t – the time to render at.
speed – the travel-speed multiplier.
dream – how far the orbit wanders.
iterations – the iteration budget per pixel.
pointer_x – horizontal pointer influence, 0 for none.
- Returns:
the rendered frame.
- class spacr.qt.widgets.fractal_travel.Pointer[source]¶
Where the pointer is, and whether it is pushing.
SAMPLED, NEVER RECEIVED. The backdrop sits behind every control; a widget that accepted mouse events would eat the click meant for the button on top of it. So nothing here is a mouse handler – the position is read from
QCursor.pos()on the render tick, and the buttons fromQApplication.mouseButtons(), both of which are global state that costs nothing and steals nothing.Coordinates come back in the -1..1 space the fractals already work in, with (0, 0) at the centre, so a kernel can use them without knowing anything about widgets.
Create the pointer state the kernels read.
Drag movement accumulates rather than being sampled: a dropped frame does not lose the movement, it arrives with the next one instead.
- sample(widget, size: float = 1.0, strength: float = 1.0) Pointer[source]¶
Read the pointer relative to
widget. Never raises.- Parameters:
widget – visible Qt widget whose global rectangle defines the returned centred coordinates and inside/outside state. Coordinates use the short edge as their scale, so that axis maps to -1..1 and the long axis may extend beyond it.
size – how far the effect reaches in short-edge-normalised coordinate units; 1.0 reaches the widget’s short edge.
strength – how hard it pulls, 0 to 2.
- class spacr.qt.widgets.fractal_travel.RegionTour(regions, dwell: float = 18.0, travel: float = 9.0)[source]¶
Floats the camera between the coordinates worth looking at.
Around twenty regions are chosen on the image and the camera floats automatically towards them.
SMOOTHLY IS THE WHOLE REQUIREMENT, so the interpolation is a smoothstep rather than a straight line: it leaves one region and arrives at the next with zero velocity, which is what stops the arrival reading as a stop. A linear blend is continuous in position and not in velocity, and the eye sees the corner.
DRIFT IS OFF THE MOMENT THE USER TAKES THE CAMERA. Dragging is a statement about where they want to be, and a tour that resumes over it is the application arguing.
take_over()stops it for good;restart()is what Ctrl+R calls.- Parameters:
regions –
(name, x, y, half_width, score)rows, usuallyspacr.qt.widgets.fractal_regions.REGIONS.dwell – seconds spent at a region before leaving.
travel – seconds spent moving between two regions.
Set up a tour that dwells on each region and travels between them.
- Parameters:
regions – the regions to visit, in order.
dwell – seconds spent on a region; floored just above zero, so a tour cannot be configured to skip its own stops.
travel – seconds spent moving between them, floored the same way.
- target_at(seconds: float) tuple | None[source]¶
Where the camera should be heading at
seconds.Nonewhen the tour is not steering, so a caller can leave the camera exactly where the user put it rather than being handed a coordinate it has to ignore.- Parameters:
seconds – time along the tour in seconds; it wraps after one full circuit of the regions.
- class spacr.qt.widgets.fractal_travel.RuntimeControls[source]¶
What the user can move while it is running.
- speed_at(t: float) float[source]¶
The speed to use at
tseconds.Constant
speedunlessvariable_speedis on, in which case it sweeps betweenspeed_minandspeed_max– named bounds rather than a hidden percentage, so what the travel will actually do is readable from the settings instead of inferred from watching it.speed_periodis how long one full sweep takes, which is the “how gradually” control: a larger number is a slower CHANGE, not a slower fractal.The bounds are used in whichever order they are given: a min above a max is a swapped pair, not an empty range, and refusing to animate would be a worse answer than animating between the two numbers.
- Parameters:
t – elapsed time in seconds; it sets the point on the sine sweep when variable speed is on and is ignored otherwise.
- class spacr.qt.widgets.fractal_travel.Settings[source]¶
What the picture is made of. Every field is a Preferences row.
supersamplingis samples per pixel along each axis, the Fractal tab’s Supersampling row. Every renderer used a fixed 2x2 whatever it said; now the shaders and the CPU kernels take an N x N grid from it. Two is the published grid, so aSettings()nobody filled in draws what it always drew.
- spacr.qt.widgets.fractal_travel.apply_saved_controls() int[source]¶
Push the saved settings into every running backdrop.
- Returns:
how many were updated.
THE BACKDROP KEEPS THE CONTROLS IT WAS BUILT WITH. Saving Preferences writes new values to the store, but an existing
RuntimeControlsobject otherwise continues holding its old values. This function synchronises every live object so changes take effect immediately.Everything that can change while a backdrop is on screen is pushed here. What cannot – the pattern, the backend, the quality, the scale and the Mandelbrot reference orbit – needs the backdrop rebuilding, which
spacr.qt.widgets.ambient.rebuild_the_spaceout_backdrops()does when Preferences is saved.
- spacr.qt.widgets.fractal_travel.clamp(value: float, low: float, high: float) float[source]¶
Return
valuelimited to the inclusivelow/highrange.- Parameters:
value – the number to limit.
low – inclusive lower bound.
high – inclusive upper bound.
- spacr.qt.widgets.fractal_travel.create_fractal_widget(settings: Settings | None = None, controls: RuntimeControls | None = None, hardware: HardwareProfile | None = None)[source]¶
Build the fractal backdrop, GPU when there is one.
- Returns:
a QWidget carrying
backend_name,stats_text(),pause(),resume()andshutdown(). Never raises for a missing GPU: an explicitbackend='gpu'that cannot be built still falls back, because a backdrop is not worth refusing to start the application over.
- spacr.qt.widgets.fractal_travel.default_region_tour(**kwargs) RegionTour[source]¶
A tour over the committed regions, or an empty one without them.
- spacr.qt.widgets.fractal_travel.gpu_is_available() bool[source]¶
Whether the GPU renderer can be built at all.
Asked rather than assumed, and asked WITHOUT importing vispy into the application when the answer is no:
importlib.util.find_speclooks the module up without executing it, so a missing vispy costs nothing and a present one is not initialised twice.A platform that cannot host a GL canvas counts as no GPU, because the alternative is a core dump rather than an exception.
- spacr.qt.widgets.fractal_travel.nudge_zoom_rate(steps: int) float[source]¶
Speed the descent up or slow it down.
- Parameters:
steps – how many notches; positive is faster.
- Returns:
the resulting rate, or 0.0 when no backdrop is running.
Clamped, unlike the settings fields: this is a key held down rather than a number somebody typed, so there is nothing to tell them about and a rate of 10^12 from leaning on an arrow key is not a request.
- spacr.qt.widgets.fractal_travel.pattern_for_this_machine(pattern: str, backend: str = 'auto') str[source]¶
The pattern that can actually be drawn here.
- Parameters:
pattern – what the user asked for.
backend – the resolved backend, or
"auto".
- Returns:
pattern, or the fallback when it cannot be drawn.
TWO PATTERNS ARE GPU-ONLY. Mandelbrot needs a texture of the reference orbit and a shader to perturb around it, and
orbit_gpuis a fragment shader with no numba twin – the CPU orbit fold is a DIFFERENT picture, four samples across four frames rather than four of one instant, which is why the two are separate entries at all.So a machine with no usable GL context gets the orbit fold instead – it has a CPU renderer and is the cheapest of the ones that do – rather than a backdrop that draws nothing.
Silent, and deliberately: the backdrop is decoration, and a dialog explaining that a machine cannot run one of five ornaments is worth less than the interruption costs.
- spacr.qt.widgets.fractal_travel.platform_can_do_opengl() bool[source]¶
Whether this Qt platform can host a GL canvas at all.
CHECKED BEFORE THE GPU WIDGET IS BUILT, because getting it wrong does not raise. On the
offscreenplatform Qt prints “QOpenGLWidget is not supported on this platform” and the process DUMPS CORE – which noexceptaround the constructor can catch, so the fallback increate_fractal_widgetwould never run. Every test and every headless launch is that platform.
- spacr.qt.widgets.fractal_travel.resolve_backend(requested: str) str[source]¶
Which renderer will actually run, given what is installed.
- Parameters:
requested –
'auto','gpu'or'cpu'; any other value is treated asDEFAULT_BACKEND, and'auto'picks the GPU when one is available.- Returns:
'gpu'or'cpu'– never'auto', because a caller showing the user which one they are on cannot show them “auto”.
- spacr.qt.widgets.fractal_travel.resolved_cpu_threads(settings: Settings, hardware: HardwareProfile) int[source]¶
How many Numba workers to take, leaving the application some.
Capped at 24 because beyond that the scheduling overhead grows for this image size, and capped below the machine’s count because a backdrop that takes every core starves the run the user actually cares about.
- Parameters:
settings – backdrop settings; a non-None
cpu_threadsis used, clamped to the threads available, instead of the automatic choice.hardware – CPU profile; its
logical_cpus, further capped by Numba’s thread limit and by 24, is the number of threads available.
- spacr.qt.widgets.fractal_travel.resolved_quality(requested: str, backend: str, hardware: HardwareProfile) str[source]¶
Resolve
autoto a quality the selected backend can sustain.Explicit quality names pass through unchanged. GPU auto mode uses the balanced profile because GPU capacity is otherwise unknown; CPU auto mode uses high only when at least sixteen logical processors are available.
- Parameters:
requested –
auto,balanced,high, or a future explicit quality name.backend – resolved renderer backend, normally
gpuorcpu.hardware – detected logical-CPU capacity.
- Returns:
the explicit quality name to apply.
- spacr.qt.widgets.fractal_travel.restart_the_dive() None[source]¶
Send every running backdrop back to the surface.
Called when the fractal settings change. A dive that resumed at the depth it had reached would apply the new numbers to a viewport thirty decades down, where a changed starting scale or iteration count has nothing recognisable to act on – so the change looks as though it did nothing.
- spacr.qt.widgets.fractal_travel.state_at_seconds(t: float, speed: float, dream: float, depth_phase: float | None = None) CameraState[source]¶
The camera at
t. Pure, so a test can assert it moves.- Parameters:
t – time in seconds; it drives every oscillation of the camera.
speed – travel rate, used only when
depth_phaseis None.dream – amount of drift, shear and stretch; 0 keeps the camera centred and unskewed, though it still rotates.
depth_phase – the integrated distance travelled. When given it is what positions the camera along the trajectory, and
speedno longer does – which is what stops a scroll teleporting it.Nonereproduces the oldt * speed, for callers that have no phase to keep.
- spacr.qt.widgets.fractal_travel.target_render_size(logical_width: int, logical_height: int, device_scale: float, render_scale: float, base_pixels: float = 0.0, adaptive_scale: float = 1.0) tuple[source]¶
How many pixels to shade for a widget of this size.
LIFTED OUT OF THE CANVAS so it can be measured. The backdrop asks which of four candidates makes fullscreen choppy while the backdrop is smooth, and says to answer with numbers before writing a fix – which is not possible while the arithmetic only exists inside a nested method on a class that needs a GL context.
The rule itself is unchanged: shade
render_scalesquared of the widget’s own physical pixels, never more than the widget has and never fewer than 180,000, keeping the aspect ratio and an even width and height.- Parameters:
logical_width – widget width in logical pixels, floored at 320.
logical_height – widget height in logical pixels, floored at 180.
device_scale – device pixel ratio, floored at 1.0.
render_scale – linear fraction of the physical pixels to shade; 0 or less uses
base_pixelsinstead.
- Returns:
(width, height)in physical pixels.
Nested helpers¶
- _join_on_destroy._join(*_args)¶
Stop and join the render thread. Closes over the thread ONLY.
spacr/qt/widgets/fractal_travel.py:935
- _make_cpu_widget.CpuFractalWidget.__init__(self, parent=None) None¶
Build the CPU canvas, painting its own background.
spacr/qt/widgets/fractal_travel.py:1096
- _make_cpu_widget.CpuFractalWidget._accept_frame(self, frame, render_seconds: float) None¶
Take a rendered frame, note how long it took, and repaint.
spacr/qt/widgets/fractal_travel.py:1268
- _make_cpu_widget.CpuFractalWidget._adapt_resolution(self) None¶
Trade resolution for frame rate, from the measured render time.
WAITS FOR TWELVE FRAMES and then only reconsiders every twenty-fourth, so the scale settles instead of oscillating on a single slow frame. The budget is 78% of the period rather than all of it, because Qt’s own conversion and the rest of the application have to fit in the remainder.
Both directions are damped and clamped – down no further than 0.58, up no further than 1.35 – so a stall cannot drive the picture to nothing and a fast machine cannot drive it past what the window can show.
spacr/qt/widgets/fractal_travel.py:1242
- _make_cpu_widget.CpuFractalWidget._on_failure(self, message: str) None¶
Record the worker’s error and stop treating a frame as pending.
spacr/qt/widgets/fractal_travel.py:1299
- _make_cpu_widget.CpuFractalWidget._request_frame(self) None¶
Ask the worker for the next frame, unless stopped or paused.
spacr/qt/widgets/fractal_travel.py:1217
- _make_cpu_widget.CpuFractalWidget._target_size(self) tuple[int, int]¶
The pixel size to shade at, from the render scale and the window.
RENDER SCALE is the fraction of the window’s own pixels to shade: 1.0 is native and anything less trades sharpness for speed. It was a setting nobody read, and it is the direct answer to “how do I get the image sharper”.
ON THIS PATH ONLY, AND THAT IS WORTH SAYING WHERE THE CLAIM IS MADE.
_render_scale()is read here and nowhere else: the GPU canvas shades its physical size at every one of its three uses and never callstarget_render_size. So on a machine with a working GPU – which is the default,backend='auto'– this setting has no effect whatever, and a reader who came here from the sentence above would otherwise go looking for the code that applies it.IT IS NOT A VISIBLE CONTROL, which is why this is a comment rather than a tooltip:
spaceout/fractal_render_scaleis a bare QSettings key with no row in Preferences, so nobody can move a slider and watch nothing happen. Making the GPU honour it would mean render-to-texture, which was measured and rejected: every shader runs 7x to 25x inside its frame budget at 4K, so there is no headroom to buy back.spacr/qt/widgets/fractal_travel.py:1185
- _make_cpu_widget.CpuFractalWidget.closeEvent(self, event) None¶
Shut the render thread down before the widget goes.
spacr/qt/widgets/fractal_travel.py:1358
- _make_cpu_widget.CpuFractalWidget.is_paused(self) bool¶
Whether the animation is currently held.
spacr/qt/widgets/fractal_travel.py:1169
- _make_cpu_widget.CpuFractalWidget.paintEvent(self, _event) None¶
Draw the last frame, or the ground colour before there is one.
spacr/qt/widgets/fractal_travel.py:1307
- _make_cpu_widget.CpuFractalWidget.pause(self) bool¶
Stop rendering and leave the last frame on screen.
Called when a RUN STARTS. A backdrop taking nineteen cores while a segmentation is queued is the opposite of what it is for, and stopping is better than thinning: a slower fractal still holds the threads.
- Returns:
True when this call did the stopping, False when it was already paused – so a caller can tell whether to resume.
spacr/qt/widgets/fractal_travel.py:1144
- _make_cpu_widget.CpuFractalWidget.resizeEvent(self, event) None¶
Ask for a frame at the new size, unless one is already in flight.
Guarded so a drag-resize does not queue a render per pixel of travel – the worker would then be shading sizes the window has already left.
spacr/qt/widgets/fractal_travel.py:1317
- _make_cpu_widget.CpuFractalWidget.resume(self) bool¶
Start rendering again from where the clock left off.
spacr/qt/widgets/fractal_travel.py:1161
- _make_cpu_widget.CpuFractalWidget.set_animating(self, on: bool) bool¶
AmbientWidget’s verb for the same thing.The ambient backdrop this replaces is stopped and started with
set_animating, and its callers – the Home screen’s teardown among them – reach for that name. Answering to it makes this a drop-in rather than something every call site has to learn.spacr/qt/widgets/fractal_travel.py:1173
- _make_cpu_widget.CpuFractalWidget.shutdown(self) None¶
Stop for good and join the thread. Safe to call twice.
spacr/qt/widgets/fractal_travel.py:1344
- _make_cpu_widget.CpuFractalWidget.stats_text(self) str¶
The render size, rate and state, for the overlay.
spacr/qt/widgets/fractal_travel.py:1327
- _make_cpu_widget._Worker.__init__(self) None¶
Build the engine this thread will shade with.
spacr/qt/widgets/fractal_travel.py:1033
- _make_cpu_widget._Worker._say_something(signal, *args) None¶
Emit, unless the object that owns the signal has been freed.
The last thing this thread does with a widget that is being destroyed, so it must never raise: nothing is listening, and an exception here ends the process rather than the frame.
spacr/qt/widgets/fractal_travel.py:1069
- _make_cpu_widget._Worker.render(self, request: object) None¶
Shade one frame and hand it back, if anyone is still there.
A FRAME CAN FINISH AFTER ITS WIDGET IS GONE. The shading runs on this thread while the GUI thread may be closing the window or swapping the pattern, and Qt deletes the worker’s C++ side with it – so
emitraises “Signal source has been deleted”. The except below then emitted the FAILURE signal, which raised the same way, and an exception escaping a slot on a QThread takes the process down: “Aborted (core dumped)”.spacr/qt/widgets/fractal_travel.py:1040
- _make_gpu_widget.GpuFractalWidget.__init__(self, parent=None) None¶
Build the GL widget under the heavy-import lock.
spacr/qt/widgets/fractal_travel.py:2598
- _make_gpu_widget.GpuFractalWidget.closeEvent(self, event) None¶
Shut the canvas down before the widget goes.
spacr/qt/widgets/fractal_travel.py:2660
- _make_gpu_widget.GpuFractalWidget.is_paused(self) bool¶
Whether the canvas is currently held.
spacr/qt/widgets/fractal_travel.py:2632
- _make_gpu_widget.GpuFractalWidget.pause(self) bool¶
Stop drawing while a run is on. The last frame stays up.
spacr/qt/widgets/fractal_travel.py:2618
- _make_gpu_widget.GpuFractalWidget.resume(self) bool¶
Restart the animation. Returns whether it was paused.
spacr/qt/widgets/fractal_travel.py:2625
- _make_gpu_widget.GpuFractalWidget.set_animating(self, on: bool) bool¶
AmbientWidget’s verb for the same thing.The ambient backdrop this replaces is stopped and started with
set_animating, and its callers – the Home screen’s teardown among them – reach for that name. Answering to it makes this a drop-in rather than something every call site has to learn.spacr/qt/widgets/fractal_travel.py:2636
- _make_gpu_widget.GpuFractalWidget.shutdown(self) None¶
Stop for good. Safe to call twice and after Qt has freed it.
spacr/qt/widgets/fractal_travel.py:2651
- _make_gpu_widget.GpuFractalWidget.stats_text(self) str¶
The render size, rate and state, for the overlay.
spacr/qt/widgets/fractal_travel.py:2647
- _make_gpu_widget._Canvas.__init__(self) None¶
Build the GL canvas, hidden until it is placed.
spacr/qt/widgets/fractal_travel.py:2004
- _make_gpu_widget._Canvas._budget_at(self, depth: float, orbit) int¶
The iteration budget at
depth, capped by the reference.- Parameters:
depth – the depth in decades.
orbit – the reference orbit, or None while it is built.
spacr/qt/widgets/fractal_travel.py:2192
- _make_gpu_widget._Canvas._dive_uniforms(self, depth: float, centre, orbit) dict¶
The zoom’s uniforms for a depth and a centre.
- Parameters:
depth – the depth in decades.
centre –
(re, im)offset from the reference orbit.orbit – the reference orbit, or None while it is built.
spacr/qt/widgets/fractal_travel.py:2208
- _make_gpu_widget._Canvas._galaxy_uniforms(self, phase: float) dict¶
Where the passing galaxies are, for the space pattern.
- Parameters:
phase – the flight’s clock, the shader’s
u_time.- Returns:
{}for every other pattern.
spacr/qt/widgets/fractal_travel.py:2089
- _make_gpu_widget._Canvas._glide_frame(self, orbit, seconds: float) tuple¶
One frame of the glide toward the busiest part of the view.
- Parameters:
orbit – the reference orbit, or None while it is built.
seconds – time since the previous frame.
- Returns:
(centre, depth).
THE DECIDING IS IN
_GlideCamera, which has no Qt in it. This is the part that cannot be: reading the settings, handing it the drag, and surveying the view on a worker thread so an escape map never stalls a frame. A survey that lands after the reference moved describes a different frame and is dropped.spacr/qt/widgets/fractal_travel.py:2229
- _make_gpu_widget._Canvas._glide_frame._look()¶
Score the current view for structure. Off the GUI thread.
spacr/qt/widgets/fractal_travel.py:2288
- _make_gpu_widget._Canvas._mandelbrot_uniforms(self, elapsed: float) dict¶
The zoom’s own uniforms for this instant.
- Returns:
{}for every other pattern, so the shared update can splice it in unconditionally.
spacr/qt/widgets/fractal_travel.py:2127
- _make_gpu_widget._Canvas._on_native_destroyed(self, *_args) None¶
Mark the canvas dead and stop its timer.
The native window can go before Python does, and a timer that fires after it draws into an object that is not there.
spacr/qt/widgets/fractal_travel.py:2567
- _make_gpu_widget._Canvas._on_timer(self, _event) None¶
Advance one frame, unless paused or already torn down.
spacr/qt/widgets/fractal_travel.py:2547
- _make_gpu_widget._Canvas._pointer_state(self)¶
(x, y, pull, push)for the shader, in -1..1 space.- Returns:
zeros when the pointer is not being followed, so a shader can multiply by them unconditionally.
THE MANDELBROT IS DRAGGED, NOT ATTRACTED. Mouse interaction moves it only through drag input; the pointer’s stationary position does not pull the view about.
The other three patterns are fields that can be warped toward a point and look right doing it. A deep zoom is a camera: pulling its coordinates toward wherever the mouse happens to rest slides the picture continuously, which reads as the image drifting away from you rather than as anything you did.
The pointer is still SAMPLED, because that is what accumulates the drag –
_steerconsumes it. Onlypullandpush, which are the position-driven terms, are withheld.spacr/qt/widgets/fractal_travel.py:2475
- _make_gpu_widget._Canvas._refine_the_reference(self, camera, orbit, budget, depth, span)¶
Move the reference back onto the boundary, now and then.
Surveyed and rebuilt on a worker thread: the survey is a 96x54 escape map and the rebuild iterates the orbit at full precision, neither of which belongs in a frame. When the new reference lands, the camera’s centre and target move by the opposite of the reference’s move, so the picture stays exactly where it was.
spacr/qt/widgets/fractal_travel.py:2393
- _make_gpu_widget._Canvas._refine_the_reference._work()¶
Find a better reference point for the current view. Off the GUI thread.
spacr/qt/widgets/fractal_travel.py:2437
- _make_gpu_widget._Canvas._start_the_reference_orbit(self) None¶
Iterate Z off the GUI thread and upload it when it is ready.
spacr/qt/widgets/fractal_travel.py:2101
- _make_gpu_widget._Canvas._start_the_reference_orbit._work()¶
Compute the reference orbit. Off the GUI thread.
It is the expensive part of deep zooming – high-precision iteration of one point that every pixel is then perturbed from – so it must not run where it would stall the frame it is for.
spacr/qt/widgets/fractal_travel.py:2107
- _make_gpu_widget._Canvas._steer(self, depth: float, budget: int, orbit, seconds: float = 0.0)¶
Where the dive is heading, in the reference orbit’s frame.
- Parameters:
seconds – the simulation clock; unused by the fixed and guided paths, and kept so every caller passes the same arguments. The tour path is
_glide_frame().- Returns:
(offset_re, offset_im)foru_center_offset.
THE DECIDING IS IN
SteeringCamera, which has no Qt in it and can be driven frame by frame in a test. This method is the part that cannot be: reading the settings, and running the search on a worker thread so a 96x54 escape map does not stall the frame.Every claim about how smooth the motion is used to come from a simulation written beside the code rather than from the code, because this logic lived inside a canvas that needs a GL context to exist. That is why three fixes in a row were wrong.
spacr/qt/widgets/fractal_travel.py:2302
- _make_gpu_widget._Canvas._steer._look()¶
Plan the next guided step. Off the GUI thread.
spacr/qt/widgets/fractal_travel.py:2372
- _make_gpu_widget._Canvas._update_uniforms(self, elapsed: float) None¶
Push this frame’s camera and time into the shader.
The size is floored at one pixel: a canvas mid-resize can report zero, and a zero dimension reaches the shader as a division by nothing.
spacr/qt/widgets/fractal_travel.py:2050
- _make_gpu_widget._Canvas._upload_the_orbit_if_it_arrived(self) None¶
Put the finished orbit into the shader’s texture.
ON THE GUI THREAD, from inside the draw, because that is where the GL context lives. Uploaded once:
_orbit_uploadedis the orbit object itself, so a rebuilt one would be noticed.spacr/qt/widgets/fractal_travel.py:2456
- _make_gpu_widget._Canvas.on_draw(self, _event) None¶
Draw one frame, unless the canvas is already torn down.
spacr/qt/widgets/fractal_travel.py:2516
- _make_gpu_widget._Canvas.on_resize(self, _event) None¶
Resize the GL viewport, never to zero.
A canvas mid-resize reports zero, and a zero viewport is a GL error rather than a small picture.
spacr/qt/widgets/fractal_travel.py:2507
- _make_gpu_widget._Canvas.stats_text(self) str¶
The render size, rate and state, for the overlay.
spacr/qt/widgets/fractal_travel.py:2576
- _make_gpu_widget._Canvas.stop_timer(self) None¶
Stop the vispy timer. Safe to call twice, and after deletion.
spacr/qt/widgets/fractal_travel.py:2560
- _make_gpu_widget._mandel_setting(name, fallback=None)¶
One Mandelbrot setting, falling back to the published default.
spacr/qt/widgets/fractal_travel.py:1979
- spacr.qt.widgets.fractal_travel.set_num_threads(_n: int) None[source]¶
Do nothing: with no Numba there is no thread pool to size.
A NO-OP RATHER THAN AN ABSENT NAME. The caller sets the thread count unconditionally, so leaving this undefined would turn a missing optional dependency into an AttributeError at the call site – far from the import that actually failed.
- Parameters:
_n – the thread count, ignored.
spacr/qt/widgets/fractal_travel.py:45