charton 0.5.2

A high-performance, layered charting system for Rust, featuring a flexible data core and multi-backend rendering.
Documentation
# Chapter 12 · Layout

Layout in Charton is more than just positioning elements; it is a mathematical negotiation between the **shape of your data** and the **geometry of the coordinate system**. While many libraries rely on hard-coded logic to switch between grouped and single-column charts, Charton uses a unified strategy to handle all positioning.

## 12.1. The Row-Stub Layout Strategy

The **Row-Stub Layout Strategy** is the core engine behind how Charton decides the width, spacing, and offset of marks (bars, sectors, or boxes). Instead of asking "Is this a grouped chart?", the renderer asks: "**How many rows of data exist for this specific coordinate?**"

### 12.1.1. The Philosophy: Data Shapes Geometry

In the Row-Stub model, the "physical" presence of a data row in the transformed DataFrame acts as a "stub" or a placeholder in the visual layout.
* **Single Row per Category**: If a category (e.g., "Category A" on the X-axis) contains exactly one row, the mark occupies the **full available span**.
* **Multiple Rows per Category**: If a category contains $N$ rows, the layout engine automatically carves the available space into $N$ sub-slots.

### 12.1.2. The Mechanism: Cartesian Normalization

To ensure consistent layouts, Charton performs a **Cartesian Product** during the data transformation phase. If you encode both `x` and `color`, Charton ensures that every unique value of `x` has a row for every unique value of `color`.

1. **Gap Filling**: If "Category A" has data for "Male" but not "Female," Charton inserts a "Female" row with a value of `0`.
2. **Stable Count**: This ensures that every X-axis slot has the exact same number of rows ($N$).
3. **Implicit Positioning**: he renderer simply iterates through these $N$ rows. The $i$-th row is automatically placed at the $i$-th sub-slot.

### 12.1.3. Dimension Deduplication: Intent Recognition

The layout engine must first distinguish between **visual aesthetics** (just adding color) and **structural dimensions** (splitting data into groups). Charton achieves this through **Automatic Column Deduplication** during the preprocessing phase.

Before the Row-Stub engine calculates $N$ (the number of rows per slot), it performs the following check:

1. **The Dimension Set**: Charton collects all fields used in `x`, `color`, `size`, and `shape`.
2. **Deduplication**: If a field is used in both a positional channel (like `x`) and a styling channel (like `color`), it is only counted **once** as a grouping dimension.
3. **Intent Recognition**:
- `x("type"), color("type")`: After deduplication, there is only **one** grouping dimension (`type`). The engine recognizes this as a **Self-Mapping** intent—use colors to distinguish categories, but keep them in a single, full-width slot.
- `x("type"), color("gender")`: There are **two** distinct dimensions. The engine recognizes this as a **Grouping** intent—sub-divide each `type` slot by `gender`.

Without this deduplication step, a Rose Chart would mistakenly try to "dodge" (place side-by-side) the same category against itself, leading to overlapping marks or unnecessarily thin sectors.

### 12.1.4. The Mechanism: Cartesian Normalization

To ensure consistent layouts across all categories, Charton performs a **Cartesian Product** based on the *deduplicated* dimension set.

1. **Grid Creation**: If `x` has 5 unique values and `color` (a different field) has 2, Charton creates a "Layout Grid" of $5 \times 2 = 10$ rows.
2. **Gap Filling**: If "Category A" has data for "Male" but not "Female," Charton joins the grid with the raw data and inserts a "Female" row with a value of `0`.
3. **Physical Alignment**: This ensures that every X-axis slot has the exact same number of physical row stubs ($N=2$).
4. **Predictable Offsets**: The renderer simply iterates through these $N$ rows. The $i$-th row is always placed at the $i$-th sub-slot, ensuring that "Male" is always the left bar and "Female" is always the right bar, even if the data for one is missing.

### 12.1.5. Mathematical Resolution

The physical width of a mark in normalized space is calculated using the following derived formula:
$$\text{Mark Width} = \frac{\text{Slot Span}}{N + (N - 1) \times \text{Spacing}}$$
Where:
- **$N$**: The number of deduplicated row stubs for that category.
- **Slot Span**: The total percentage of the category width used (default 0.7-1.0).
- **Spacing**: The inner padding between bars within the same group.

### 12.1.6. Advantages of Row-Stub Layout

1. **Consistency**: Bars never "jump" positions if data is missing; the "0" value row keeps the slot occupied.
2. **Polar-Cartesian Parity**: The same logic that creates side-by-side bars in Cartesian coordinates creates perfectly partitioned sectors in a Rose Chart.
3. **Zero Hard-coding**: The renderer doesn't need to know if the chart is "Grouped" or "Stacked"—it simply follows the rows provided by the deduplicated data engine.

## 12.2. The Legend Layout Strategy

## 12.3 The Axis Layout Strategy