1
0
Fork 0
freesewing/sites/dev/docs/guides/designs
Joost De Cock ab3204f9f1 chore: Port FreeSewing.dev to docusaurus
The replaces the NextJS site powering FreeSewing.dev with a Docusaurus
setup. It's part of my efforts to simplify FreeSewing's setup so we can
focus on our core value proposition.
2024-09-28 13:13:48 +02:00
..
parts chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
paths chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
pattern chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
points chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
sets chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
snippets chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
stacks chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
store chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00
readme.mdx chore: Port FreeSewing.dev to docusaurus 2024-09-28 13:13:48 +02:00

---
title: Design guide
sidebar_position: 30
---

Hi there and welcome to this guide that explains how FreeSewing designs work.


it's about what goes on under the hood each time a sewing
pattern is generated by FreeSewing.

This illustration is a good starting point to gain a better
understanding of the structure of a FreeSewing design:

<Example caption="A schematic overview of what goes on inside a FreeSewing design">
```mjs
({ Point, points, Path, paths, options, part }) => {

  // Draws a w*h box, returns a Path object
  const box = (name, origin, width, height, classes='fabric') => {
    let base = height
    if (width < height) base = width
    let t = base
    points[name + 'TopLeft'] = new Point(origin.x, origin.y)
    points[name + 'BottomLeft'] = new Point(origin.x, origin.y + height)
    points[name + 'BottomRight'] = new Point(
      origin.x + width,
      origin.y + height
    )
    points[name + 'TopRight'] = new Point(origin.x + width, origin.y)
    points[name + 'Mid'] = points[name + 'TopLeft'].shiftFractionTowards(
      points[name + 'BottomRight'],
      0.5
    )
    points[name + 'Mid'].y += 3

    return new Path()
      .move(points[name + 'TopLeft'])
      .line(points[name + 'BottomLeft'])
      .line(points[name + 'BottomRight'])
      .line(points[name + 'TopRight'])
      .line(points[name + 'TopLeft'])
      .close()
      .addClass(classes)
  }

  // Draws and arrow from to and places text
  const arrow = (name, text = '') => {
    let from = points[name + 'From']
    let to = points[name + 'To']
    from = from.shiftTowards(to, 2)
    to = to.shiftTowards(from, 2)
    const base = from.dist(to)
    const r = 3
    points[name + 'Tip1'] = to.shiftTowards(from, 3.5).rotate(r, from)
    points[name + 'Tip2'] = to.shiftTowards(from, 3.5).rotate(r * -1, from)
    const path = new Path()
      .move(from)
      .line(to)
      .move(points[name + 'Tip1'])
      .line(to)
      .line(points[name + 'Tip2'])
      .addClass('lining stroke-lg')
    if (options.focus === name) path.addClass('note')

    return text
      ? path.addText('  ' + text, options.focus === name ? 'fill-note center' : 'center')
      : path
  }

  // Draws a box and handled text placement
  const drawBox = (name, x, y, width, height, text=true, classes, textClasses='') => {
    points[name + 'Origin'] = new Point(x, y)
    paths[name] = box(name, points[name + 'Origin'], width, height, classes)
    if (text === 'b') {
      paths[name+'_label'] = new Path()
        .move(points[name+'BottomLeft'])
        .line(points[name+'BottomRight'])
        .addClass('hidden')
        .addText(name, 'center ' + textClasses)
        .attr('data-text-dy', -1)
    }
    else if (text === 't') {
      paths[name+'_label'] = new Path()
        .move(points[name+'TopLeft'])
        .line(points[name+'TopRight'])
        .addClass('hidden')
        .addText(name, 'center ' + textClasses)
        .attr('data-text-dy', 11)
    }
    else if (text === 'r') {
      paths[name+'_label'] = new Path()
        .move(points[name+'BottomRight'])
        .line(points[name+'TopRight'])
        .addClass('hidden')
        .addText(name, 'center ' + textClasses)
        .attr('data-text-dx', -5)
    }
    else if (text) points[name + 'Mid'].addText(name, 'center')
  }

  // Settings
  drawBox('Settings Set 0', -140, -18, 50, 20, 'Settings 0', 'fabric fill-bg')
  drawBox('Settings Set 1', -140, 6, 50, 20, 'Settings 0', 'fabric fill-bg')
  const sname = 'Settings'
  drawBox(sname, -145, -24, 61, 65, 'b', 'fabric', 'text-lg bold')
  points.arrowDraftFrom = new Point(-84,15)
  points.arrowDraftTo = new Point(-45, 15)
  paths.arrowDraft = arrow('arrowDraft', 'draft()')

  // Stacks
  drawBox('Stack 0', -30, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
  paths['Stack 0'].attr('fill-opacity', 0.2)
  drawBox('Stack 1', 23, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
  paths['Stack 1'].attr('fill-opacity', 0.2)
  drawBox('Stack 2', 76, -33, 50, 169, 'b', 'mark fill-mark', 'fill-mark')
  paths['Stack 2'].attr('fill-opacity', 0.2)

  // Sets
  drawBox('Set 0', -33, -30, 174, 76, 'r', 'contrast fill-contrast', 'fill-contrast')
  paths['Set 0'].attr('fill-opacity', 0.2)
  drawBox('Set 1', -33, 50, 174, 76, 'r', 'contrast fill-contrast', 'fill-contrast')
  paths['Set 1'].attr('fill-opacity', 0.2)

  // Parts set 0
  drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note fill-bg')
  drawBox('points', -24, -24, 38, 12, true, 'note')
  drawBox('paths', -24, -9, 38, 12, true, 'note')
  drawBox('snippets', -24, 6, 38, 12, true, 'note')

  drawBox('Part B (set 0)', 26, -27, 44, 70, 'b', 'note fill-bg')
  drawBox(' points ', 29, -24, 38, 12, true, 'note')
  drawBox(' paths ', 29, -9, 38, 12, true, 'note')
  drawBox(' snippets ', 29, 6, 38, 12, true, 'note')

  drawBox('Part C (set 0)', 79, -27, 44, 70, 'b', 'note fill-bg')
  drawBox('  points  ', 82, -24, 38, 12, true, 'note')
  drawBox('  paths  ', 82, -9, 38, 12, true, 'note')
  drawBox('  snippets  ', 82, 6, 38, 12, true, 'note')

  drawBox('setStore 0', -24, 21, 144, 12, true, 'lining fill-lining dashed', 'fill-various')
  paths['setStore 0'].attr('fill-opacity', 0.2)

  // Parts set 1
  drawBox('Part A (set 1)', -27, 53, 44, 70, 'b', 'note fill-bg')
  drawBox(' points', -24, 56, 38, 12, true, 'note')
  drawBox(' paths', -24, 71, 38, 12, true, 'note')
  drawBox(' snippets', -24, 86, 38, 12, true, 'note')

  drawBox('Part B (set 1)', 26, 53, 44, 70, 'b', 'note fill-bg')
  drawBox('  points ', 29, 56, 38, 12, true, 'note')
  drawBox('  paths ', 29, 71, 38, 12, true, 'note')
  drawBox('  snippets ', 29, 86, 38, 12, true, 'note')

  drawBox('Part C (set 1)', 79, 53, 44, 70, 'b', 'note fill-bg')
  drawBox('   points  ', 82, 56, 38, 12, true, 'note')
  drawBox('   paths  ', 82, 71, 38, 12, true, 'note')
  drawBox('   snippets  ', 82, 86, 38, 12, true, 'note')

  drawBox('setStore 1', -24, 101, 147, 12, true, 'lining fill-lining dashed', 'fill-various')
  paths['setStore 1'].attr('fill-opacity', 0.2)

  // Design
  drawBox('Pattern Store', -30, -52, 155, 15, true, 'lining fill-lining')
  paths['Pattern Store'].attr('fill-opacity', 0.2)
  drawBox('Design', -43, -59, 195, 216, 'b', 'fabric stroke-lg', 'text-lg bold')

  // Render
  points.arrowSvgFrom = new Point(154,15)
  points.arrowSvgTo = new Point(225, -15)
  paths.arrowSvg = arrow('arrowSvg', 'render()')
  points.arrowReactFrom = new Point(154,15)
  points.arrowReactTo = new Point(225, 45)
  paths.arrowReact = arrow('arrowReact', 'getRenderProps()')
  drawBox('Render stage', 190, -52, 105, 175, 'b', 'interfacing lashed', 'text-lg bold')

  // Render logos
  points.svg = new Point(225, -15).addText('SVG', 'text-lg bold')
  points.js = new Point(225, 45).addText("React\nSvelte\nVue\nJS\n...", 'text-lg')

  // Prevent clipping
  paths.unclip = new Path().move(new Point(260, -80))

  return part
}
```
</Example>

