# Polar Marks

Polar marks are available only from the capability subpath:

```ts
import {
  angleGrid,
  pie,
  polar,
  radialArc,
  radialArea,
  radialBarAngle,
  radialBarRadius,
  radialDot,
  radialGrid,
  radialLine,
  radialRule,
  radialText,
} from '@tanstack/charts/polar'
```

`polar` resolves the responsive coordinate system. The exported `PolarMark`
and `PolarGuide` types are opaque composition contracts returned by the
built-in radial mark and guide constructors. Use them to type collections
passed to `polar`; do not implement their internal initialize or render
lifecycle. Guide backgrounds paint first, marks paint second, and guide
foregrounds paint last.

## `polar`

```ts
function polar(options: PolarOptions): ChartMark
```

| Option        | Type                    | Default       | Meaning                                          |
| ------------- | ----------------------- | ------------- | ------------------------------------------------ |
| `id`          | `string`                | Layer-derived | Stable container ID                              |
| `className`   | `string`                | None          | Class added beside `ts-chart__polar`             |
| `marks`       | `readonly PolarMark[]`  | Required      | Radial marks rendered in order                   |
| `guides`      | `readonly PolarGuide[]` | `[]`          | Background/foreground guide layers around marks  |
| `angle`       | `PolarAngleOptions`     | None          | Angle factory or instance and optional wrapping  |
| `radius`      | `PolarRadiusOptions`    | None          | Radius scale and optional responsive pixel range |
| `startAngle`  | `number`                | `0`           | Start of the available angular range in radians  |
| `endAngle`    | `number`                | `2π`          | End of the available angular range in radians    |
| `inset`       | `number`                | `0`           | Pixels removed from the maximum centered radius  |
| `radiusRatio` | `number`                | `1`           | Multiplier applied to the radius after inset     |

The default angular range is a complete circle. Angles use D3's radial
convention: zero is at twelve o'clock and positive values move clockwise.

`PolarAngleOptions` and `PolarRadiusOptions` accept compatible factories with
mark-inferred domains or configured instances with fixed domains. `nice`
applies after inference. TanStack supplies responsive ranges without mutating
an instance. An omitted `wrap` closes a complete revolution without adding a
duplicate semantic category, but preserves both endpoints of a partial range.
Set it explicitly to override that behavior.

`PolarRadiusOptions.range` overrides the default `[0, radius]` pixel range on
the copied radius scale. Each endpoint is a nonnegative pixel length or a
`PolarLength` callback, so concentric layouts can resolve physical ranges from
the final radius:

```ts
const radiusOptions = {
  scale: scaleLinear().domain([0, maximum]),
  range: [({ radius }) => radius * 0.2, ({ radius }) => radius],
}
```

The range is re-resolved on resize and never mutates the authored D3 scale.

`PolarLayoutContext` contains `chart`, `centerX`, `centerY`, `radius`,
`startAngle`, `endAngle`, and optional resolved angle/radius scales. Each
`PolarResolvedScale` exposes its semantic `domain`, responsive `map`, `ticks`,
and `bandwidth`. A `PolarLength` is either a pixel length or a callback of the
layout context. Use a callback for radii that must remain proportional during
resize.

The outer chart omits `x` and `y`. Cartesian axes do not participate in the
internal polar scales.

## `pie`

```ts
function pie<TDatum extends object>(
  source: Iterable<TDatum>,
  options: PieOptions<TDatum>,
): PieDatum<TDatum>[]
```

`pie` eagerly allocates a nonnegative value channel into angle intervals. It
does not render geometry or depend on chart dimensions.

| Option       | Type                      | Default     | Meaning                                      |
| ------------ | ------------------------- | ----------- | -------------------------------------------- |
| `value`      | `TransformValue<number>`  | Required    | Nonnegative value allocated to each interval |
| `orderBy`    | `TransformValue`          | None        | Explicit angular ordering value              |
| `order`      | `ascending \| descending` | `ascending` | Direction applied to `orderBy`               |
| `startAngle` | `number`                  | `0`         | Overall start angle in radians               |
| `endAngle`   | `number`                  | `2π`        | Overall end angle in radians                 |
| `gapAngle`   | `number`                  | `0`         | Direct empty angle between visible slices    |

Output rows remain in source order. `index` records angular order, `value` is
the resolved finite value, `fraction` is its share of the positive total, and
`startAngle`, `endAngle`, and `angle` are the visible interval and midpoint.
Each row also carries direct `source` and `sourceIndexes` lineage. Missing or
non-finite values are omitted, zero is retained, and negative values fail.

`gapAngle` is radius-independent. A complete revolution includes a seam gap;
a partial range uses only internal gaps and preserves both authored endpoints.
The output `padAngle` is intentionally `0` compatibility metadata so the
default `radialArc` accessors do not pad an already-gapped interval a second
time. Use the mark's `padAngle` and `padRadius` only when D3's radius-dependent
arc padding is wanted instead.

The derived fields `value`, `index`, `fraction`, `startAngle`, `endAngle`,
`angle`, `padAngle`, `source`, and `sourceIndexes` overwrite source fields with
the same names. Stable identity is not synthesized; preserve a semantic source
field and pass it to the consuming mark's `key` channel.

