> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ploterr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Compose a sheet

> Put views on an A3 page at a scale, add a dimension and a note, and let your users arrange the page.

export const LiveExample = ({path, title = 'Live example', height = 460}) => {
  return <Frame>
      <iframe src={`https://floorkit-examples.ploterr.com/floorkit/${path}?embed=1`} ref={frame => {
    if (!frame || !['localhost', '127.0.0.1'].includes(window.location.hostname)) return;
    const local = `http://localhost:5555/${path}?embed=1`;
    if (frame.src !== local) frame.src = local;
  }} title={title} loading="lazy" allow="fullscreen" style={{
    width: '100%',
    height: `${height}px`,
    border: 0,
    display: 'block'
  }} />
    </Frame>;
};

A sheet is a page. A viewport puts one saved view on the page at a scale. You can put many viewports on one sheet, and one view on many sheets. A sheet also holds dimensions, notes and a title block.

<LiveExample path="guides/compose-a-sheet" />

## Before you start

You have a project with the views `ground-plan`, `section-a` and `elevation-south`, and the section mark, from [Create a plan view](/floorkit/guides/create-a-plan-view) and [Cut a section](/floorkit/guides/cut-a-section).

## Write the sheet

All sizes and positions on a sheet are in millimetres. The origin is the top-left corner of the page. This sheet starts with one viewport, the plan at 1:50.

```typescript theme={null}
import type { ProjectFile, Sheet } from '@opengeometry/floorkit';

export const planSheet: Sheet = {
  id: 'sheet-a101',
  name: 'House drawings',
  number: 'A-101',
  page: {
    format: 'A3',
    orientation: 'landscape',
    size: { width: 420, height: 297 },
    margin: 10,
  },
  viewports: [
    {
      id: 'plan-viewport',
      viewId: 'ground-plan',
      drawingNumber: '1',
      frame: { x: 20, y: 20, width: 200, height: 120 },
      scale: 0.02,
      rotationDeg: 0,
      clip: true,
      title: { text: 'Ground floor plan', showScale: true },
    },
  ],
  annotations: [],
  titleblock: { templateId: 'default', values: { sheetNumber: 'A-101' } },
};
```

| Field | Meaning |
| - | - |
| `viewId` | The id of the saved view that the viewport shows |
| `drawingNumber` | The number of this drawing on the sheet. The title shows it |
| `frame` | The rectangle on the page, in millimetres |
| `scale` | Paper size divided by real size. `0.02` is 1:50. `0.01` is 1:100. |
| `clip` | `true` hides the linework outside the frame |
| `title` | The text below the viewport. `showScale` adds the scale. |

## Add it to the project

```typescript theme={null}
export function addSheet(project: ProjectFile): ProjectFile {
  return { ...project, paper: { ...project.paper, sheets: [...project.paper.sheets, planSheet] } };
}
```

A project is data, so each change makes a new project. This helper changes the sheet and keeps the other parts of the project:

```typescript theme={null}
export function changeSheet(project: ProjectFile, change: (sheet: Sheet) => Sheet): ProjectFile {
  return {
    ...project,
    paper: {
      ...project.paper,
      sheets: project.paper.sheets.map((sheet) =>
        sheet.id === planSheet.id ? change(sheet) : sheet,
      ),
    },
  };
}
```

## Put more views on the sheet

Add one viewport for each view. These viewports put the section at 1:50 and the elevation at 1:100.

```typescript theme={null}
import type { Viewport } from '@opengeometry/floorkit';

export const sectionViewport: Viewport = {
  id: 'section-viewport',
  viewId: 'section-a',
  drawingNumber: '2',
  frame: { x: 235, y: 20, width: 165, height: 140 },
  scale: 0.02,
  rotationDeg: 0,
  clip: true,
  title: { text: 'Section A', showScale: true },
};

export const elevationViewport: Viewport = {
  id: 'elevation-viewport',
  viewId: 'elevation-south',
  drawingNumber: '3',
  frame: { x: 20, y: 160, width: 200, height: 85 },
  scale: 0.01,
  rotationDeg: 0,
  clip: true,
  title: { text: 'South elevation', showScale: true },
};

export function addViewport(project: ProjectFile, viewport: Viewport): ProjectFile {
  return changeSheet(project, (sheet) => ({ ...sheet, viewports: [...sheet.viewports, viewport] }));
}
```

## Change the scale

The scale is a field of the viewport. The title below the viewport shows the new scale.

```typescript theme={null}
export function setScale(project: ProjectFile, viewportId: string, scale: number): ProjectFile {
  return changeSheet(project, (sheet) => ({
    ...sheet,
    viewports: sheet.viewports.map((viewport) =>
      viewport.id === viewportId ? { ...viewport, scale } : viewport,
    ),
  }));
}
```

## Number the section mark

The section mark on the plan can show where its section is. Give the target of the mark the sheet and the viewport. The mark then reads `2 / A-101`: drawing 2 on sheet A-101.

```typescript theme={null}
export function numberSectionMark(project: ProjectFile): ProjectFile {
  return {
    ...project,
    markers: (project.markers ?? []).map((marker) =>
      marker.id === 'section-a-mark'
        ? {
            ...marker,
            target: { viewId: 'section-a', sheetId: 'sheet-a101', viewportId: 'section-viewport' },
          }
        : marker,
    ),
  };
}
```