If it looks like _a lot_ don't despair. I included all the info on the
graphical overview, but most of it you can safely ignore and consider _under
the hood_ unless you want to do some really advanced things with FreeSewing.

If we look at our image through squinted eyes, we can identify three areas:

- The[ **Settings**](#the-settings) on the left
- The[ **Render stage**](#rendering-your-pattern) on the right
- The[ **Pattern**](#the-pattern) in the center

:::note

The left and right parts are all about how to integrate FreeSewing in your _frontend_.
In other words, how you'll plug it into your website, or online store, or a mobile
application.

We'll briefly cover those areas in this page. But a deep-dive into those topics
is outside the scope of this guide.

:::

## The settings

On the left, we have the **settings** box. It represents the
[settings](/reference/settings) that are provided by the user.

Most of the settings are the same for all designs generated with FreeSewing as they are provided by the core library.

Noteworthy exceptions are the [`measurements`](/reference/settings/measurements)
and [`options`](/reference/settings/options) keys as they are defined by the pattern design.

In other words, different patterns will required different measurements and provide different options.
But all patterns will allow users to set the [`units`](/reference/settings/units) (for example).

:::tip
Settings are provided by passing them to the [Pattern constructor](/reference/api/pattern)
:::

:::note
##### Multiset support

Since version 3 of FreeSewing, you can pass multiple sets of settings to `Pattern.draft()`.
We call this _multset support_.

In 99% of the cases, you will only have a single set of settings, and you can mostly forget about this feature.
It is typically used to compare different drafts or to draft for different sets of measurements (for example).
:::


## Rendering your pattern

By rendering the pattern, we mean to generate the output to show to the user.

This can be done in two different ways:

### Render to SVG

The core library ships with a renderer that will render your pattern to SVG.

To use it, call [`Pattern.render()`](/reference/api/pattern/render) which will return an SVG string.

### BYOR (bring your own renderer)

If you'd like more fine-grained control over the output, you can use your own renderer.

To do so, call [`Pattern.getRenderProps()`](/reference/api/pattern/getrenderprops)
which will return all the data you need to render your pattern in the way you prefer.

:::note
This is what we use on these documentation pages to render with React
:::

## The pattern

Last but not least, we've arrived at the central item: the pattern itself.
The pattern is a container that holds all your parts, along with a
(pattern-wide) store.

In reality, your pattern is a _constructor_ that takes the user's settings as
input and will return an instantiated pattern.

That pattern instance will have a `draft()` method which will do the actual work of
drafting the pattern. Once drafted, the pattern can be rendered.

While the pattern does a lot of heavy lifting behind the scenes, we can content
ourselves by understanding its different building blocks:

<ReadMore />