## `radialArc`

```ts
function radialArc<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialArcOptions<TDatum>,
): PolarMark<TDatum>
```

`radialArc` renders one D3 arc per valid interval.

| Option            | Meaning                                                        |
| ----------------- | -------------------------------------------------------------- |
| `id`, `className` | Stable layer ID and optional class                             |
| `startAngle`      | Start-angle channel; defaults to datum `startAngle`            |
| `endAngle`        | End-angle channel; defaults to datum `endAngle`                |
| `padAngle`        | Padding-angle channel; defaults to datum `padAngle`, then zero |
| `innerRadius`     | `PolarLength`; defaults to zero                                |
| `outerRadius`     | `PolarLength`; defaults to the layout radius                   |
| `cornerRadius`    | D3 arc corner radius as a `PolarLength`                        |
| `padRadius`       | Explicit D3 arc padding radius as a `PolarLength`              |
| `generator`       | Responsive D3 arc factory for advanced per-datum geometry      |
| `key`             | Stable arc identity; defaults to top/nested `id`, then index   |
| `z`               | Geometry and interaction group                                 |
| `color`           | Color-scale value; defaults to `z`                             |
| `fill`            | Final constant or datum-derived paint override                 |
| `fillOpacity`     | Fill opacity                                                   |
| `stroke`          | Constant or datum-derived boundary stroke                      |
| `strokeOpacity`   | Boundary opacity                                               |
| `strokeWidth`     | Boundary width                                                 |
| `strokeDasharray` | Boundary dash array                                            |
| `opacity`         | Whole-arc opacity                                              |

Use the native `pie` transform for flat typed rows with source lineage. D3
`pie` output remains valid interoperability input because its `startAngle`,
`endAngle`, and `padAngle` fields are also the channels this mark needs. A pie,
donut, and gauge differ only in inner radius and angular interval.

`generator` replaces the default D3 arc configuration for bespoke per-datum
geometry. Its factory receives the final `PolarLayoutContext`; keep the D3
generator context `null` so it returns SVG path data. Standard hierarchy
partitioning belongs to the optional
[`sunburst`](./sunburst.md) mark, which accepts flat source rows and preserves
their lineage.

## `radialBarRadius` and `radialBarAngle`

```ts
function radialBarRadius<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialBarRadiusOptions<TDatum>,
): PolarMark<TDatum>

function radialBarAngle<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialBarAngleOptions<TDatum>,
): PolarMark<TDatum>
```

The two radial-bar marks transpose ordinary bar semantics across polar axes.
`radialBarRadius` uses an angle band and a quantitative radius interval;
`radialBarAngle` uses a radius band and a quantitative angle interval. The
categorical scale must have positive bandwidth. Configure spacing through the
D3 band scale's inner and outer padding.

| Mark              | Categorical channel             | Quantitative interval                                                   |
| ----------------- | ------------------------------- | ----------------------------------------------------------------------- |
| `radialBarRadius` | `angle`; defaults to row index  | `radius` is shorthand for `radius2`; `radius1` is the optional baseline |
| `radialBarAngle`  | `radius`; defaults to row index | `angle` is shorthand for `angle2`; `angle1` is the optional baseline    |

An omitted `radialBarRadius.radius1` starts at physical radius zero, even when
`PolarRadiusOptions.range` maps semantic zero to an inner offset. An explicit
`radius1` is mapped through the radius scale. An omitted
`radialBarAngle.angle1` is semantic zero and is mapped through the angle scale.
Use the implicit physical-center radius baseline for nonnegative magnitudes.
For signed values or true radial intervals, set `radius1: 0` (or another
semantic baseline) so both endpoints map through the configured scale.

Both marks accept `id`, `className`, `key`, `z`, `color`, `fill`, fill opacity,
stroke styling, opacity, and motion. `cornerRadius` accepts a `PolarLength` or
`"full"`; the latter resolves to half the bar's radial thickness. Each valid
bar emits one geometry-backed interaction point at its quantitative endpoint
and preserves its interval endpoints for focus and tooltip formatting.

## `radialLine` and `radialArea`

```ts
function radialLine<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialLineOptions<TDatum>,
): PolarMark<TDatum>

function radialArea<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialAreaOptions<TDatum>,
): PolarMark<TDatum>
```

Both marks use `angle` and `radius` channels and accept `id`, `className`,
`key`, `z`, `color`, and a D3 curve factory. The channels default to row index
and a numeric datum. `color` contributes to the chart color scale and defaults
to `z`. When `z` is omitted, an authored `color` also partitions the paths.
When both are present, `z` remains the explicit geometry and interaction
group. `radialLine` accepts final stroke, dash, opacity, and optional `points`
styling. `radialArea` accepts final fill and stroke styling plus `radius1` for
an explicit inner scale value; `radius1` defaults to zero.

Their datum key defaults to a unique top-level or nested `data.id`, then a
unique angle within each effective path group, then row index.

Input order is path order. Use a closed D3 curve such as
`curveLinearClosed` for radar polygons. An explicit `z`, or `color` when `z`
is absent, creates one path per group. `radialArea` can carry its own stroke;
layer a closed `radialLine` only when the outline needs independent styling.