## Add a dimension and a note

A sheet holds annotations. A `measure` dimension uses two points of the model, in metres, in the view of one viewport. Floorkit calculates the distance, so the text is always the real size. A `text` note uses one point on the page, in millimetres.

```typescript theme={null}
import type { Annotation } from '@opengeometry/floorkit';

export const roomWidth: Annotation = {
  id: 'room-width',
  kind: 'measure',
  viewportId: 'plan-viewport',
  from: { x: 0, y: 4 },
  to: { x: 6, y: 4 },
  offset: 10,
  fontSize: 2.5,
};

export const siteNote: Annotation = {
  id: 'site-note',
  kind: 'text',
  text: 'Check the door width on site.',
  position: { x: 240, y: 200 },
  fontSize: 4,
};

export function addAnnotation(project: ProjectFile, annotation: Annotation): ProjectFile {
  return changeSheet(project, (sheet) => ({
    ...sheet,
    annotations: [...sheet.annotations, annotation],
  }));
}
```

## Show the sheet on screen

`createSheetRenderer` makes the screen view of a sheet. It needs your sheets and a function that draws a saved view. Here `project` is a function, so the renderer always draws your current project.

```typescript theme={null}
import {
  TitleblockRegistry,
  createSheetRenderer,
  drawProjectView,
  type PaperScreenRenderer,
} from '@opengeometry/floorkit';

export async function showSheet(
  container: HTMLElement,
  project: () => ProjectFile,
): Promise<PaperScreenRenderer> {
  const renderer = await createSheetRenderer();
  renderer.mount(container, project().paper, {
    resolveSavedView: (viewId) => drawProjectView(project(), viewId),
    extractor: { extract: () => drawProjectView(project(), 'ground-plan') },
    templates: new TitleblockRegistry(),
    projectName: 'Garden house',
  });
  return renderer;
}
```

`resolveSavedView` draws a viewport that has a `viewId`. `extractor` draws a viewport that has its own `source`. This project has no such viewport, so the extractor gives the plan.

After you change the project, call `renderer.update(project.paper)`.

## Let the user arrange the page

The user can select a viewport or an annotation and move it. The renderer does not change your project. It sends an event, and you save the change.

```typescript theme={null}
import type { PaperEditEvent } from '@opengeometry/floorkit';

export function applyMove(project: ProjectFile, event: PaperEditEvent): ProjectFile {
  return changeSheet(project, (sheet) => ({
    ...sheet,
    viewports: sheet.viewports.map((viewport) =>
      viewport.id === event.targetId && event.frame
        ? { ...viewport, frame: event.frame }
        : viewport,
    ),
    annotations: sheet.annotations.map((annotation) =>
      annotation.id === event.targetId && event.annotation ? event.annotation : annotation,
    ),
  }));
}

export function followEdits(
  renderer: PaperScreenRenderer,
  read: () => ProjectFile,
  write: (project: ProjectFile) => void,
): void {
  renderer.on('move', (event) => {
    write(applyMove(read(), event));
    renderer.update(read().paper);
  });
  renderer.on('create', (event) => {
    if (!event.annotation) return;
    write(addAnnotation(read(), event.annotation));
    renderer.update(read().paper);
  });
}
```

The `create` event comes from a drawing tool. `renderer.setTool('text')` starts the text tool: the user clicks the page and gets a note. Other tools are `'dimension'`, `'leader'`, `'line'` and `'rect'`. `renderer.setTool('select')` goes back to selection.

## Change the paper style

A paper style changes the pens, the fills and the lettering of the sheet. It does not change your project.

```typescript theme={null}
export function setSheetStyle(
  renderer: PaperScreenRenderer,
  style: 'iso-128' | 'ink-sketch' | 'graphite' | 'napkin' | undefined,
): void {
  renderer.setPaperStyle?.(style);
}
```

`undefined` goes back to the style of the document. To export with a style, pass `{ style: 'ink-sketch' }` as the options of [`exportProjectPdf`](/floorkit/guides/export-a-pdf).

## Rules

* The `size` of the page must agree with its `format` and `orientation`. [`pageSize`](/floorkit/api/drawings/page-sizes-and-frames) gives you the correct values.
* A viewport shows the full view, centred in its frame. Set `viewCenter` to keep one model point in the centre.
* To show the same view at a different scale, add a second viewport with the same `viewId`.
* The points of a `measure` dimension do not follow the model. If you move the wall, set the points again.
* The renderer shows your sheets. It does not own them. Save each change in your project, then call `update`.

## Next

<CardGroup cols={2}>
  <Card title="Export a PDF" icon="file-pdf" href="/floorkit/guides/export-a-pdf">
    Get this sheet as a file.
  </Card>

  <Card title="Sheet types" icon="code" href="/floorkit/api/drawings/sheet-documents#types">
    See each field of a sheet, a viewport and an annotation.
  </Card>

  <Card title="createSheetRenderer API" icon="code" href="/floorkit/api/views/create-sheet-renderer">
    See each member of the screen view.
  </Card>

  <Card title="Paper styles API" icon="code" href="/floorkit/api/drawings/paper-styles">
    Make your own style.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.