Source code for camtasia.builders.tile_layout

"""Tile layout builder for grid-based image arrangements."""
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from camtasia.project import Project
    from camtasia.timeline.clips.base import BaseClip


[docs] class TileLayout: """Place images in a grid layout on the timeline.""" def __init__(self, project: Project, track_prefix: str = 'Tile') -> None: self._project = project self._track_prefix = track_prefix self._tiles: list[BaseClip] = []
[docs] def add_grid( self, image_paths: list[Path | str], start_seconds: float, end_seconds: float, grid: tuple[int, int] = (2, 2), stagger_seconds: float = 0.0, scale: float = 1.0, fade_in_seconds: float = 0.5, ) -> list[BaseClip]: """Place images in a grid layout. Args: image_paths: Images to place in the grid. start_seconds: When the first tile appears. end_seconds: When all tiles disappear. grid: (rows, cols) grid dimensions. stagger_seconds: Delay between each tile appearing. scale: Scale factor for each tile. fade_in_seconds: Fade-in duration for each tile. """ rows, cols = grid canvas_w = self._project.width canvas_h = self._project.height cell_w = canvas_w / cols cell_h = canvas_h / rows placed: list[BaseClip] = [] for idx, image_path in enumerate(image_paths): row = idx // cols col = idx % cols if row >= rows: break # grid full media = self._project.import_media(Path(image_path)) track_name = f'{self._track_prefix}-{idx}' track = self._project.timeline.get_or_create_track(track_name) tile_start = start_seconds + (idx * stagger_seconds) tile_duration = end_seconds - tile_start if tile_duration <= 0: break # remaining tiles would also be negative clip = track.add_image( media.id, start_seconds=tile_start, duration_seconds=tile_duration, ) # Position in grid offset_x = (col - (cols - 1) / 2) * cell_w offset_y = (row - (rows - 1) / 2) * cell_h clip.translation = (offset_x, offset_y) clip.scale = (scale, scale) if fade_in_seconds > 0: clip.fade_in(fade_in_seconds) placed.append(clip) self._tiles.extend(placed) return placed
@property def tiles(self) -> list[BaseClip]: """All placed tile clips.""" return list(self._tiles)