# Drawing

The pen draws freehand ink on the canvas. Each stroke becomes an entity of type `draw`, so you can
select it, move it, delete it and undo it like any other entity, and you can save it and load it
with the rest of the graph.

## Turn on the pen

Press D while the canvas has focus to turn on the pen, and press D again or Escape to turn it off.
The canvas takes focus when you click it or tab to it. While the pen is on, the pointer is a
crosshair.
```tsx
'use client';

import {
  KookieFlow,
  Toolbar,
  useGraph,
  type EntityTypeDefinition,
  type KookieFlowProps,
} from '@kushagradhawan/kookie-flow';

// Both objects live outside the component, so neither changes identity on a render.
const entityTypes: Record<string, EntityTypeDefinition> = {
  draw: { type: 'draw', toolbar: true },
};

const penStyle: KookieFlowProps['penStyle'] = { strokeWidth: 4, strokeColor: '#e5484d' };

export default function DrawingExample() {
  const { entities, edges, onEntitiesChange, onEdgesChange } = useGraph({
    initialEntities: [
      {
        id: 'hint',
        type: 'text',
        position: { x: 40, y: 40 },
        width: 420,
        data: { content: 'Click the board, press D, and drag to draw. Press Escape to select.' },
      },
    ],
    history: true,
  });

  return (
    <div style={{ width: '100%', height: 360 }}>
      <KookieFlow
        entities={entities}
        edges={edges}
        onEntitiesChange={onEntitiesChange}
        onEdgesChange={onEdgesChange}
        entityTypes={entityTypes}
        penStyle={penStyle}
        ariaLabel="Drawing board"
      >
        <Toolbar />
      </KookieFlow>
    </div>
  );
}
```
While the pen is on, a press of the primary button starts a stroke wherever it lands, including
over a node, a socket or a widget. You can still pan with the middle button or by holding Space.
The pen stays on after each stroke, so you can draw several in a row. Turn it off to select or
move what you drew.

D turns the pen on or off only when you press it without Cmd, Ctrl, Alt or Shift. Alt+D, for
example, lines up the right edges of two or more selected entities. No prop or ref method turns on
the pen, so the people using your canvas turn it on from the keyboard.

## One stroke is one entity

While you draw, the canvas grows the stroke on screen and reports nothing to your app. When you
release the button, the canvas does three things:

1. Simplifies the stroke, dropping the points along straight runs and keeping the corners.
2. Fits a box around the ink, including half the stroke's width on every side.
3. Reports the stroke through `onEntitiesChange` as a single `add` change.

Because the stroke arrives as one change, `useGraph({ history: true })` records it as one undo step,
and Cmd+Z or Ctrl+Z removes the whole stroke. A tap without moving leaves a dot.

A short zigzag drawn with the default pen arrives as an entity like this one:

```ts
import type { DrawEntity } from '@kushagradhawan/kookie-flow';

const stroke: DrawEntity = {
  id: 'kf-draw-1757592000000-k3x9q', // generated by the canvas
  type: 'draw',
  position: { x: 118.5, y: 78.5 }, // the top-left corner of the box
  width: 63, // the distance across the points, plus the stroke width
  height: 43,
  resizable: false,
  data: { points: [1.5, 41.5, 31.5, 1.5, 61.5, 41.5], strokeWidth: 3 },
};
```

`DrawEntity` is the type for it: an `Entity<DrawEntityData>` whose `type` is `'draw'`.

| `DrawEntityData` field | Type | Default | What it is |
| --- | --- | --- | --- |
| `points` | `number[]` | none | The stroke as a flat list, `x, y, x, y`, relative to the entity's top-left corner. |
| `strokeWidth` | `number` | `3` | The thickness of the ink, in world pixels. |
| `strokeColor` | `string` | The theme's primary text colour | The colour of the ink. |

Points are relative, so moving a stroke changes its `position` and leaves `points` alone. The box
decides what a click hits, so a click anywhere inside a stroke's box selects it, not only a click
on the ink.

## Set the pen's style

`penStyle` sets the width and colour of the strokes the pen draws next. Its type is
`Pick<DrawEntityData, 'strokeWidth' | 'strokeColor'>`.

```tsx
<KookieFlow
  entities={entities}
  edges={edges}
  onEntitiesChange={onEntitiesChange}
  penStyle={{ strokeWidth: 6, strokeColor: '#3e63dd' }}
/>
```

