Object System¶
This document provides a comprehensive overview of the object system architecture in ZooUI, explaining how objects are positioned, rendered, and animated within the Zooming User Interface. The object system is responsible for managing the spatial relationships, physics simulation, autosave backups, clipboard operations, parallel rendering, and rendering of all elements in the scene.
Overview¶
The object system is responsible for:
Managing 3D position and velocity (x, y, zoom level)
Simulating physics with exponential damping
Handling coordinate transformations between reference frames
Rendering media objects at various zoom levels with parallel processing
Managing scene composition and object lifecycle
Automatically backing up scenes with configurable rotation and expiration
Copy/paste operations with grid-aligned positioning
The system uses a hierarchical class structure where PhysicalObject provides
the base physics simulation, MediaObject adds media-specific positioning and rendering,
and concrete media types implement specific rendering strategies. The Scene class
delegates advanced functionality to three sub-managers: SceneParallelRenderer,
SceneAutosaveManager, and SceneClipboardManager.
Architecture¶
The object system consists of the following class hierarchy:
PhysicalObject (Abstract Base)
│ • Position: _x, _y, _z (zoom level)
│ • Velocity: vx, vy, vz
│ • Damping factor
│ • Centre point
│ • Physics simulation (step, move, zoom, aim)
│
├── MediaObject (Abstract)
│ │ • Media ID and scene reference
│ │ • Coordinate transforms (scene ↔ screen)
│ │ • Scaling management (scale, onscreen_size)
│ │ • Position properties (topleft, bottomright, centre)
│ │ • Visibility testing (hides, fit)
│ │ • Abstract render() method
│ │ • Serialization: to_dict() / from_dict()
│ │
│ ├── TiledMediaObject
│ │ ├── Large image support via tile pyramid
│ │ ├── Tile grid management
│ │ ├── Progressive loading with placeholders
│ │ ├── Process-based parallel conversion
│ │ ├── Converter support (PDF, images)
│ │ └── Efficient for huge images
│ │
│ ├── PdfMediaObject (extends TiledMediaObject)
│ │ ├── Multi-page PDF with per-page tiling
│ │ ├── Page navigation (Ctrl+↑/↓)
│ │ ├── Smart alignment (forward: top-left, backward: bottom-right)
│ │ ├── Lazy 2-page tiling buffer
│ │ ├── Go-to-page dialog (Ctrl+Alt+G)
│ │ └── Large PDF (>2 MB) page selection dialog
│ │
│ ├── StringMediaObject
│ │ • Hybrid rendering: moving (direct) vs static (cached)
│ │ • Multi-line text support
│ │ • Font scaling based on zoom
│ │ • Transparent background
│ │ • ParallelLayoutCalculator (thread-pool text layout)
│ │ • TextLayoutData (thread-safe pre-calculated layouts)
│ │
│ └── SVGMediaObject
│ • Vector graphics rendering
│ • Scalable without quality loss
│ • QSvgRenderer integration
│ • Transparent background
│ • SVG content caching (in-memory + disk via SVGCache)
│ • Modification tracking (picker/clipboard)
│ • Serialization: to_dict() / from_dict()
│ • SVG shape utilities (arrow, circle, square, stick, triangle)
│ • Custom size visibility thresholds
│
└── Scene
• Container for MediaObjects
• Viewport management
• Scene persistence (save/load)
• Thread-safe operations (RLock)
• Bulk object selection (ctrl+click/drag)
• Render order toggle (smaller/larger on top)
• Rendering coordination
├── SceneParallelRenderer (parallel.py)
│ • Parallel rendering with thread pools
│ • Integrates ParallelLayoutCalculator & PriorityBatcher
│ • Statistics tracking (batches, cache hits, timing)
├── SceneAutosaveManager (autosave.py)
│ • Timer-based per-scene auto-backup
│ • Configurable interval, rotation, expiration
│ • Integrates BackupManager
└── SceneClipboardManager (clipboard.py)
• Copy/paste operations
• Grid-aligned positioning
• Offset support
Supporting Services
├── ZoomManager (objectsutils/zoom/)
│ • Zoom level clamping (-10 to 12)
│ • Crash prevention at extreme zoom levels
└── PriorityBatcher (sceneutils/)
• Viewport-aware priority batching
• 4 priority levels (HIGH, MEDIUM, LOW, BACKGROUND)
• heapq-based priority sorting
SVG Utilities (mediaobjectsutils/svg/)
├── SVGCache
│ • Hashed disk cache with svg_ prefix
│ • Singleton access via get_svg_cache()
└── Shape Utils (5 files)
• svgarrowutils, svgcircleutils, svgsquareutils
• svgstickutils, svgtriangleutils
• XML ElementTree parsing with SVG namespace
• Shape detection and elongation
Core Components¶
PhysicalObject¶
The PhysicalObject class provides the foundation for all objects that exist in
the zooming user interface. It manages position, velocity, and physics simulation.
Key Attributes:
_x, _y, _z: Position coordinates (z represents zoom level)vx, vy, vz: Velocity componentsdamping_factor: Controls how quickly velocity decays (default: 512)_centre: Center point in object coordinates
Physics Model:
The object system uses exponential damping for smooth deceleration:
v(t) = u * d^(-t)
where:
u = initial velocity
d = damping_factor
t = time in seconds
Displacement is calculated by integrating the velocity:
s(t) = (u / log(d)) * (1 - d^(-t))
This creates smooth, natural-feeling motion that gradually comes to rest.
Key Methods:
move(dx, dy): Move object by displacement (dx, dy)zoom(amount): Zoom by given amount with centre fixed on screenaim(v, s, t=None): Calculate initial velocity for desired displacementstep(t): Update position and velocity for time step tmoving: Property indicating if object has non-zero velocity
MediaObject¶
The MediaObject class extends PhysicalObject to add media-specific
functionality and coordinate transformations.
Key Attributes:
_media_id: Unique identifier for the media content_scene: Reference to parent Scene objecttransparent: Boolean flag for transparency (subclass-specific)
Coordinate Systems:
MediaObjects use three coordinate systems:
Screen Coordinates - Absolute pixel positions on display
Scene Coordinates - Positions relative to scene origin, scaled by scene zoom
Image Coordinates - Positions within the media object, scaled by object zoom
Coordinate Transformations:
From MediaObject position to screen coordinates:
screen_x = scene.origin[0] + obj.pos[0] * (2 ** scene.zoomlevel)
screen_y = scene.origin[1] + obj.pos[1] * (2 ** scene.zoomlevel)
From MediaObject centre (image coords) to screen coordinates:
# First convert image coords to scene coords
C_s_x = obj.pos[0] + obj._centre[0] * (2 ** obj.zoomlevel)
C_s_y = obj.pos[1] + obj._centre[1] * (2 ** obj.zoomlevel)
# Then convert scene coords to screen coords
screen_x = scene.origin[0] + C_s_x * (2 ** scene.zoomlevel)
screen_y = scene.origin[1] + C_s_y * (2 ** scene.zoomlevel)
Key Properties:
scale: Combined zoom factor:2 ** (scene.zoomlevel + self.zoomlevel)topleft: On-screen position of top-left cornerbottomright: On-screen position of bottom-right corneronscreen_size: Tuple (width, height) in screen pixelsonscreen_area: Total pixel area occupied on screen
Key Methods:
render(painter, mode): Abstract method for rendering (implemented by subclasses)move(dx, dy): Move relative to scene using screen distancezoom(amount): Zoom with centre fixed on screenhides(other): Check if this object completely obscures anotherfit(bbox): Fit and center object within bounding boxto_dict(): Serialize object to dictionary (base implementation)from_dict(d, scene): Deserialize object from dictionary (base implementation)
Scene¶
The Scene class manages collections of MediaObjects and coordinates rendering.
As of v0.3.2+, Scene delegates advanced functionality to three dedicated sub-managers
for cleaner separation of concerns.
Key Attributes:
__objects: List of MediaObjects in the scene__objects_lock: Thread-safe access control (RLock)__viewport_size: Current viewport dimensionsselection: Currently selected object (list of MediaObjects for bulk selection)right_selection: Right-click selected objectstandard_viewport_size: Default size for scene persistence (1280x720)__config: Stored configuration dictionary__last_save_path: Path of last saved file (for autosave)__autosave_manager:SceneAutosaveManagerinstance__clipboard_manager:SceneClipboardManagerinstance__parallel_renderer:SceneParallelRendererinstance
Scene Configuration:
The Scene constructor accepts an optional config dictionary with the following keys:
config = {
"zoom": {
"zoomlevel": 0.0, # Initial scene zoom level
"origin": [640, 360], # Initial viewport origin
},
"render": {
"order": "smaller_on_top", # or "larger_on_top"
},
"parallel_rendering": {
"enabled": True,
},
"autosave": {
"enabled": True,
"interval": 300, # Seconds (default: 5 minutes)
"max_backups": 20,
"expire_days": 7,
},
}
Render Order:
The render_order property and set_render_order() method control the z-ordering
of objects during rendering:
"smaller_on_top"(default): Smaller objects render on top (original behavior). Sort by onscreen_area ascending, iterate from smallest to largest."larger_on_top": Larger objects render on top (natural depth). Sort by onscreen_area descending, iterate from largest to smallest.
The render order can be toggled via the View menu (Ctrl+R) at runtime. It affects both
the sort direction in __sort_objects() and the occlusion culling logic.
Bulk Selection:
As of v0.3.2, Scene supports multi-object selection:
Ctrl+Click: Add/remove objects from selection group
Left-click drag: Area selection (lasso) to select multiple objects
selectionis alist[MediaObject](was single object in earlier versions)Right-click selection remains single-object for context menu operations
Scene Coordinate System:
World Reference Frame
────────────────────────────────────────▶ x
│ Scene
│ @ ──────────────────────────────+───▶
│ │ ViewPort MediaObj │
│ │ (Screen View) *───────+──▶ │
│ │ │ & │ │
│ │ % +───────" │
│ │ │ │
│ │ ▼ │
│ │ │
│ +───────────────────────────────#
│ │
│ ▼
▼
y
Legend:
@ → Scene.origin() (world coordinates)
% → Scene.centre (screen coordinates)
# → Scene.viewport_size (screen dimensions)
* → MediaObject.topleft (screen coordinates)
" → MediaObject.bottomright
& → MediaObject.centre
Scene Origin and Centre:
The scene origin (ox, oy) is the world-space coordinate of the top-left corner
of the viewport:
# Scene centre in screen coordinates
centre_x = origin[0] + (viewport_size[0] / 2) * (2 ** zoomlevel)
centre_y = origin[1] + (viewport_size[1] / 2) * (2 ** zoomlevel)
Scene Lifecycle:
Scene manages its own cleanup through:
__del__: Stops autosave timer and shuts down parallel renderer threadsshutdown_threads(): Public method for explicit cleanup at application shutdownAutosave manager handles timer lifecycle separately
Parallel renderer stops its thread pool gracefully
Key Methods:
add(mediaobject): Add object to sceneremove(mediaobject): Remove object and purge unused tilesget(pos): Get topmost (smallest) object at screen positionrender(painter, draft): Render all objects with occlusion culling (smaller on top)step(t): Update physics for scene and all objectssave(filename): Save scene to .pzs fileload_scene(filename): Load scene from .pzs file (static method)set_render_order(order): Set render order (“smaller_on_top” or “larger_on_top”)shutdown_threads(): Cleanup parallel renderer threadscopy_selected(): Copy selected objects via clipboard managerpaste(): Paste objects via clipboard manager
Scene Sub-Managers¶
SceneParallelRenderer¶
SceneParallelRenderer handles parallel rendering of StringMediaObject
text layouts in background threads, improving performance for scenes with many
text objects. Located at ../zooui/parallel.
Key Features:
Thread Pool: Concurrent text layout calculation using
ParallelLayoutCalculatorPriority Batching: Uses
PriorityBatcherto prioritize objects closest to the viewport centerStatistics: Tracks text object count, batch count, cache hit rate, and timing
Configuration: Controlled via
parallel_rendering.enabledconfig key
Rendering Flow:
1. PriorityBatcher sorts text objects by viewport distance
2. Objects are grouped into priority batches (HIGH through BACKGROUND)
3. ParallelLayoutCalculator computes layouts in thread pool
4. Pre-calculated TextLayoutData is cached for faster rendering
5. Scene renders using cached layouts, skipping expensive layout calculation
SceneAutosaveManager¶
SceneAutosaveManager provides timer-based automatic backup creation for
scene files. Located at ../zooui/autosave.
Key Features:
Timer-based: Configurable interval (default: 5 minutes)
Per-scene directories:
~/.local/share/zooui/backups/{filename}_{4char_hash}/Rotation: Keeps last N backups, deletes oldest automatically (default: 20)
Expiration: Inactive scene directories expire after N days (default: 7)
Enabled by default: Autosave starts automatically after scene load
CLI integration:
--autosave-interval,--autosave-max-backups,--backup-expire-days,--no-autosave
SceneClipboardManager¶
SceneClipboardManager provides copy/paste operations for scene objects.
Located at ../zooui/clipboard.
Key Features:
Copy: Serializes selected SVG objects to internal clipboard list
Paste: Deserializes and re-creates objects with optional grid-aligned offset
Grid Alignment: Pasted objects are positioned relative to the current selection with configurable offset
Supports: SVGMediaObject serialization with all state fields
MediaObject Types¶
TiledMediaObject¶
TiledMediaObject handles large images by breaking them into a pyramid of tiles.
Features:
Format Support: PDF, PPM, and standard images (JPG, PNG, GIF, TIFF)
Converters: Automatic conversion via PDFConverter or VipsConverter, with process-based parallel conversion via ../zooui/converterrunner
Tile Pyramid: Multi-resolution tile structure for efficient zooming
Progressive Loading: Shows placeholder with progress percentage
Caching: Intelligent tile block caching with age-based invalidation
Loading Process:
Check if media is already tiled (via TileManager)
If not tiled, select appropriate converter based on file extension
Convert to PPM format (if needed)
Run PPMTiler to create tile pyramid
Store tiles on disk via TileStore
Load tiles on demand via TileManager
Rendering Strategy:
1. Calculate required tile level from zoom
2. Determine visible tile range from viewport
3. Request tileblock (group of adjacent tiles)
4. Check cache for tileblock
5. If cache miss or stale, render new tileblock:
- Request individual tiles from TileManager
- Composite tiles into tileblock
- Cache tileblock for reuse
6. Scale tileblock to match zoom level
7. Draw scaled tileblock to screen
Key Attributes:
__autofit: Whether to fit media to placeholder area__loaded: Flag indicating media is ready__converter: PDFConverter or VipsConverter instance__tiler: PPMTiler instance__tileblock: Cached composite of adjacent tiles__maxtilelevel: Maximum tile pyramid level__tilesize: Size of individual tiles in pixels
StringMediaObject¶
StringMediaObject renders text strings with automatic font scaling and
dual-mode hybrid rendering for optimal performance.
Features:
Multi-line Support: Automatically handles newline characters
Color Support: Parse color from media_id (e.g., ‘string:ff0000:Hello’)
Font Scaling: Font size scales with zoom level
Transparent Background: Doesn’t obscure objects behind it
Hybrid Rendering: Dual-mode rendering for performance (v0.3.0+)
Media ID Format:
string:RRGGBB:text content
Examples:
string:ff0000:Hello World → Red text
string:00ff00:Line 1\nLine 2 → Green multi-line text
string:0000ff:Blue Text → Blue text
Hybrid Rendering:
StringMediaObject uses two rendering modes to balance quality and performance:
Moving Mode: When the scene is zooming or panning (
scene.vzmovingis True), text is rendered directly viaQPainter.drawText()for smooth animation with minimal overhead.Static Mode: When the scene is still, text is rendered to a cached
QImageonce, then blitted to screen on subsequent frames. This provides higher quality anti-aliased output.
Cache Management:
Cache validity conditions:
- Cached image exists AND scale hasn't changed beyond 1% threshold
- Mode hasn't switched (moving ↔ static)
- Text content hash hasn't changed
Cache invalidation triggers:
- Object starts moving (vzmoving becomes True)
- Scale changes significantly
- Explicit call to invalidate_cache() or clear_caches()
Cache Methods:
__render_text_to_image(): Renders text to QImage for static mode__is_cache_valid(): Checks if cached image can be reused__compute_text_hash(): Hashes text content for change detectioninvalidate_cache(): Marks cache as staleclear_caches(): Force-clears all cached images
Rendering:
Base font size: 24pt at 100% scale
Only renders when size is between 2.3% and 77% of viewport
Uses
TextLayoutDatafor pre-calculated font metrics and bounding rectsCreates QRectF for each line in multi-line text
Parallel Layout Calculation:
For scenes with many StringMediaObjects, ParallelLayoutCalculator
computes text layouts in a thread pool. Located at
../zooui/parallellayout. Key features:
Thread-safe design — no Qt objects constructed on worker threads
Uses
TextLayoutDatadataclass for pre-calculated layout resultsIntegrated with
SceneParallelRendererandPriorityBatcher
SVGMediaObject¶
SVGMediaObject renders scalable vector graphics with extensive
caching, modification tracking, and utility support.
Features:
Vector Rendering: Uses QSvgRenderer for crisp graphics at any zoom
Transparent Background: SVG transparency is preserved
Scalable: No quality loss when zooming
Content Caching: In-memory SVG content cache for fast re-rendering
Modification Tracking: Tracks changes from picker, clipboard, and shape utilities
Serialization: Full state serialization via
to_dict()/from_dict()SVG Cache Integration: Disk-backed cache with content-hash addressing
Shape Utilities: 5 shape generators for interactive SVG creation
SVG Cache Integration:
SVGs can be loaded from a file path or from the disk-backed SVGCache
(../zooui/svgcache) using a content-hash based media_id with
an svg_ prefix:
media_id formats:
"path/to/file.svg" → File-loaded SVG
"svg_a1b2c3d4..." → Cache-loaded SVG (content hash)
The __get_svg_load_path() method resolves both formats, fetching content
from SVGCache when needed.
Content Caching:
__cached_svg_content: In-memory byte buffer of SVG contentget_svg_content(): Returns content from cache or reads from fileset_svg_content(content): Sets embedded SVG content (e.g., from clipboard)Reduces disk I/O for frequently rendered SVGs
Modification Tracking:
__is_modified: Boolean flag set when SVG content changesmark_as_modified(): Called by picker, clipboard, and shape utilitiesoriginal_file_path: Property that returns the original file path (orNonefor cache-loaded SVGs)
Serialization:
# to_dict() returns all SVG state:
{
"class_name": "SVGMediaObject",
"media_id": "svg_a1b2c3...",
"zoomlevel": 0.0,
"x": 100.0, "y": 200.0,
"width": 64.0, "height": 64.0,
"transparent": True,
"is_modified": False,
"original_file_path": None,
}
Custom Size Visibility:
SVGMediaObject uses SVG-specific thresholds for visibility testing:
Minimum dimension >
viewport_min / 55Maximum dimension <
viewport_max / 0.5
Embedded SVGs:
SVGs created interactively (via shape utilities or picker) may have their
content embedded directly rather than loaded from disk. A warning is emitted
if the embedded content exceeds MAX_EMBEDDED_SVG_SIZE_BYTES (1 MB).
Rendering:
Loads SVG file or cached content
Creates QSvgRenderer for rendering
Stores default width and height from SVG
Renders to QRectF scaled by current zoom level
Only renders when size is between 2.3% and 77% of viewport
MediaObject Utilities¶
SVG Shape Utilities¶
Five utility modules in mediaobjectsutils/svg/utils/ provide shape detection
and elongation for interactive SVG creation. Each utility parses SVG content
using xml.etree.ElementTree with proper SVG namespace handling.
Utility |
Shape |
File |
|---|---|---|
|
Arrows (directional connectors) |
|
|
Circles and ellipses |
|
|
Rectangles and squares |
|
|
Stick figures (line-based) |
|
|
Triangles of various orientations |
|
All shape utilities interface with SVGCache for persistent storage of created and modified shapes.
Shape Detection:
Each utility detects whether a given SVG content matches its target shape using XML element inspection. When a shape is detected, the utility can:
Identify key geometric elements (e.g., circle radius, rectangle dimensions)
Apply elongation transformations (e.g., stretch an arrow along its axis)
Generate modified SVG content
Store the result in SVGCache
See Also:
../zooui/svgarrowutils
../zooui/svgcircleutils
../zooui/svgsquareutils
../zooui/svgtriangleutils
../zooui/svgcache
SVGCache¶
SVGCache provides a disk-backed content-addressable cache for SVG data.
Located at ../zooui/svgcache.
Key Features:
Content Hashing: SVGs are stored by content hash (
svg_prefix)Flat Directory: Single-level storage under cache root
Singleton: Accessed via
get_svg_cache()Human-readable names:
get_human_readable_name()for displayCRUD operations:
store_svg(),get_svg_content(),remove_svg()
Usage Pattern:
from zooui.objects.mediaobjects.mediaobjectsutils.svg.svgcache.svgcache import get_svg_cache
cache = get_svg_cache(base_dir)
# Store SVG content
cache_hash = cache.store_svg(svg_bytes)
# Retrieve SVG content
content = cache.get_svg_content(cache_hash)
# Remove SVG
cache.remove_svg(cache_hash)
TextLayoutData¶
TextLayoutData is a dataclass storing pre-calculated text layout
information for StringMediaObject rendering. Located at
../zooui/textlayout.
Key Fields:
Font metrics (QFont-compatible sizing data)
Bounding rectangles for each text line
Text alignment options (
LEFT,CENTER)Layout options (word wrap, line spacing)
Thread Safety:
TextLayoutData is designed to be thread-safe — it contains no Qt objects,
making it safe to compute in background threads via ParallelLayoutCalculator
and consume on the main rendering thread.
ParallelLayoutCalculator¶
ParallelLayoutCalculator manages parallel text layout calculation
using thread pools. Located at ../zooui/parallellayout.
Key Features:
Thread Pool:
concurrent.futures.ThreadPoolExecutorfor concurrent layout jobsQueue-based: Incoming layout requests are queued and dispatched
Thread Safety: No Qt objects constructed on worker threads
Statistics: Tracks calculation status (PENDING, IN_PROGRESS, COMPLETED, FAILED)
Integration: Used by
SceneParallelRendererandPriorityBatcher
Coordinate Systems and Reference Frames¶
Understanding the coordinate transformations is crucial for working with ZooUI.
Reference Frame Hierarchy¶
┌─────────────────────────────────────────────────────────────┐
│ World/Absolute Reference Frame │
│ • Origin: (0, 0) │
│ • Used for: Scene positioning │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Scene Reference Frame │
│ • Origin: Scene.origin (world coords) │
│ • Scaling: 2 ** Scene.zoomlevel │
│ • Used for: MediaObject positioning │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ MediaObject Reference Frame │
│ • Origin: MediaObject.pos (scene coords) │
│ • Scaling: 2 ** MediaObject.zoomlevel │
│ • Used for: Internal object coordinates (e.g., centre) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Screen/Viewport Reference Frame │
│ • Origin: Top-left corner of window │
│ • Units: Pixels │
│ • Used for: Rendering and mouse input │
└─────────────────────────────────────────────────────────────┘
Zoom Mechanics¶
Both Scene and MediaObject can be zoomed independently. Zoom is always centered on a specific point to maintain visual continuity.
Scene Zoom:
When zooming the scene by amount a with centre point P:
P = origin + C * (2 ** zoomlevel)
Solving for new origin:
origin' = P - (P - origin) * (2 ** a)
zoomlevel' = zoomlevel + a
MediaObject Zoom:
When zooming a media object by amount a:
C_s = pos + C_i * (2 ** zoomlevel_i)
Solving for new position:
pos' = C_s - (C_s - pos) * (2 ** a)
zoomlevel' = zoomlevel + a
This ensures the centre point C maintains its screen position during zoom.
ZoomManager:
ZoomManager enforces zoom level limits to prevent crashes at extreme
zoom levels. Located at ../zooui/zoommanager.
# Default limits
min_zoomlevel = -10 # Minimum allowed zoom
max_zoomlevel = 12 # Maximum allowed zoom
clamp_enabled = True # Enable/disable clamping
# Methods
clamp_zoomlevel(level) # Clamp a single level
is_valid_zoomlevel(level) # Check if level is within bounds
validate_and_clamp_zoomlevels(levels) # Bulk clamp
Physics System¶
Motion and Damping¶
All objects inherit physics simulation from PhysicalObject. The system
uses exponential damping for smooth, natural motion.
Damping Formula:
velocity(t) = initial_velocity * (damping_factor ** -t)
# Default damping_factor = 512
# Velocity reduced by factor of 512 every second
# Velocity < 0.1 is clamped to 0
Displacement Calculation:
displacement(t) = (u / log(d)) * (1 - d**-t)
where:
u = initial velocity
d = damping_factor
t = time in seconds
Aim Method:
The aim() method calculates the initial velocity needed to achieve a desired
displacement:
# Aim for displacement s that stops naturally
obj.aim('x', s) # s = u / log(d)
# Aim for displacement s at specific time t
obj.aim('x', s, t) # s = (u/log(d)) * (1 - d**-t)
Velocity Components¶
Each object has three velocity components:
vx: Horizontal velocity (screen pixels per second at current zoom)vy: Vertical velocity (screen pixels per second at current zoom)vz: Zoom velocity (zoom levels per second)
Step Method:
The step(t) method advances physics simulation by time t:
def step(self, t):
if self.vx or self.vy:
self.move(
self.__displacement(t, self.vx),
self.__displacement(t, self.vy))
self.vx = self.__damp(self.vx, t)
self.vy = self.__damp(self.vy, t)
if self.vz:
self.zoom(self.__displacement(t, self.vz))
self.vz = self.__damp(self.vz, t)
Rendering Pipeline¶
Scene Rendering¶
The Scene.render() method orchestrates rendering of all objects, integrating
incremental and parallel rendering strategies.
Render Order:
The render order (smaller_on_top or larger_on_top) determines how
objects are sorted:
smaller_on_top (default): Objects sorted by
onscreen_areaascending. Smaller objects render last, appearing on top.larger_on_top: Objects sorted by
onscreen_areadescending. Larger objects render last, appearing on top.
The render order also affects occlusion culling: with smaller_on_top,
a small object that fills the viewport occludes larger objects behind it;
with larger_on_top, large objects occlude smaller ones.
Rendering Process:
1. Sort objects by onscreen_area (direction depends on render order)
2. Parallel Rendering (if enabled):
- PriorityBatcher sorts text objects by viewport distance
- Objects grouped into priority batches (HIGH → BACKGROUND)
- ParallelLayoutCalculator computes text layouts in thread pool
- Cached layouts avoid redundant calculation on subsequent frames
3. Occlusion Culling:
- Iterate objects in sort order
- If object fills entire viewport, mark later objects as hidden
- Render order determines which objects occlude which
4. Assign Render Modes:
- Hidden objects: RenderMode.Invisible
- Draft mode: RenderMode.Draft (fast rendering)
- Otherwise: RenderMode.HighQuality
5. Render Each Object (in reverse sort order):
- Reverse iteration so correct z-order is maintained
- Topmost objects are painted last
- Call object.render(painter, mode)
- Catch LoadError exceptions
- Remove objects with errors
6. Draw Selection Borders:
- Green border for left-click selection
- Blue border for right-click selection
7. Return error list
Z-Order: With smaller_on_top (default), smaller objects are always
rendered on top of larger objects. When objects overlap, the smaller one
will be visible and selectable. With larger_on_top, the reverse applies.
Render Modes:
class RenderMode:
Invisible = 0 # Don't render
Draft = 1 # Fast, lower quality
HighQuality = 2 # Slow, higher quality
PriorityBatcher¶
PriorityBatcher provides viewport-aware priority batching for text
objects in parallel rendering. Located at ../zooui/prioritybatcher.
Priority Levels:
class BatchPriority(Enum):
HIGH = 0 # Objects in viewport center
MEDIUM = 1 # Objects near viewport
LOW = 2 # Objects far from viewport
BACKGROUND = 3 # Objects outside viewport (pre-calculation)
How It Works:
Receives current viewport center and dimensions
Calculates each text object’s distance from viewport center
Assigns priority level based on distance thresholds
Uses heapq to sort objects by priority
Returns groups of objects in priority batches
Integrated with
SceneParallelRendererandParallelLayoutCalculator
Priority batching ensures objects closest to the user’s focus are rendered first, improving perceived performance.
Object Rendering¶
Each MediaObject subclass implements its own render() method:
TiledMediaObject: - Calculates required tile level from zoom - Determines visible tile range - Requests and composites tiles - Scales to match zoom - Draws to painter
StringMediaObject: - Checks if text is visible size - Moving mode: creates QFont with scaled point size, measures text with QFontMetrics, creates QRectF for each line, draws with QPainter.drawText() - Static mode: checks cache validity, serves pre-rendered QImage if valid, or renders new image and caches it
SVGMediaObject: - Checks if SVG is visible size - Gets content from cache or file - Creates QRectF at scaled dimensions - Renders with QSvgRenderer.render()
Thread Safety¶
The Scene uses RLock for thread-safe access to the object list:
with self.__objects_lock:
# Thread-safe operations on self.__objects
self.__sort_objects()
for mediaobject in self.__objects:
# ... render logic ...
Parallel rendering operates in background threads with explicit thread-safety guarantees:
TextLayoutDatacontains no Qt objects — safe for cross-thread useParallelLayoutCalculatornever creates QFont or QFontMetrics on worker threadsAll Qt operations remain on the main thread
Thread pool is shut down cleanly via
Scene.shutdown_threads()
Scene Persistence¶
Scenes can be saved to and loaded from .pzs files, with automatic backup protection via the autosave subsystem.
Save Format¶
Line 1: scene.zoomlevel scene.origin[0] scene.origin[1]
Line 2+: class_name media_id zoomlevel pos[0] pos[1] [extra_fields]
Example:
0.0 640.0 360.0
TiledMediaObject image.jpg 0.5 100.0 200.0
StringMediaObject string:ff0000:Hello 0.0 300.0 400.0
SVGMediaObject svg_a1b2c3d4 0.0 500.0 300.0 width=64 height=64 is_modified=0 original_file_path=None
Extended Fields for SVGMediaObject:
width/height: SVG default dimensionsis_modified: Whether content was modified since original load (0 or 1)original_file_path: Path to original file, orNonefor cache SVGstransparent: Boolean flag for background transparency
Saving Process:
Store current viewport size
Temporarily set viewport to standard size (1280x720)
Write scene zoom and origin
Sort and write all objects (with serialized state via
to_dict())Trigger autosave backup if enabled
Restore actual viewport size
Loading Process:
Create new Scene
Read scene zoom and origin from first line
For each subsequent line: - Parse object type and parameters - Create appropriate MediaObject subclass via
from_dict()- Set zoom level, position, and extra state - Add to sceneStart autosave timer if enabled
Return loaded scene
Autosave Subsystem¶
The autosave subsystem provides automatic backup creation for scene files to prevent data loss. See ../zooui/autosave and ../zooui/backupmanager.
Configuration:
{
"autosave": {
"enabled": true,
"interval": 300,
"max_backups": 20,
"expire_days": 7
}
}
Storage Layout:
~/.local/share/zooui/backups/
├── scene1_a1b2/ ← Per-scene directory (filename + 4-char hash)
│ ├── 26_05_09_14_30_c3d4.pzs
│ ├── 26_05_09_14_35_e5f6.pzs
│ └── ...
└── scene2_g7h8/
├── 26_05_09_14_31_i9j0.pzs
└── ...
Key Behaviors:
Each scene file gets its own backup directory
Backup filenames are timestamp-first for chronological sorting:
yy_mm_dd_hh_mm_filename_hash.pzsOldest backups are deleted when
max_backupslimit is exceededScene directories expire after
expire_daysof inactivityAutosave can be disabled via
--no-autosaveCLI flag
BackupManager:
BackupManager handles the filesystem operations for the autosave
system. Located at ../zooui/backupmanager. Key methods:
create_backup(scene, filepath): Creates timestamped backuprotate_backups(scene_dir, max_backups): Deletes oldest backupscleanup_expired(base_dir, expire_days): Removes expired scene directories
SVG Cache Persistence¶
SVGs created or modified via shape utilities, picker, or clipboard may be
persisted to SVGCache rather than saved as standalone files. These SVGs are
referenced in .pzs files by their cache hash (svg_a1b2c3...) instead
of a file path. This enables:
Deduplication: Identical SVGs share the same cache entry
Portability: Cache-based SVGs don’t depend on original file locations
Modification tracking:
is_modifiedflag indicates deviation from original
Usage Examples¶
Creating and Populating a Scene¶
from zooui.objects.scene.scene import Scene, load_scene
from zooui.objects.mediaobjects.tiledmediaobject import TiledMediaObject
from zooui.objects.mediaobjects.stringmediaobject import StringMediaObject
from zooui.objects.mediaobjects.svgmediaobject import SVGMediaObject
# Create new scene with configuration
config = {
"render": {"order": "smaller_on_top"},
"parallel_rendering": {"enabled": True},
"autosave": {"enabled": True, "interval": 300, "max_backups": 20},
}
scene = Scene(config=config)
scene.viewport_size = (1920, 1080)
# Add a tiled image
image = TiledMediaObject('path/to/large_image.jpg', scene)
image.pos = (0, 0)
image.zoomlevel = 0
scene.add(image)
# Add text
text = StringMediaObject('string:ff0000:Welcome to ZooUI!', scene)
text.pos = (100, 100)
text.zoomlevel = 0
scene.add(text)
# Add SVG from file
svg = SVGMediaObject('path/to/icon.svg', scene)
svg.pos = (500, 300)
svg.zoomlevel = 0
scene.add(svg)
# Save scene (triggers autosave backup if enabled)
scene.save('my_scene.pzs')
# Load scene later
scene = load_scene('my_scene.pzs')
# Autosave timer starts automatically after load
Working with Physics¶
# Set object velocity directly
obj.vx = 100.0 # 100 pixels per second
obj.vy = 50.0
obj.vz = 0.5 # 0.5 zoom levels per second
# Use aim() for smooth motion to target
obj.aim('x', 500) # Move 500 pixels right, then stop
obj.aim('y', -200, 2.0) # Move 200 pixels up in 2 seconds
# Update physics (typically called each frame)
dt = 1.0 / 60.0 # 60 FPS
scene.step(dt)
# Check if anything is moving
if scene.moving:
print("Scene or objects still in motion")
Managing Render Order¶
# Query current render order
print(scene.render_order) # "smaller_on_top" (default)
# Toggle to larger-on-top (natural depth ordering)
scene.set_render_order("larger_on_top")
# Toggle back (also accessible via View menu Ctrl+R)
scene.set_render_order("smaller_on_top")
Bulk Selection¶
# Ctrl+click to select multiple objects
# Drag to area-select
# selection is now a list
for obj in scene.selection:
print(f"Selected: {obj.media_id}")
Clipboard Operations¶
# Copy selected objects
scene.copy_selected()
# Paste with offset (from last selected position)
scene.paste()
# Paste at specific position
scene.paste(offset_x=100, offset_y=100)
Working with SVGCache¶
from zooui.objects.mediaobjects.mediaobjectsutils.svg.svgcache.svgcache import (
get_svg_cache,
)
cache = get_svg_cache("/path/to/cache")
# Store SVG content
with open("my_shape.svg", "r") as f:
cache_hash = cache.store_svg(f.read())
# Create SVGMediaObject from cache
svg = SVGMediaObject(cache_hash, scene)
scene.add(svg)
# Get human-readable name for display
name = cache.get_human_readable_name(cache_hash)
# Remove cached SVG
cache.remove_svg(cache_hash)
Coordinate Transformations¶
# Get screen position of object
screen_x, screen_y = obj.topleft
# Get topmost (smallest) object at mouse position
clicked_obj = scene.get((mouse_x, mouse_y))
# Fit object to bounding box
obj.fit((100, 100, 500, 500)) # x1, y1, x2, y2
# Center object on screen
obj.centre = (viewport_width / 2, viewport_height / 2)
# Get current scale
scale = obj.scale # 2 ** (scene.zoom + obj.zoom)
Custom Rendering¶
from PySide6.QtGui import QPainter
# Create painter (typically from QWidget.paintEvent)
painter = QPainter(widget)
# Render scene
errors = scene.render(painter, draft=False)
# Handle errors
for obj in errors:
print(f"Failed to render: {obj.media_id}")
# Clean up when done
scene.shutdown_threads()
Configuring Autosave¶
# Enable autosave with custom settings
from zooui.objects.scene.sceneutils.autosave import SceneAutosaveManager
autosave_config = {
"enabled": True,
"interval": 600, # 10 minutes
"max_backups": 50, # Keep up to 50 backups
"expire_days": 14, # Clean up after 2 weeks
}
scene.autosave_manager.set_autosave_config(autosave_config)
# Disable autosave
scene.autosave_manager.disable_autosave()
# Re-enable with default settings
scene.autosave_manager.enable_autosave()
# Check status
if scene.autosave_manager.is_autosave_enabled():
interval = scene.autosave_manager.get_autosave_interval()
print(f"Autosave active, interval: {interval}s")
API Reference¶
Key Classes¶
zooui.objects.physicalobject.PhysicalObject- Base physics simulationzooui.objects.mediaobjects.mediaobject.MediaObject- Abstract media objectzooui.objects.mediaobjects.tiledmediaobject.TiledMediaObject- Tiled imageszooui.objects.mediaobjects.stringmediaobject.StringMediaObject- Text objectszooui.objects.mediaobjects.svgmediaobject.SVGMediaObject- Vector graphicszooui.objects.scene.scene.Scene- Scene container and coordinator
Scene Sub-Managers¶
zooui.objects.scene.sceneutils.parallel.SceneParallelRenderer- Parallel renderingzooui.objects.scene.sceneutils.autosave.SceneAutosaveManager- Autosave orchestrationzooui.objects.scene.sceneutils.clipboard.SceneClipboardManager- Copy/paste operations
Rendering Utilities¶
zooui.objects.scene.sceneutils.prioritybatcher.PriorityBatcher- Priority-based batchingzooui.objects.mediaobjects.mediaobjectsutils.string.parallellayout.ParallelLayoutCalculator- Thread-pool text layoutzooui.objects.mediaobjects.mediaobjectsutils.string.textlayout.TextLayoutData- Pre-calculated layouts
Zoom and Backup¶
zooui.objects.objectsutils.zoom.zoommanager.ZoomManager- Zoom level clampingzooui.backup.backupmanager.BackupManager- Backup storage managementzooui.objects.mediaobjects.mediaobjectsutils.svg.svgcache.svgcache.SVGCache- SVG disk cache
For detailed API documentation, see the individual class documentation pages.
See Also¶
Tiling System - Details on tile pyramid and caching
zooui/objects/mediaobjects/tiledmediaobject - TiledMediaObject implementation details
Project structure - Overall project architecture
../zooui/autosave - SceneAutosaveManager API
../zooui/clipboard - SceneClipboardManager API
../zooui/parallel - SceneParallelRenderer API
../zooui/prioritybatcher - PriorityBatcher API
../zooui/parallellayout - ParallelLayoutCalculator API
../zooui/textlayout - TextLayoutData API
../zooui/zoommanager - ZoomManager API
../zooui/backupmanager - BackupManager API
../zooui/svgcache - SVGCache API
../zooui/svgarrowutils - Arrow shape utility
../zooui/svgcircleutils - Circle shape utility
../zooui/svgsquareutils - Square shape utility
../zooui/svgtriangleutils - Triangle shape utility
../zooui/converterrunner - Process-based parallel conversion