# Leather

The Leather Workbench is Moket's own plugin for leather goods: wallets, card holders, straps and cases. Its features work like any feature of a part: you add them with `feature.create` in a plan and read them with `describe_commands` first. A leather part is flat pieces stacked on each other, stitch lines through them, and folds that close it.

Read [Parts](parts.md) first. `inspect_document` lists the user's plugins under `plugins` with their feature types; Leather is there unless the user turned it off in Settings > Plugins.

## Features

| Feature                          | What it does                                                                                                                   |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `io.moket.leather.piece`         | A flat piece from one closed region of a sketch, `thickness` thick, with its `leather`, `finish`, `grain`, `name` and `count`. |
| `io.moket.leather.stitchPath`    | A stitch line: holes along edges of a piece's face, `margin` in from them, at a `pitch`, spread by arc length.                 |
| `io.moket.leather.stitchHoles`   | Punches a stitch line's holes through every piece in `bodies`, the same holes in each layer: `chisel` slits or `round`.        |
| `io.moket.leather.seamAllowance` | Moves a piece's cut line out from chosen edges for the pattern; the piece itself does not change.                              |
| `io.moket.leather.fold`          | Folds pieces along a sketch line by an `angle` with an inside `radius`; stacked layers fold together.                          |

- A piece grows from its sketch along the sketch's normal; `"flip": true` grows it the other way. Its faces are named like an extrude's: `f(<piece>:start)` on the sketch, `f(<piece>:end)` on top, `f(<piece>:side:<line>)` round it.
- Stack a piece on another by sketching it on the lower one's top face (`"plane":{"kind":"face","ref":{"kind":"face","name":"f($outer:end)"}}`). On a face that faces up, the sketch's x and y are the part's X and Y.
- Leathers are `veg-tan` (outers, 1.2 to 2 mm), `chrome-tan` (pockets, 0.6 to 1 mm) and `lining` (0.4 to 0.8 mm); finishes `natural`, `tan`, `brown`, `dark-brown`, `black`, `burgundy`, `navy`, `green`. Use the user's leather and thickness.
- The usual pitch is 3.85 mm (a 4 mm iron), 3 mm for small goods; the margin 3 to 4 mm. `corners` `hole` puts a hole at every corner and evens the pitch between corners.

## Stitching through layers

One stitch line and one Stitch holes through every layer it holds give every layer the same holes. Draw the stitch line on the top layer's face and list the pieces it goes through in `bodies`, bottom to top: a hole goes only where a piece is, so a pocket that covers part of the outer gets the holes over it.

Edges of a stitch line follow each other end to end. Name them from the piece and the lines of its sketch: in the same plan, a template's lines are `$outline.lines.bottom` (a rectangle's bottom, right, top, left; a profile's `$pocket.lines.0` from its first corner on).

```json plan:card-holder
{
  "summary": "Card holder 100 x 65 with a front pocket, stitched round",
  "steps": [
    {
      "label": "Name and parameters",
      "commands": [
        { "type": "document.rename", "name": "Card holder" },
        {
          "type": "parameter.create",
          "id": "$width",
          "name": "width",
          "expression": "100 mm"
        },
        {
          "type": "parameter.create",
          "id": "$height",
          "name": "height",
          "expression": "65 mm"
        },
        {
          "type": "parameter.create",
          "id": "$pocketHeight",
          "name": "pocketHeight",
          "expression": "45 mm"
        }
      ]
    },
    {
      "label": "Outer, 1.4 mm veg-tan",
      "commands": [
        {
          "type": "sketch.template",
          "id": "$outline",
          "name": "Outer",
          "template": {
            "kind": "rectangle",
            "width": "width",
            "depth": "height"
          }
        },
        {
          "type": "feature.create",
          "id": "$outer",
          "featureType": "io.moket.leather.piece",
          "name": "Outer",
          "params": {
            "profile": { "sketch": "$outline" },
            "body": "$outerBody",
            "thickness": "1.4 mm",
            "leather": "veg-tan",
            "finish": "brown"
          }
        }
      ]
    },
    {
      "label": "Pocket on the outer, 0.8 mm chrome-tan",
      "commands": [
        {
          "type": "sketch.template",
          "id": "$pocketOutline",
          "name": "Pocket",
          "template": {
            "kind": "profile",
            "corners": [
              { "x": "-width / 2", "y": "-height / 2" },
              { "x": "width / 2", "y": "-height / 2" },
              { "x": "width / 2", "y": "pocketHeight - height / 2" },
              { "x": "-width / 2", "y": "pocketHeight - height / 2" }
            ],
            "plane": {
              "kind": "face",
              "ref": { "kind": "face", "name": "f($outer:end)" }
            }
          }
        },
        {
          "type": "feature.create",
          "id": "$pocket",
          "featureType": "io.moket.leather.piece",
          "name": "Pocket",
          "params": {
            "profile": { "sketch": "$pocketOutline" },
            "body": "$pocketBody",
            "thickness": "0.8 mm",
            "leather": "chrome-tan",
            "finish": "brown"
          }
        }
      ]
    },
    {
      "label": "Stitch round the outer, through the pocket",
      "commands": [
        {
          "type": "feature.create",
          "id": "$stitch",
          "featureType": "io.moket.leather.stitchPath",
          "name": "Edge stitch",
          "params": {
            "edges": [
              {
                "kind": "edge",
                "name": "e(f($outer:end)|f($outer:side:$outline.lines.bottom))"
              },
              {
                "kind": "edge",
                "name": "e(f($outer:end)|f($outer:side:$outline.lines.right))"
              },
              {
                "kind": "edge",
                "name": "e(f($outer:end)|f($outer:side:$outline.lines.top))"
              },
              {
                "kind": "edge",
                "name": "e(f($outer:end)|f($outer:side:$outline.lines.left))"
              }
            ],
            "margin": "4 mm",
            "pitch": "3.85 mm"
          }
        },
        {
          "type": "feature.create",
          "id": "$holes",
          "featureType": "io.moket.leather.stitchHoles",
          "name": "Edge holes",
          "params": {
            "line": "$stitch",
            "bodies": ["$outerBody", "$pocketBody"],
            "shape": "chisel"
          }
        }
      ]
    }
  ]
}
```

