feat(markdown): Ported pattern guide to v3
This commit is contained in:
parent
ff8774463e
commit
3fcf229c3c
9 changed files with 1015 additions and 73 deletions
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
title: Configuration
|
|
||||||
order: 10
|
|
||||||
---
|
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="config">
|
|
||||||
|
|
||||||
The pattern configuration holds important information about the pattern
|
|
||||||
|
|
||||||
</Example>
|
|
||||||
|
|
||||||
A pattern's [configuration](/reference/api/config/) is created by the pattern designer
|
|
||||||
and details a number of important things about the pattern, like:
|
|
||||||
|
|
||||||
- The **measurements** that are required to draft the pattern
|
|
||||||
- The different **parts** in the pattern and how they depend on each other
|
|
||||||
- The different **options** that are available to tweak the pattern
|
|
||||||
|
|
||||||
The configuration is part of the pattern's code. It is created by the designer and
|
|
||||||
it is the same for everybody using the pattern.
|
|
||||||
In other words, you cannot change the configuration. Instead, the configuration
|
|
||||||
specifies what kind of settings the pattern accepts.
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
title: How patterns work
|
title: Pattern guide
|
||||||
---
|
---
|
||||||
|
|
||||||
This short guide will illustrate and explain how patterns work in FreeSewing.
|
This short guide will illustrate and explain how patterns work in FreeSewing.
|
||||||
|
@ -11,19 +11,188 @@ pattern is generated by FreeSewing.
|
||||||
This illustration is a good starting point to gain a better
|
This illustration is a good starting point to gain a better
|
||||||
understanding of the structure of a FreeSewing pattern:
|
understanding of the structure of a FreeSewing pattern:
|
||||||
|
|
||||||
<Example part="docs_overview">
|
<Example caption="A schematic overview of what goes on inside a FreeSewing pattern">
|
||||||
A schematic overview of FreeSewing
|
```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)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -52, 155, 15, true, 'lining fill-lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -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>
|
</Example>
|
||||||
|
|
||||||
|
If it looks like _a lot_ don't despair. There's a lot of repetition, and we'll
|
||||||
|
work throug the building blocks step by step.
|
||||||
|
|
||||||
If we look at our image, it can be divided into three areas:
|
If we look at our image, it can be divided into three areas:
|
||||||
|
|
||||||
- The left area with the **settings** box
|
- [The **Settings**](#the-settings) on the left.
|
||||||
- The middle area with the **Pattern** box and everything in it
|
- [The **Render stage**](#rendering-your-pattern) on the right
|
||||||
- The right area with the **draft** box and the _SVG_ and _React_ logos
|
- [The **Pattern**](#anatomy-of-a-pattern)
|
||||||
|
|
||||||
Let's take a closer look at everything that is contained within our central **Pattern** box:
|
|
||||||
|
|
||||||
<ReadMore />
|
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
|
|
||||||
|
@ -31,6 +200,74 @@ The left and right parts are all about how to integrate FreeSewing in your _fron
|
||||||
In other words, how you'll plug it into your website, or online store, or a mobile
|
In other words, how you'll plug it into your website, or online store, or a mobile
|
||||||
application.
|
application.
|
||||||
|
|
||||||
That part is outside the scope of this guide.
|
We'll briefly cover those areas in this page. But a deep-dive in those topics
|
||||||
|
is outside the scope of this guide.
|
||||||
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
|
## 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 contructor
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
<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).
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
|
||||||
|
## Rendering your pattern
|
||||||
|
|
||||||
|
By rendering our 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 compact>
|
||||||
|
This is what we use on these pages to render with React
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## 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 />
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,97 @@
|
||||||
---
|
---
|
||||||
title: Parts
|
title: Parts
|
||||||
order: 20
|
order: 120
|
||||||
---
|
---
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="Part">
|
A pattern is a container for a bunch of parts. And parts are in turn a
|
||||||
Parts divide your pattern into re-usable components
|
container for the the points, paths, and snippets of (a part of) your pattern.
|
||||||
</Example>
|
Parts can be re-used and mixed and matched to create other patterns, a powerful
|
||||||
|
concept to build a pattern library.
|
||||||
|
|
||||||
Parts are a container for the points, paths, and snippets of (a part of) your pattern.
|
If you design a T-shirt pattern with a `front`, `back`, and `sleeve`, each of
|
||||||
They are also re-usable by other patterns, which makes them a powerful tool to build
|
those would be a part. If you then wanted to make a long-sleeved version of
|
||||||
a pattern library.
|
your T-shirt pattern, you only need to design a new sleeve part. You can re-use
|
||||||
|
the `front` and `back` parts of your short-sleeved T-shirt pattern, as they did
|
||||||
If you design a T-shirt pattern with a `front`, `back`, and `sleeve`, each of those would be a part.
|
not change.
|
||||||
If you then wanted to make a long-sleeved version of your T-shirt pattern, you only need to design
|
|
||||||
a new sleeve part. You can re-use the `front` and `back` parts of your short-sleeved T-shirt pattern, as they did not change.
|
|
||||||
|
|
||||||
When developing a FreeSewing pattern, you will spend most of your time designing the individual parts.
|
When developing a FreeSewing pattern, you will spend most of your time designing the individual parts.
|
||||||
|
|
||||||
|
<Example caption="A schematic overview of parts inside a FreeSewing pattern">
|
||||||
|
```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 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) points[name + 'Mid'].addText(name, 'center')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note fill-note', 'bold')
|
||||||
|
paths['Part A (set 0)'].attr('fill-opacity', 0.2)
|
||||||
|
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-note', 'bold')
|
||||||
|
paths['Part B (set 0)'].attr('fill-opacity', 0.2)
|
||||||
|
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-note', 'bold')
|
||||||
|
paths['Part C (set 0)'].attr('fill-opacity', 0.2)
|
||||||
|
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 dashed')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -45, 155, 15, true, 'lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -34, -49, 163, 106, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,9 @@ title: Paths
|
||||||
order: 40
|
order: 40
|
||||||
---
|
---
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="Paths">
|
|
||||||
Paths are the lines and curves of your pattern
|
|
||||||
</Example>
|
|
||||||
|
|
||||||
Paths are the lines and curves that make up your pattern.
|
Paths are the lines and curves that make up your pattern.
|
||||||
|
They are made up of individual drawing operations that together make up the path.
|
||||||
|
|
||||||
They are made up of a set of drawing operations that together make up the path.
|
|
||||||
FreeSewing supports the following types of drawing operations:
|
FreeSewing supports the following types of drawing operations:
|
||||||
|
|
||||||
- The **move** operation moves our virtual pen but does not draw anything.
|
- The **move** operation moves our virtual pen but does not draw anything.
|
||||||
|
@ -33,9 +29,92 @@ Understanding that each drawing operation builds upon the next one is an importa
|
||||||
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
|
<Example caption="A schematic overview of where paths are kept inside a FreeSewing pattern">
|
||||||
|
```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 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) points[name + 'Mid'].addText(name, 'center')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox('points', -24, -24, 38, 12, true, 'note')
|
||||||
|
drawBox('paths', -24, -9, 38, 12, true, 'note fill-note')
|
||||||
|
paths['paths'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('snippets', -24, 6, 38, 12, true, 'note')
|
||||||
|
|
||||||
|
drawBox('Part B (set 0)', 26, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox(' points ', 29, -24, 38, 12, true, 'note')
|
||||||
|
drawBox(' paths ', 29, -9, 38, 12, true, 'note fill-note')
|
||||||
|
paths[' paths '].attr('fill-opacity', 0.2)
|
||||||
|
drawBox(' snippets ', 29, 6, 38, 12, true, 'note')
|
||||||
|
|
||||||
|
drawBox('Part C (set 0)', 79, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox(' points ', 82, -24, 38, 12, true, 'note')
|
||||||
|
drawBox(' paths ', 82, -9, 38, 12, true, 'note fill-note')
|
||||||
|
paths[' paths '].attr('fill-opacity', 0.2)
|
||||||
|
drawBox(' snippets ', 82, 6, 38, 12, true, 'note')
|
||||||
|
|
||||||
|
drawBox('setStore 0', -24, 21, 144, 12, true, 'lining dashed')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -45, 155, 15, true, 'lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -34, -49, 163, 106, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
|
|
||||||
Our example image (which, if you hadn't realized was created with FreeSewing) has a lot of
|
Our example image (which, if you hadn't realized was created with FreeSewing) has a lot of
|
||||||
paths in it. Each box, the arrows, the lines in the React logo, and so on.
|
paths in it. Each box, the arrows, the lines in the React logo, and so on.
|
||||||
|
|
||||||
|
Click the **X-Ray** tab to reveal them.
|
||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,6 @@ title: Points
|
||||||
order: 30
|
order: 30
|
||||||
---
|
---
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="Points">
|
|
||||||
Points store coordinates
|
|
||||||
</Example>
|
|
||||||
|
|
||||||
Developing a pattern with FreeSewing is similar to doing it on paper.
|
Developing a pattern with FreeSewing is similar to doing it on paper.
|
||||||
But instead of using a pencil and paper, you'll be writing code.
|
But instead of using a pencil and paper, you'll be writing code.
|
||||||
|
|
||||||
|
@ -29,9 +25,92 @@ may intuitively expect.
|
||||||
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
|
<Example caption="A schematic overview of where points are kept inside a FreeSewing pattern">
|
||||||
|
```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 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) points[name + 'Mid'].addText(name, 'center')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox('points', -24, -24, 38, 12, true, 'note fill-note')
|
||||||
|
paths['points'].attr('fill-opacity', 0.2)
|
||||||
|
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', 'bold')
|
||||||
|
drawBox(' points ', 29, -24, 38, 12, true, 'fill-note note')
|
||||||
|
paths[' points '].attr('fill-opacity', 0.2)
|
||||||
|
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', 'bold')
|
||||||
|
drawBox(' points ', 82, -24, 38, 12, true, 'note fill-note')
|
||||||
|
paths[' points '].attr('fill-opacity', 0.2)
|
||||||
|
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 dashed')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -45, 155, 15, true, 'lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -34, -49, 163, 106, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
|
|
||||||
Our example image (which, if you hadn't realized was created with FreeSewing) has a lot of
|
Our example image (which, if you hadn't realized was created with FreeSewing) has a lot of
|
||||||
points in it. The corners of the boxes, the location where the text goes, and so on.
|
points in it. The corners of the boxes, the location where the text goes, and so on.
|
||||||
|
|
||||||
|
Click the **X-Ray** tab to reveal them.
|
||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
|
||||||
|
|
340
markdown/dev/guides/patterns/sets/en.md
Normal file
340
markdown/dev/guides/patterns/sets/en.md
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
---
|
||||||
|
title: Sets
|
||||||
|
order: 100
|
||||||
|
---
|
||||||
|
|
||||||
|
When we refer to a _set_ we mean a _set of settings_.
|
||||||
|
In the vast majority of cases, there is only one set of settings. So we just
|
||||||
|
refer to them as _the settings_. But FreeSewing supports instantiating a
|
||||||
|
pattern with multiple sets of settings. We refer to this as **multiset
|
||||||
|
support**.
|
||||||
|
|
||||||
|
## Multiset support
|
||||||
|
|
||||||
|
Multiset support underpins features such as
|
||||||
|
[sampling](/reference/api/pattern/sample) and in general can be used to
|
||||||
|
_combine_ multiple drafted variants into a single pattern container.
|
||||||
|
|
||||||
|
Here's a simple example:
|
||||||
|
|
||||||
|
<Example settings="sample: { type: measurement, measurement: head }" withHead caption="A simple example of sampling the `head` measurement">
|
||||||
|
```js
|
||||||
|
({
|
||||||
|
Point,
|
||||||
|
points,
|
||||||
|
Path,
|
||||||
|
paths,
|
||||||
|
Snippet,
|
||||||
|
snippets,
|
||||||
|
measurements,
|
||||||
|
part
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const size = measurements.head
|
||||||
|
paths.box = new Path()
|
||||||
|
.move(new Point(0,0))
|
||||||
|
.line(new Point(0, size/3))
|
||||||
|
.line(new Point(size, size/3))
|
||||||
|
.line(new Point(size, 0))
|
||||||
|
.addClass('fabric')
|
||||||
|
.close()
|
||||||
|
points.logo = new Point(size/2, size/5)
|
||||||
|
snippets.logo = new Snippet('logo', points.logo)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
When drafting for multiple sets of settings, it's important to keep the different draft variants from cross-contaminating each other.
|
||||||
|
To ensure this, the core library will:
|
||||||
|
|
||||||
|
- Set up each pattern part per set
|
||||||
|
- Provide a per-set store that is shared between parts in the set
|
||||||
|
|
||||||
|
This is illustrated below:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Example caption="A schematic overview of what goes on inside a FreeSewing pattern">
|
||||||
|
```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')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stacks
|
||||||
|
drawBox('Stack 0', -30, -33, 50, 169, 'b', 'mark', 'fill-mark')
|
||||||
|
paths['Stack 0'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Stack 1', 23, -33, 50, 169, 'b', 'mark', 'fill-mark')
|
||||||
|
paths['Stack 1'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Stack 2', 76, -33, 50, 169, 'b', 'mark', 'fill-mark')
|
||||||
|
paths['Stack 2'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Sets
|
||||||
|
drawBox('Set 0', -33, -30, 174, 76, 'r', 'contrast fill-contrast', 'bold text-lg fill-contrast')
|
||||||
|
paths['Set 0'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Set 1', -33, 50, 174, 76, 'r', 'contrast fill-contrast', 'bold text-lg fill-contrast')
|
||||||
|
paths['Set 1'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note')
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
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-various')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Parts set 1
|
||||||
|
drawBox('Part A (set 1)', -27, 53, 44, 70, 'b', 'note')
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
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-various')
|
||||||
|
paths['setStore 1'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -52, 155, 15, true, 'lining', 'fill-lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -43, -59, 195, 216, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
## One set is plenty
|
||||||
|
|
||||||
|
In the vast majority of cases, a pattern will only have one set of settings. As such, multiset support is not something you need to be intimately familiar with.
|
||||||
|
But it is good to know it exists, and explain certain things that might seem _odd_ if you are unaware of multiset support, such as the fact that there's a pattern-wide store, and a different store per set, the so-called setStore(s).
|
||||||
|
|
||||||
|
Below is an illustration of a pattern with a single set of settings which, once again, is the vast majority of use cases:
|
||||||
|
|
||||||
|
<Example caption="A schematic overview of what goes on inside a FreeSewing pattern in a typical use-case: a single set of settings">
|
||||||
|
```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')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stacks
|
||||||
|
drawBox(' Stack 0', -30, -33, 50, 89, 'b', 'mark', 'fill-mark')
|
||||||
|
drawBox(' Stack 1', 23, -33, 50, 89, 'b', 'mark', 'fill-mark')
|
||||||
|
drawBox(' Stack 2', 76, -33, 50, 89, 'b', 'mark', 'fill-mark')
|
||||||
|
|
||||||
|
// Sets
|
||||||
|
drawBox('Set 0', -33, -30, 174, 76, 'r', 'contrast fill-contrast', 'bold text-lg fill-contrast')
|
||||||
|
paths['Set 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note')
|
||||||
|
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')
|
||||||
|
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')
|
||||||
|
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-various')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -52, 155, 15, true, 'lining', 'fill-lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox(' Pattern', -43, -59, 195, 128, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,6 @@ title: Snippets
|
||||||
order: 50
|
order: 50
|
||||||
---
|
---
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="Snippets">
|
|
||||||
Snippets are little embelishments that go on your pattern
|
|
||||||
</Example>
|
|
||||||
|
|
||||||
Snippets are little embellishments you can use and re-use on your pattern.
|
Snippets are little embellishments you can use and re-use on your pattern.
|
||||||
They are typically used for things like logos or buttons.
|
They are typically used for things like logos or buttons.
|
||||||
|
|
||||||
|
@ -15,9 +11,110 @@ Each snippet must have:
|
||||||
- An anchor point that determine where the snippet will be located
|
- An anchor point that determine where the snippet will be located
|
||||||
- The name of the snippet to insert
|
- The name of the snippet to insert
|
||||||
|
|
||||||
Since our example image does not have any snippets in it, here's another example
|
<Example caption="A schematic overview of where snippets are kept inside a FreeSewing pattern">
|
||||||
of a `button`, `buttonhole`, and `logo` snippet added to a FreeSewing pattern:
|
```mjs
|
||||||
|
({ Point, points, Path, paths, options, part }) => {
|
||||||
|
|
||||||
<Example part="snippet">
|
// Draws a w*h box, returns a Path object
|
||||||
An example of the use of snippets
|
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 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) points[name + 'Mid'].addText(name, 'center')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts set 0
|
||||||
|
drawBox('Part A (set 0)', -27, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox('points', -24, -24, 38, 12, true, 'note')
|
||||||
|
drawBox('paths', -24, -9, 38, 12, true, 'note')
|
||||||
|
drawBox('snippets', -24, 6, 38, 12, true, 'note fill-note')
|
||||||
|
paths['snippets'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
drawBox('Part B (set 0)', 26, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox(' points ', 29, -24, 38, 12, true, 'note')
|
||||||
|
drawBox(' paths ', 29, -9, 38, 12, true, 'note')
|
||||||
|
drawBox(' snippets ', 29, 6, 38, 12, true, 'note fill-note')
|
||||||
|
paths[' snippets '].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
drawBox('Part C (set 0)', 79, -27, 44, 70, 'b', 'note', 'bold')
|
||||||
|
drawBox(' points ', 82, -24, 38, 12, true, 'note')
|
||||||
|
drawBox(' paths ', 82, -9, 38, 12, true, 'note')
|
||||||
|
drawBox(' snippets ', 82, 6, 38, 12, true, 'note fill-note')
|
||||||
|
paths[' snippets '].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
drawBox('setStore 0', -24, 21, 144, 12, true, 'lining dashed')
|
||||||
|
paths['setStore 0'].attr('fill-opacity', 0.2)
|
||||||
|
|
||||||
|
// Pattern
|
||||||
|
drawBox('Pattern Store', -30, -45, 155, 15, true, 'lining')
|
||||||
|
paths['Pattern Store'].attr('fill-opacity', 0.2)
|
||||||
|
drawBox('Pattern', -34, -49, 163, 106, 'b', 'fabric stroke-lg', 'text-lg bold')
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
</Example>
|
</Example>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
|
||||||
|
Our example image (which, if you hadn't realized was created with FreeSewing) has a lot of
|
||||||
|
paths in it. Each box, the arrows, the lines in the React logo, and so on.
|
||||||
|
|
||||||
|
Click the **X-Ray** tab to reveal them.
|
||||||
|
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
Since our example image does not have any snippets in it, here's an example
|
||||||
|
of a `button` snippet:
|
||||||
|
|
||||||
|
<Example caption="An example of the button snippet">
|
||||||
|
```js
|
||||||
|
({ Point, Path, paths, Snippet, snippets, part }) => {
|
||||||
|
|
||||||
|
snippets.demo = new Snippet('button', new Point(0,0))
|
||||||
|
|
||||||
|
// Prevent clipping
|
||||||
|
paths.diag = new Path()
|
||||||
|
.move(new Point(-50,-4))
|
||||||
|
.move(new Point(50,4))
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
|
57
markdown/dev/guides/patterns/stacks/en.md
Normal file
57
markdown/dev/guides/patterns/stacks/en.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
title: Stacks
|
||||||
|
order: 110
|
||||||
|
---
|
||||||
|
|
||||||
|
Stacks come into play when layouting a pattern.
|
||||||
|
The FreeSewing core library, by default, will handle the layout of a pattern
|
||||||
|
for you by placing all parts next to each other in as small a space as
|
||||||
|
possible.
|
||||||
|
|
||||||
|
That is _typically_ what you want, but not always. For example, when sampling,
|
||||||
|
you want parts to be stacked on top of each other:
|
||||||
|
|
||||||
|
<Example settings="sample: { type: measurement, measurement: head }" withHead caption="A simple example of sampling the `head` measurement">
|
||||||
|
```js
|
||||||
|
({
|
||||||
|
Point,
|
||||||
|
points,
|
||||||
|
Path,
|
||||||
|
paths,
|
||||||
|
Snippet,
|
||||||
|
snippets,
|
||||||
|
measurements,
|
||||||
|
part
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const size = measurements.head
|
||||||
|
paths.box = new Path()
|
||||||
|
.move(new Point(0,0))
|
||||||
|
.line(new Point(0, size/3))
|
||||||
|
.line(new Point(size, size/3))
|
||||||
|
.line(new Point(size, 0))
|
||||||
|
.addClass('fabric')
|
||||||
|
.close()
|
||||||
|
points.logo = new Point(size/2, size/5)
|
||||||
|
snippets.logo = new Snippet('logo', points.logo)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Example>
|
||||||
|
|
||||||
|
Under the hood, sampling uses multiple sets of settings, and then uses stacks
|
||||||
|
to place them on top of each other. But this functionality is also available
|
||||||
|
to patterns designers who want to use it.
|
||||||
|
|
||||||
|
Essentially, stacks behave as layers. Parts that are on the same _stack_ will be stacked on top of each other in the layout.
|
||||||
|
|
||||||
|
You can stack parts from the same set, or from different sets.
|
||||||
|
|
||||||
|
<Fixme>Include code example</Fixme>
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
In the vast majority of cases you won't be using any stacks, or the stacking
|
||||||
|
will be handled for you by the core library (like in the sampling example
|
||||||
|
above).
|
||||||
|
</Note>
|
|
@ -3,25 +3,21 @@ title: Store
|
||||||
order: 60
|
order: 60
|
||||||
---
|
---
|
||||||
|
|
||||||
<Example part="docs_overview" options_focus="Store">
|
The store in a FreeSewing pattern provides shared key-value storage.
|
||||||
The store provides pattern-wide key/value storage
|
|
||||||
</Example>
|
|
||||||
|
|
||||||
The store provides key-value storage that is shared across your pattern.
|
|
||||||
|
|
||||||
If you have some information in one part that you want to make available
|
If you have some information in one part that you want to make available
|
||||||
outside that part (in another part) you can save it to the store.
|
outside that part (in another part) you can save it to the store.
|
||||||
|
|
||||||
A **Store** object holds a simple key/value store with methods for storing and retrieving information.
|
There are two types of stores:
|
||||||
|
|
||||||
|
- A pattern-wide store that
|
||||||
|
- A store per set that is shared across parts in that set
|
||||||
|
|
||||||
## Note
|
When you interact with a store in your part code, it is almost certainly the
|
||||||
|
so-called setStore, the store that is shared accross parts in the set.
|
||||||
|
|
||||||
|
The pattern-wide store is used for pattern initialization and storing logs and
|
||||||
|
other data in the early stages of the pattern lifecycle.
|
||||||
|
|
||||||
A store is typically used to share information between parts. For example
|
|
||||||
the length of the neck opening in one part can be used to calculate the
|
|
||||||
length for the collar in another part.
|
|
||||||
All patterns have access to
|
|
||||||
|
|
||||||
<Example caption="An overview of different stores within a pattern">
|
<Example caption="An overview of different stores within a pattern">
|
||||||
```js
|
```js
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue