ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Grid Layout System

⏹️ Arranging the clients nicely

⏹️ Arranging the clients nicely

Square Packing

The grid layout system provides dynamic multi-frame layout for ascii-chat video display. It arranges multiple ASCII frames (client video feeds) into an optimal grid pattern that maximizes terminal space usage while maintaining readability.

Overview

The grid layout system dynamically adapts to:

  • Terminal dimensions (width × height in characters)
  • Number of video sources (clients)
  • Character aspect ratio (typically ~2:1 height-to-width)

The system uses a multi-factor scoring algorithm to select the optimal grid configuration, balancing:

  • Cell aspect ratio (preferring ~2:1 for ASCII characters)
  • Space utilization (minimizing empty cells)
  • Cell size (ensuring minimum readability thresholds)
  • Terminal shape matching (matching grid to terminal aspect ratio)

Architecture

Core Components

The grid layout system consists of:

Frame Source Structure (ascii_frame_source_t):

typedef struct {
const char *frame_data; // ASCII frame string (not owned)
size_t frame_size; // Frame data size in bytes
} ascii_frame_source_t;

This structure holds a single ASCII frame's data and size, used as input to the grid creation function.

Grid Creation Function (ascii_create_grid()):

  • Takes array of frame sources
  • Calculates optimal grid dimensions
  • Combines frames into single grid layout
  • Returns allocated ASCII string with grid output

Grid Selection Algorithm

The algorithm tries all possible grid configurations and scores each:

  1. Try Each Grid Configuration:
    • Test 1×N, 2×N, 3×N, ... up to N×1
    • For each configuration, calculate cell dimensions
  2. Filter Invalid Configurations:
    • Skip if cells too small (< 10×3 characters)
    • Skip if too many empty cells (> 50% waste)
  3. Score Each Valid Configuration:
    • Aspect Ratio Score: Prefers ~2:1 cell aspect ratio (optimal for ASCII)
    • Utilization Score: Prefers fewer empty cells
    • Size Score: Prefers larger cells when possible
    • Shape Match Score: Prefers grids that match terminal shape
  4. Select Best Configuration:
    • Choose configuration with highest total score
    • Calculate cell dimensions for selected grid

Scoring Algorithm

The multi-factor scoring algorithm uses weighted combination:

For 2 clients:

  • Aspect ratio: 90% weight (optimal aspect most important)
  • Utilization: 10% weight (2 clients = 100% utilization anyway)

For 3+ clients:

  • Aspect ratio: 70% weight
  • Utilization: 30% weight
  • Bonus: +0.05 for square grids (e.g., 2×2, 3×3)

This ensures:

  • 2 clients adapt to terminal shape (horizontal on wide, vertical on tall)
  • 3+ clients balance aspect ratio with space usage
  • Square grids slightly preferred (cleaner layout)

API Reference

Functions

ascii_create_grid()

Creates a grid layout from multiple ASCII frame sources with | and _ separators.

Parameters: sources Array of ASCII frame sources to combine source_count Number of sources in the array width Target width of the output grid height Target height of the output grid out_size Output parameter for the size of the returned buffer

Returns: A newly allocated, null-terminated string containing the grid layout, or NULL on error. Caller must free the returned buffer.

Example Usage
// Prepare frame sources
ascii_frame_source_t sources[4];
sources[0].frame_data = frame1_ascii;
sources[0].frame_size = strlen(frame1_ascii);
sources[1].frame_data = frame2_ascii;
sources[1].frame_size = strlen(frame2_ascii);
sources[2].frame_data = frame3_ascii;
sources[2].frame_size = strlen(frame3_ascii);
sources[3].frame_data = frame4_ascii;
sources[3].frame_size = strlen(frame4_ascii);
// Create grid layout (80 chars wide, 40 chars tall per frame)
size_t grid_size;
char *grid = ascii_create_grid(sources, 4, 80, 40, &grid_size);
if (grid) {
// Write grid to terminal
ascii_write(grid);
free(grid); // Must free allocated string
}
asciichat_error_t ascii_write(const char *frame)
Definition ascii.c:333
char * ascii_create_grid(ascii_frame_source_t *sources, int source_count, int width, int height, size_t *out_size)
Definition ascii.c:542

Data Structures

ascii_frame_source_t

Note
Frame data is not owned by the structure - it must remain valid during grid creation.

Layout Examples

2 Clients

Wide Terminal (120×40):

  • Layout: 2×1 (horizontal split)
  • Cells: 60×40 each
  • Optimal for wide terminals

Tall Terminal (40×60):

  • Layout: 1×2 (vertical split)
  • Cells: 40×30 each
  • Optimal for tall terminals

4 Clients

Standard Terminal (80×24):

  • Layout: 2×2 grid
  • Cells: 40×12 each
  • Balanced layout

Large Terminal (160×48):

  • Layout: 2×2 grid
  • Cells: 80×24 each
  • Larger cells for better quality

9 Clients

Standard Terminal (80×24):

  • Layout: 3×3 grid
  • Cells: 26×8 each
  • Maximum clients for standard terminal

Large Terminal (200×60):

  • Layout: 3×3 grid
  • Cells: 66×20 each
  • Larger cells for better quality

Implementation Details

Grid Calculation

The grid calculation process:

  1. Input Processing:
    • Receive array of frame sources
    • Receive target dimensions (width × height)
    • Count valid sources (non-NULL, non-empty frames)
  2. Edge Cases:
    • 0 sources: Return NULL
    • 1 source: Return single frame (no grid needed)
    • All sources invalid: Return NULL
  3. Grid Selection:
    • Try all configurations from 1×N to N×1
    • Calculate cell dimensions for each
    • Filter by minimum size (10×3)
    • Score each valid configuration
    • Select best configuration
  4. Frame Placement:
    • Allocate output buffer (width × height + newlines)
    • Initialize with spaces
    • For each source, place in grid cell
    • Handle frame truncation if too large
    • Add separators (| for vertical, - for horizontal)

Frame Placement

Each frame is placed in its grid cell:

  1. Calculate Cell Position:
    • Grid row: src_index / grid_cols
    • Grid column: src_index % grid_cols
    • Start row: grid_row * (cell_height + 1)
    • Start column: grid_col * (cell_width + 1)
  2. Copy Frame Data:
    • Parse source frame line by line
    • Copy each line to cell position
    • Truncate lines if too long for cell
    • Skip lines if too tall for cell
  3. Add Separators:
    • Vertical separators (|) between columns
    • Horizontal separators (-) between rows
    • Separators drawn after all frames placed

Memory Management

Memory allocation:

  • Output buffer: Allocated with malloc() (caller must free)
  • Buffer size: width × height + height + 1 (characters + newlines + null terminator)
  • Frame data: Not copied, pointers stored (sources must remain valid)
Note
The returned string must be freed by the caller using free().

Performance

Grid creation performance:

  • Grid calculation: O(N) where N = number of sources
  • Frame placement: O(W × H × N) where W×H = grid dimensions, N = sources
  • Memory allocation: O(W × H) for output buffer

Typical performance (Intel i7-10700K @ 3.8 GHz):

  • 4 clients, 80×40 grid: ~1-2 ms
  • 9 clients, 120×60 grid: ~3-5 ms
  • 16 clients, 200×80 grid: ~10-15 ms

Grid calculation is fast enough for real-time video display (60 FPS).

Constraints

Minimum requirements:

  • Cell width: Minimum 10 characters (readability threshold)
  • Cell height: Minimum 3 characters (readability threshold)
  • Terminal width: Must be >= 10 characters
  • Terminal height: Must be >= 3 characters

If terminals too small:

  • Grid creation returns single frame (first source)
  • No grid layout applied
  • Falls back to single-frame display

Use Cases

Multi-User Video Chat

Primary use case: Display multiple client video feeds simultaneously.

  • Server creates grid layout for all connected clients
  • Each client sees all other clients in optimal grid
  • Grid adapts automatically to terminal size
  • Supports dynamic client join/leave

Quality vs. Quantity Trade-off

The system balances:

  • More clients: Smaller cells, lower quality per client
  • Fewer clients: Larger cells, higher quality per client
  • Optimal layout: Maximum quality while showing all clients

The scoring algorithm automatically finds this balance.

Future Enhancements

Potential improvements:

  • Pagination: Support more clients than terminal capacity
  • Focus Mode: One large client + thumbnails for others
  • Dynamic Resizing: Adjust layout on terminal resize
  • Client Ordering: Prioritize active clients
  • Custom Layouts: User-configurable grid arrangements
See also
ascii.h
ascii_create_grid()
ascii_frame_source_t