## Checking stitch counts

A stitch line and Stitch holes report what they made: the preview's `reports` (`"78 holes, pitch 3.8 to 3.83 mm, 298 mm of stitching"`), the feature's `report` in `inspect_document` and its `reports` in `get_feature`, with the count in `details` (`holes`). Two edges sewn to each other (the sides of a gusset and the panel it joins, a fold's two halves stitched closed) need the same number of holes: compare the reports, and change the pitch or the gaps (`startGap`, `endGap` on an open line) of one until they match. Tell the user the counts.

## Folds

A fold turns the pieces in `bodies` about a line of a sketch on them (`line`: `{"sketch":<sketch>,"curves":[<line>]}`), by `angle` (180 deg folds them shut) with an inside `radius`. The side to the left of the line, seen from the side the sketch faces and walking from its start to its end, moves; `otherSide` moves the other one, and `under` folds away from the side the sketch faces. Every layer folds with its own radius, the stitches and the pieces lying wholly on the moving side ride along.

```json plan:key-fob
{
  "summary": "Key fob strap 120 x 25, folded in half",
  "steps": [
    {
      "label": "Strap and crease",
      "commands": [
        { "type": "document.rename", "name": "Key fob" },
        {
          "type": "sketch.template",
          "id": "$outline",
          "name": "Strap",
          "template": {
            "kind": "rectangle",
            "width": "120 mm",
            "depth": "25 mm"
          }
        },
        {
          "type": "feature.create",
          "id": "$strap",
          "featureType": "io.moket.leather.piece",
          "name": "Strap",
          "params": {
            "profile": { "sketch": "$outline" },
            "body": "$strapBody",
            "thickness": "1.4 mm"
          }
        },
        {
          "type": "feature.create",
          "id": "$crease",
          "featureType": "core.sketch",
          "name": "Crease",
          "params": {
            "plane": {
              "kind": "face",
              "ref": { "kind": "face", "name": "f($strap:end)" }
            },
            "entities": [
              { "id": "$a", "kind": "point", "x": 0, "y": -20, "fixed": true },
              { "id": "$b", "kind": "point", "x": 0, "y": 20, "fixed": true },
              {
                "id": "$creaseLine",
                "kind": "line",
                "start": "$a",
                "end": "$b"
              }
            ],
            "constraints": []
          }
        }
      ]
    },
    {
      "label": "Fold the left half over",
      "commands": [
        {
          "type": "feature.create",
          "id": "$fold",
          "featureType": "io.moket.leather.fold",
          "name": "Fold",
          "params": {
            "line": { "sketch": "$crease", "curves": ["$creaseLine"] },
            "bodies": ["$strapBody"],
            "angle": "180 deg",
            "radius": "1 mm"
          }
        }
      ]
    }
  ]
}
```

Draw the crease as a `core.sketch` on the top layer's face with two fixed points and a line between them; placeholders work as entity ids. The line here runs up the middle, so the left half folds over onto the right. Fold last, after every stitch, hole and allowance: the pattern shows each piece flat as cut. A fold whose pieces need more room warns `The pieces inside the fold need an inside radius of at least ... mm`: raise the radius to it, never leave the warning. Folds are geometric: the leather does not stretch or thin.

## A wallet

A bifold wallet is an outer, a liner stacked on it, and pockets stacked on the liner on each side of the fold:

1. Parameters for the closed size, thicknesses, margin and pitch; the outer and the liner the open size (twice the closed width).
2. Pockets on the liner, each stack on the one under it, each pocket a little lower than the one it covers so the cards show.
3. A stitch line round each pocket stack, through the stack and the liner and outer under it; a stitch line round the liner through the liner and the outer. Compare the counts of seams that must match.
4. The fold on the liner's top face at the middle, through every piece, 180 deg, with the radius it asks for.

## The pattern and plugins of the user

- The user exports the pattern (the Leather workbench's **Export pattern**: tiled PDF, PDF, SVG and DXF at 1:1). You cannot; name the pieces (`name`) and their `count` so the pattern reads well.
- A user's own plugin runs only in a browser tab: on Moket's server its features are described, and adding or changing one answers `PLUGIN_NEEDS_TAB`. Ask the user to open Moket in a tab, then call again.
- A plugin the user does not have on shows `missing` in `inspect_document`: its features keep their last result, frozen, and cannot be changed.
