
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.
277 lines
9.9 KiB
Text
277 lines
9.9 KiB
Text
---
|
|
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 />
|
|
|