Without `penStyle`, ink is 3 pixels wide and has no `strokeColor` of its own. Ink with no colour of
its own is drawn in the theme's primary text colour, and it changes colour when the theme switches
between light and dark. Ink with a `strokeColor` keeps that colour in both.

A stroke keeps the style it was drawn with. When `penStyle` changes, only the strokes drawn after
the change use the new style.

Write `strokeColor` as a six-digit hex value, such as `#3e63dd`. The ink is drawn in WebGL, which
can't resolve a CSS variable. The toolbar's colour control is the browser's colour input, which
can show only a six-digit hex value.

## Restyle strokes afterwards

To let people restyle ink, turn on the built-in toolbar controls for the `draw` type and render
`Toolbar` inside `KookieFlow`, as the example above does:

```tsx
const entityTypes: Record<string, EntityTypeDefinition> = {
  draw: { type: 'draw', toolbar: true },
};
```

Select a stroke, and the toolbar shows two controls. `strokeColor` is a colour picker that shows
the stroke's colour, or the theme colour for ink with none. `strokeWidth` is a number field that
commits when you press Enter or move focus away, and it ignores zero and negative values. Both apply
to every selected stroke. The [selection toolbar chapter](/interaction/toolbar) covers the toolbar
itself.

A width change reports three changes for each stroke, not one: a new `position`, new `dimensions`
and new `data` holding the width and the shifted points. The box holds half the width on every
side and the points are relative to that box, so a thicker line needs a bigger box, a corner moved
out, and every point moved in by the same amount. The ink stays exactly where it was.

A colour change reports one `data` change for each selected stroke. To restyle strokes from your
own code, report the same change. Set `strokeColor` to `undefined` to put a stroke back on the
theme colour:

```tsx
const strokes = flowRef.current?.getSelectedEntities().filter((e) => e.type === 'draw') ?? [];
onEntitiesChange(strokes.map((e) => ({ type: 'data', id: e.id, data: { strokeColor: '#3e63dd' } })));
```

## Create ink from data

You can put strokes in `entities` yourself, such as when you load a saved drawing or import a path
from another tool. Build them the way the pen does, or the box and the ink disagree and clicks miss
the stroke.

```ts title="ink.ts"
import type { DrawEntity, XYPosition } from '@kushagradhawan/kookie-flow';

/** Fits a pen-style box around world points: half the stroke width of room on every side. */
export function fitInk(world: number[], strokeWidth: number) {
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  for (let i = 0; i < world.length; i += 2) {
    minX = Math.min(minX, world[i]);
    maxX = Math.max(maxX, world[i]);
    minY = Math.min(minY, world[i + 1]);
    maxY = Math.max(maxY, world[i + 1]);
  }
  const position: XYPosition = { x: minX - strokeWidth / 2, y: minY - strokeWidth / 2 };
  return {
    position,
    width: maxX - minX + strokeWidth,
    height: maxY - minY + strokeWidth,
    points: world.map((v, i) => v - (i % 2 === 0 ? position.x : position.y)),
  };
}

export function ink(id: string, world: number[], strokeWidth = 3, strokeColor?: string): DrawEntity {
  const box = fitInk(world, strokeWidth);
  return {
    id,
    type: 'draw',
    position: box.position,
    width: box.width,
    height: box.height,
    resizable: false,
    data: { points: box.points, strokeWidth, ...(strokeColor ? { strokeColor } : {}) },
  };
}
```

`ink('zigzag', [100, 200, 160, 140, 220, 200, 280, 140], 4)` returns a four-pixel zigzag in the
theme colour. Pass at least one point, because `fitInk` can't fit a box around none.

Set `resizable: false`, as the pen does. The canvas draws ink from its points and never scales them
to the box, so dragging a corner would change the box and leave the ink as it was.

The same `fitInk` refits a stroke for a width control of your own. Turn the stroke's points back into
world points, fit them at the new width, and report all three changes together:

```ts
import type { DrawEntity, EntityChange } from '@kushagradhawan/kookie-flow';
import { fitInk } from './ink';

export function setInkWidth(stroke: DrawEntity, strokeWidth: number): EntityChange[] {
  const { x, y } = stroke.position;
  const world = (stroke.data.points ?? []).map((v, i) => v + (i % 2 === 0 ? x : y));
  const box = fitInk(world, strokeWidth);
  return [
    { type: 'position', id: stroke.id, position: box.position },
    { type: 'dimensions', id: stroke.id, dimensions: { width: box.width, height: box.height } },
    { type: 'data', id: stroke.id, data: { strokeWidth, points: box.points } },
  ];
}
```
