@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
143 lines (114 loc) • 6.88 kB
Markdown
---
title: Dot and Hexagon Marks
description: Reference for dot and hexagon channels, pixel radius, injected radius scales, color, grouping, keys, and interaction points.
---
`dot` and `hexagon` place fixed-pixel symbols at scaled x/y values. Their
radius does not change when a positional scale zooms or a chart resizes unless
the application changes `r` or `rScale`.
```ts
import { dot, hexagon } from '@tanstack/charts'
```
## `dot`
```ts
const mark = dot(rows, {
x: 'date',
y: 'value',
z: 'series',
r: 'population',
rScale: radiusScale,
})
```
```ts
function dot<TDatum>(
source: Iterable<TDatum>,
options?: DotOptions<TDatum>,
): ChartMark<TDatum, InferredX, InferredY>
```
### Options
| Option | Type | Default | Meaning |
| --------------- | ------------------------------------ | -------------------- | ------------------------------------- |
| `id` | `string` | Layer-derived | Stable mark ID |
| `x` | `Channel<TDatum, ChartValue?>` | Row index | Horizontal value |
| `y` | `Channel<TDatum, ChartValue?>` | Numeric datum | Vertical value |
| `z` | `Channel<TDatum, ChartKey?>` | No group | Interaction group |
| `color` | `Channel<TDatum, ChartKey?>` | `z` | Value sent to the chart color scale |
| `key` | `Channel<TDatum, ChartKey>` | ID, x, y, x/y, index | Stable scene and interaction identity |
| `r` | `number \| Channel<TDatum, number?>` | `3.5` | Raw radius value |
| `rScale` | `(value: number) => number` | Identity | Maps each valid raw radius to pixels |
| `fill` | `string` | Resolved color | Final constant fill override |
| `fillOpacity` | `number` | SVG default | Fill opacity |
| `layout` | `DodgeXLayout \| DodgeYLayout` | None | Resolved collision placement |
| `stroke` | `string` | None | Constant stroke |
| `strokeOpacity` | `number` | SVG default | Stroke opacity |
| `strokeWidth` | `number` | SVG default | Stroke width |
| `states` | `readonly ChartMarkState[]` | None | Focus-driven presentation overrides |
`rScale` is called only for finite, nonnegative raw radii. The mapped result
must also be finite and nonnegative or the row is skipped.
Unlike `hexagon`, `dot.fill` and `dot.stroke` are constants. Use `color` and
the chart color scale for data-driven dot color.
[`dodgeX` and `dodgeY`](./dodge.md) derive one dot coordinate after the
measured axis scale and final plot bounds resolve. The generated coordinate
does not contribute a positional scale domain.
Without an explicit key, `dot` tries a unique top-level or nested `data.id`,
then x, y, and the x/y tuple. Supply `key` when positions can change while the
same entity should reconcile across updates.
## `hexagon`
`hexagon` draws a pointy-topped six-sided symbol.
```ts
const mark = hexagon(bins, {
x: 'x',
y: 'y',
color: 'count',
r: 'count',
rScale: radiusScale,
})
```
```ts
function hexagon<TDatum>(
source: Iterable<TDatum>,
options?: HexagonOptions<TDatum>,
): ChartMark<TDatum, InferredX, InferredY>
```
### Options
| Option | Type | Default | Meaning |
| --------------- | ------------------------------------ | ---------------------- | ----------------------------------- |
| `id` | `string` | Layer-derived | Stable mark ID |
| `x` | `Channel<TDatum, ChartValue?>` | Row index | Horizontal center |
| `y` | `Channel<TDatum, ChartValue?>` | Numeric datum | Vertical center |
| `z` | `Channel<TDatum, ChartKey?>` | No group | Interaction group |
| `color` | `Channel<TDatum, ChartKey?>` | `z` | Value sent to the chart color scale |
| `key` | `Channel<TDatum, ChartKey>` | Top/nested `id`, index | Stable identity |
| `r` | `number \| Channel<TDatum, number?>` | `6` | Raw circumradius |
| `rScale` | `(value: number) => number` | Identity | Maps radius values to pixels |
| `fill` | `VisualChannel<TDatum, string>` | Resolved color | Final fill override |
| `fillOpacity` | `number` | SVG default | Fill opacity |
| `stroke` | `VisualChannel<TDatum, string>` | None | Optional stroke per mark or row |
| `strokeOpacity` | `number` | SVG default | Stroke opacity |
| `strokeWidth` | `number` | SVG default | Stroke width |
The generated vertices begin at the top and proceed in 60-degree increments.
The interaction point remains at the scaled center, and its `color` is the
resolved fill.
## Valid rows and points
Both marks skip a row when x or y is not a valid `ChartValue`, or when the
final radius is negative, nonfinite, null, or undefined. A zero radius remains
a valid interaction point even though it has no visible area.
Every valid row emits one `ChartPoint` with:
- the original datum and row index
- semantic channel values in `xValue` and `yValue`
- scaled center coordinates in `x` and `y`
- `z` as its group
- fill paint as its interaction color
## Radius scales
Pass a numeric scale factory to infer `[0, maximum]` from the radius channel.
Configure its semantic pixel range inside the factory:
```ts
import { scaleSqrt } from 'd3-scale'
const rScale = {
scale: () => scaleSqrt().range([2, 18]),
}
```
An ordinary numeric mapper or configured scale instance remains valid when
the application owns the complete mapping. The shared integration boundary is documented in
[Scales](../../concepts/scales-and-d3.md).
Radius does not contribute to x/y guide margins. Add an explicit partial
margin when large edge symbols must remain fully inside the SVG viewport.