## `radialDot`

```ts
function radialDot<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialDotOptions<TDatum>,
): PolarMark<TDatum>
```

`radialDot` uses the same angle/radius channel defaults. It also accepts `id`,
`className`, `key`, `z`, `color`, `r`, `rScale`, fill, stroke, and opacity styling.
Radius defaults to 3.5 pixels. Each valid datum emits one interaction point
with its original angle/radius values and projected screen position. Its key
defaults to a unique top-level or nested `data.id`, then row index.

## `radialText`

```ts
function radialText<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialTextOptions<TDatum>,
): PolarMark<TDatum>
```

`radialText` maps `angle` and `radius` channels through the container's copied
polar scales, then positions labels with D3's radial point projection. It
accepts `text`, `key`, `z`, `color`, fill, font size and weight, anchor, baseline,
rotation, and pixel `dx`/`dy`. `radiusOffset` is a signed constant or per-datum
visual channel applied in pixels after the semantic radius is mapped. It does
not contribute to the radius domain. Set `anchor: "outside"` to resolve
`start`, `middle`, or `end` from the final mapped angle. Exact and near-exact
top and bottom angles use `middle`. A nonfinite resolved offset omits that
label and its interaction point.

Use it for arc labels, donut-center values, and gauge readouts without leaving
the polar coordinate system. Its interaction point follows the final radial
offset plus `dx`/`dy` while retaining the original semantic radius value. Its
key defaults to a unique top-level or nested `data.id`, then row index.

## `radialRule`

```ts
function radialRule<TDatum>(
  source: Iterable<TDatum>,
  options?: RadialRuleOptions<TDatum>,
): PolarMark
```

`radialRule` emits one radial segment per datum. `angle`, `radius1`, and
`radius2` are scale values; `radius1` defaults to zero. The mark also accepts
`radius1Offset` and `radius2Offset` as signed constant or per-datum pixel
visual channels applied after the corresponding semantic radius is mapped.
Offsets never contribute to radius-domain inference. A nonfinite resolved
endpoint offset omits that segment. The mark also accepts `key`, `z`, `color`,
stroke, opacity, width, and dash styling. It covers gauge needles, ticks, and
pie-label leaders without expanding one logical segment into two path rows.
Rules remain decorative and emit no interaction points. Its key defaults to a
unique top-level or nested `data.id`, then a unique angle within each `z`
group, then row index.

Pixel offsets do not reserve space outside the polar radius. Use
`radiusRatio`, `inset`, or chart margins when labels or leaders must remain
inside the chart surface.

## `radialGrid` and `angleGrid`

```ts
function radialGrid(options?: RadialGridOptions): PolarGuide
function angleGrid(options?: AngleGridOptions): PolarGuide
```

`radialGrid` draws radius values as circles or polygons. Supply explicit
`values`, or let `ticks` request values from the configured radius scale.
Labels are off by default. Label angle, offset, rotation, format, fill, and
font size are configurable.

`angleGrid` draws spokes for explicit `values` or the configured angle domain.
It can show labels around the circumference with `format` and `labelOffset`.
Labels are on by default and use the same outside-anchor rule as
`radialText({ anchor: "outside" })` unless `labelAnchor` is supplied. Both
guides accept ID, class, stroke, opacity, width, and dash styling.

Guide label position and orientation can be constants or callbacks through
`PolarGuideLabelOption`. Each callback receives a `PolarGuideLabelContext`
with the semantic `value`, `index`, angle, radius, local x/y position, and
complete layout. Use `labelAnchor`, `labelBaseline`, `labelDx`, `labelDy`, and
`labelRotate` without rebuilding the guide. `labelClassName` targets the label
group. Guides are decorative and emit no interaction points.

Every guide returns a `PolarGuideScene`:

```ts
interface PolarGuideScene {
  background: readonly SceneNode[]
  foreground?: readonly SceneNode[]
}
```

`polar` collects every guide background in declaration order, renders all
marks, then appends every optional foreground in the same guide order. The
built-in grids put rings and spokes in `background` and labels in
`foreground`, keeping labels legible without painting grid geometry over the
data.

The exported option contracts are `PolarOptions`, `RadialArcOptions`,
`RadialBarRadiusOptions`, `RadialBarAngleOptions`, `RadialLineOptions`,
`RadialAreaOptions`, `RadialDotOptions`, `RadialTextOptions`,
`RadialRuleOptions`, `RadialGridOptions`, and `AngleGridOptions`. The coordinate contracts are `PolarAngleOptions`,
`PolarRadiusOptions`, `PolarResolvedScale`, `PolarLayoutContext`,
`PolarLength`, `PolarGuideLabelContext`, `PolarGuideLabelOption`, `PolarMark`,
`PolarGuide`, and `PolarGuideScene`. `PolarMark` and `PolarGuide` annotate
built-in constructor results rather than a supported custom-extension
boundary.

See [Polar and Radar Charts](../../examples/polar-and-radar.md) for pie,
donut, gauge, radar, radial bar, numeric line, and numeric scatter
compositions.
