1
0
Fork 0

Merge branch 'develop' into eriese-fix-builds

This commit is contained in:
Enoch Riese 2022-10-02 22:36:50 -05:00
commit 14db0afec0
219 changed files with 2228 additions and 4119 deletions

View file

@ -1,14 +0,0 @@
---
title: FreeSewing v3 Migration Guide
---
## About this guide
This guide is intended for people who are using FreeSewing v2 and want to know what changes are required for FreeSewing version 3.
If your experience with/exposure to FreeSewing started with version 3, you can safely ignore this guide.
## Part-based configuration
### Motivation
Onn

View file

@ -1,14 +1,147 @@
---
title: Migration guide
title: FreeSewing version 3
---
- named exports
- ESM only
Since August 2022, we've been working on the next major version of FreeSewing: version 3 (v3).
This guides gives a high level overview of the changes that are going into v3, why we're making them, and what to expect.
If you're looking for a more hands-on list of changes between v2 and v3, please refer to [the v3 migration guide](/guides/v3/migration).
Once v3 is in production, these guides will become less relevant. For the time being, that is not the case though, so things could still change. That being said, here's the inside scoop on v3.
## Why a version 3 in the first place?
Before diving into the details, let's take a moment to go over some of the reasons why we decided to start work on v3, rather than put keep working on the v2 branch of FreeSewing.
FreeSewing practices [semantic versioning](https://semver.org/), which means that any breaking changes require a new major version.
In more practical terms, it means that new features or ideas that would require breaking changes are put on the back burner until enough of them accumulate that they start making a compelling case for putting out a new major version.
For FreeSewing v3, [our contributor call on 20 August 2022](https://github.com/freesewing/freesewing/discussions/2582) marks the moment that we decided enough of our plans and ambitions were being held back by our inability to introduce breaking changes that we decided to turn the page and start working on v3.
## High-level goals
### Support for packs
The main driver for v3 was what was listed on [our roadmap](https://github.com/freesewing/freesewing/discussions/1278) as *Support for packs*. The idea initially focussed on publishing collections of re-usable pattern parts such as a *sleeve pack* or a *collar pack* and so on. You would then be able to create new designs by pulling in a collar from the collar pack, some sleeves from the sleeve pack and so on.
As we itterated on this idea, it became clear that there was little added value in creating another type of container (a *pack*) and that it would be better of we made it easier to treat each design as a *pack*. In other words, allow people to combine parts from different designs in a seamless way.
Design inheritance was already possible in v2, but because the configuration was handled on the design level, it required careful re-confiruration of (required) measuremetns, options, part dependencies, and so on. It was possible, but came with a lot of friction.
So in v3, all configuration is moved to the part level, and a design is now not much more than a container object for a collection of parts.Its the parts themselves that configure what they need. Anything from the measurements they require, the options they provide, the plugins they use, and so on.
This way, you can pull a part out of a design and all of its configuration, dependencies, plugins, and so on will follow it.
This migration of the configuration to the part level (from the design level) is the biggest and most fundamental change between v2 and v3. It is also where most of the work needs to be done to port existing designs from v2 to v3.
### Improved developer experience.
In version 2, we ship both CJS and ESM modules, and we rely on default exports.
In version 3, FreeSewing is ESM-only and we have also migrated to only use named exports in the code we publish as they provide better IDE integration.
As part of our move to v3, we've switched from Rollup to Esbuild as our bundles, and in the process dumped Babel.
For many of our users, these choices are deep enough under the hood that they are unconcerned by them.
Together, they only speed up our builds, they also improve the developer experience, which has been another big driver in v3.
### Multisets, Stacks, and better support for sampling
Sampling is when FreeSewing drafts a number of variations of a given pattern and allows you to compare them by stacking the parts on top of each other. In v2 this was handled in a sort of hackish way as it was very much bolted on as an afterthought.
To handle this type of use-case in v3, we've added two new features that together open up a range of possibilities.
One is support for multisets, the other is support for stacks.
Multisets means you can pass a number of different sets of settings to a pattern and core will draft the pattern for each set of settings.
Stacks allow you to designate different parts of your pattern --- or parts across different sets of settings --- as belonging to the same stack. When it's time to layout your pattern, core will stack them on top of each other, a bit like layers.
These two new features not only make sampling a lot more straight-forward, they also allow other possibilities such as drafting a pattern for two sets of measurements which can be handy when dealing with an assymetric body for example.
### Provide more generic extending capabilities, rather than tight-coupling with our frontend
This is something that works on two different levels.
One one hand, we've had some feature requests in v2 that were good use cases, but a bit too specific to the user's use-case for us to add them to core.
On the other hand, there are things in core and in a design's configuration that are tightly coupled to FreeSewing's own frontend (on FreeSewing.org) and not really relevant to people using our sortware for other purposes.
In v3, we wanted to get rid of the FreeSewing.org specific stuff and instead give all our users, including ourselves, the possibility to extend the software with the features they needed for frontend integration.
Designs now no longer ship with any freesewing.org specific info, but more importantly, plugins can now further extend core with *store methods*. We've also extended to available lifecycle hooks to let people hook into the drafting process at different times in a pattern's lifecycle.
We also allow overloading of both options and the store with additional information to facilitate frontend integration.
Last but not least, we've improved the logging process as well as allow people to plug in their own log handlers.
## New in the core API
The core API is -- for the most part -- unchanged in v3. What's changed is covered in [the migration guide](/guides/v3/migration).
But there's a bunch of new things, and here is the list:
### New attributes
The following attributes have been added to the core API in v3:
#### On the `Pattern` object
- [Pattern.designConfig](/reference/api/pattern#pattern-attributes)
- [Pattern.patternConfig](/reference/api/pattern#pattern-attributes)
- [Pattern.setStores](/reference/api/pattern#pattern-attributes)
### New methods
The following methods have been added to the core API in v3:
#### On the `Attributes` object
- [Attributes.addClass](/reference/api/attributes/addclass)
- [Attributes.asPropIfPrefixIs](/reference/api/attributes/aspropifprefixis)
- [Attributes.render](/reference/api/attributes/render)
- [Attributes.renderAsCss](/reference/api/attributes/renderascss)
- [Attributes.renderIfPrefixIs](/reference/api/attributes/renderifprefixis)
#### On the `Part` object
- [Part.hide](/reference/api/part/hide)
- [Part.setHidden](/reference/api/part/sethidden)
- [Part.unhide](/reference/api/part/unhide)
#### On the `Path` object
- [Path.addClass](/reference/api/path/addclass)
- [Path.addText](/reference/api/path/addtext)
- [Path.hide](/reference/api/path/hide)
- [Path.setClass](/reference/api/path/setclass)
- [Path.setText](/reference/api/path/settext)
- [Path.setHidden](/reference/api/path/sethidden)
- [Path.smurve](/reference/api/path/smurve)
- [Path.smurve_](/reference/api/path/smurve_)
- [Path.unhide](/reference/api/path/unhide)
#### On the `Pattern` object
- [Pattern.addPart](/reference/api/pattern/addPart)
- [Pattern.getConfig](/reference/api/pattern/getConfig)
new:
- point.addCircle
- point.addText
- point.setCircle
- point.setText
#### On the `Point` object
- [Point.addCircle](/reference/api/point/addcircle)
- [Point.addText](/reference/api/point/addtext)
- [Point.setCircle](/reference/api/point/setcircle)
- [Point.setText](/reference/api/point/settext)
#### On the `Store` object
- [Store.extend](/reference/api/store/extend)
- [Store.push](/reference/api/store/push)
- [Store.remove](/reference/api/store/remove)
## Changes for developers
- FreeSewing is now ESM-only
- We use named exports instead of default experts
- We've switched from Rollup to Esbuild for our bundler
- FreeSewing v3 requires NodeJS 16 or more recent
<Fixme compact>This guide is a work in process</Fixme>

View file

@ -0,0 +1,96 @@
---
title: V3 migration guide
---
This guide covers the migration from FreeSewing version 2 (v2) to FreeSewing
version 3 (v3). It is intended for pattern designers and developers using our
core library. But it is also a good source of information for anybody who wants
to learn more about what's changed between v2 and v3 of FreeSewing.
The focus on this guide is on our core library, our designs, our monorepo, and other topics of interest to developers.
It does not cover any changes to our website(s) or other more user-facing aspects.
## Breaking changes
### EMS only
FreeSewing is now ESM only. We no longer publish CJS modules.
To make this explicit, we now use `.mjs` as file extention for our source code.
### Named exports only
All our published packages now have only named exports, and no longer any default exports.
Please refer to [the reference documentation](/reference/api/core) to see what named exports are available.
### NodeJS 16 or more recent
FreeSewing now requires NodeJS version 16 or more recent.
<Note compact>This does not apply if you're using FreeSewing exclusively in the browser.</Note>
### Removed packages
The following packages have been removed in v3:
- @freesewing/pattern-info
- gatsby-remark-jargon: We no longer use gatsby
- remark-jargon: Use rehype-jargon instead
- @freesewing/mui-theme: We no longer use Material-UI
- @freesewing/css-theme: We now use TailwindCSS
- @freesewing/components: These were depending on Material-UI and we no longer use it
- @freesewing/utils: We no longer use these, or they are included elsewhere
- @freesewing/plugin-export-dxf: DXF is kinda garbage, you deserve better
- @freesewing/plugin-validate
## Migrating designs
### Design configuration
In v2, a design had its own confifuration which contained all the info about the design.
In v3, all of that is migrated to the part level. A design is now merely a container of parts, but also allows you to pass in additional data:
```js
import { Design } from '@freesewing/core' // Note: named export
import { myPart1, myPart2 } from './parts.mjs'
export const MyDesign = new Design({
parts: [ myPart1, myPart2 ],
data: {
anything: 'goes',
this: {
is: ['here', 'to', 'use' ]
}
}
})
```
You pass the Design contructor a single object where the only required property is the `parts` key that holds an array of part objects.
The `data` property is optional, and allows you to add data/information to the design that you can use to facilitate frontend integration or a host of other things. Anything under `data` will be made available in the pattern store.
Obviously, we still need to know what measurements the design requires, what plugins it uses, what options it offers, and so on.
All of that is now configured at the part level.
### Part configuration
In v3 of FreeSewing __all__ configuration happens at the part level.
Refer to [the part configuration docs](/reference/api/part/config) for details on configuring parts.
Apart from being attached at the part level, changes in comparison to v2 include:
- The `name` property is mandatory in v3
- The `dependencies` property v2 is named `after` in v3
- The `inject` property in v2 is named `from` in v3
- The `hide` property in v2 is now one of `hide`, `hideDependencies`, or `hideAll`
- The `plugins` property is new
### File and directory structure changes
- We no longer use a `config` folder, instead keep the config next to the parts.
- We use `.mjs` extentions rather than `.js`
<Fixme compact>This guide is a work in process</Fixme>

View file

@ -1,43 +0,0 @@
---
title: dependencies
---
The `dependencies` key in the pattern configuration file allow you to configure
dependencies between different parts of your pattern.
For example, you may only be able to draft the sleeve after having drafted the
part that contains the armhole the sleeve should fit in.
Dependencies control the order in which parts get drafted, but are also used
when users requests to [only draft some parts of a
pattern](/reference/api/settings/only).
Behind the scenes, FreeSewing will draft all dependencies, and make sure to not
render them if they were not requested.
## Structure
A plain object of `key`-`value` pairs that controls the order in which pattern
parts will get drafted.
The value can either be a string, or an array of strings.
Those strings should be part names.
You read the configuration as: `key` depends on `value`.
## Example
```js
dependencies: {
front: "back",
sleeveplacket: ["sleeve", "cuff"]
}
```
In this example:
- The `front` part depends on the `back` part
- The `sleeveplacket` part depends on the `sleeve` and `cuff` parts.
<Tip>
See [Part dependencies](/advanced/dependencies) for more in-depth information on dependencies.
</Tip>

View file

@ -1,38 +0,0 @@
---
title: Pattern configuration file
---
The pattern configuration file holds a variety of information about the
pattern, its various parts, what measurements it requires, the options it
accepts and so on.
It is part of the initial design and as such static in nature.
<Note>
This is about the pattern configuration file, used at build-time.
For run-time configuration, see [Pattern settings](/reference/api/settings).
</Note>
## Structure
The pattern configuration is a plain object with one or more of the following
properties:
<ReadMore />
## Example
Below is a minimal example. Look at [the Aaron config file][aaron] for a full example.
```js
const config = {
version: '0.0.1',
name: "sorcha",
// More configuration here
}
```
[aaron]: https://github.com/freesewing/freesewing/blob/3ca5d0edfe54c7ac20aaf3af2f3544aee72f9b99/designs/aaron/config/index.js

View file

@ -1,26 +0,0 @@
---
title: hide
---
The `hide` key in the pattern configuration file allow you to configure
parts that should be hidden by default.
_Hidden_ means that they will be drafted, but not rendered. This is
typically used for a base part on which other parts are built.
Note that hidden parts will be rendered when the user requests
to [only draft some parts of a pattern](/reference/api/settings/only)
and includes the hidden part(s).
## Structure
An array of strings that holds part names.
## Example
```js
hide: [
"base"
]
```
In the configuration above, the `base` part will be hidden by default.

View file

@ -1,30 +0,0 @@
---
title: inject
---
The `inject` key in the pattern configuration file allow you to configure
the rules for injecting one part into another.
By _injecting_ we mean that rather than starting out with a fresh part,
you'll get a part that has the points, paths, and snippets of the injected part.
## Structure
A plain object of key/value pairs of parts.
The `value` part will be injected in the `key` part.
## Example
```js
inject: {
front: "back"
}
```
In this example, the `back` part will be injected in the `front` part.
In doing so, the `front` part will start out as a copy of the `back` part.
<Tip>
See [the Howto on Part inheritance](/howtos/code/inject) for a hands-on example.
</Tip>

View file

@ -1,38 +0,0 @@
---
title: measurements
---
The `measurements` key in the pattern configuration file allows you to configure
the measurments that are required to draft the pattern.
## Structure
An array of strings where the strings are the names of the measurements
required to draft this pattern.
## Example
```js
measurements: [
"bicepsCircumference",
"centerBackNeckToWaist"
]
```
<Note>
###### Don't just make up names
See [freesewing models](https://freesewing.dev/reference/packages/models)
for a list of measurement names already used in freesewing patterns.
It is a [best practice](/guides/best-practices/reuse-measurements/) to stick to these names.
</Note>
<Related>
This configuration is for **required measurements** only.
There is a also a way to configure [optional
measurements](/reference/api/config/optionalmeasurements)
</Related>

View file

@ -1,18 +0,0 @@
---
title: name
---
The `name` key in the pattern configuration holds the name of your design.
## Structure
The value should hold a string that is also a [valid NPM package
name](https://github.com/npm/validate-npm-package-name).
## Example
```js
name: "sorcha"
```
In this example, the pattern is named **sorcha**.

View file

@ -1,29 +0,0 @@
---
title: optionalMeasurements
---
The `optionalMeasurements` key in the pattern configuration file allows
you to configure measurements that are optional to draft the pattern.
## Structure
An array of strings where the strings are the names of the optional
measurements.
## Example
```js
optionalMeasurements: [
'highBust'
]
```
<Note>
###### Why would you want optional measurements?
This is often used in combination with [the bust plugin](/reference/plugins/bust/) to
allow a pattern to be drafted to the `highBust` measurement rather than the
`chest` measurement, thereby providing better fit for people with breasts.
</Note>

View file

@ -1,64 +0,0 @@
---
title: optionGroups
---
Option groups allow you to group options together when presenting them
to the user. They also support (one) level of sub-grouping which is
useful when your design has many options.
<Note>
##### This section applies to frontend integration
When you use FreeSewing patterns via the API -- in a backend NodeJS system
or on the command line for example -- all options can be used.
The conditional display of options is intended for frontend integration.
It is what's used on FreeSewing.org and our development environment alike, but
it is not intended as a way to block access to a given option. It merely hides it.
</Note>
## Structure
Option groups are stored under the `optionGroups` key in the pattern
configuration file.
They hold a plain object where each property can hold:
- An array of strings that are the names of the options to include in the group
- A plain object whose properties hold an array of strings that are the names
of the options to include in the group. (this creates a subgroup)
## Example
```js
optionGroups: {
fit: [
'chestEase',
'waistEase',
],
style: [
'cuffStyle',
'hemStyle',
{
collar: [
'collarHeight',
'collarShape'
]
}
],
}
```
The configuration above will create the following structure:
- **fit**
- `chestEase`
- `waistEase`
- **style**
- `cuffStyle`
- `hemStyle`
- **collar**
- `collarHeight`
- `collarShape`

View file

@ -1,25 +0,0 @@
---
title: boolean
---
For options where the choice is either `true` or `false`, **on** or **off**,
or **yes** or **no**, use a boolean option.
## Structure
A boolean option is a plain object with these properties:
- `bool` : Either `true` or `false` which will be the default
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
## Example
```js
options: {
withLining: {
bool: true
}
}
```

View file

@ -1,36 +0,0 @@
---
title: constant
---
If your option is a scalar value (like a string or a number),
it will be treated as a constant. Constant options are never
exposed in the frontend, but can still be set when using FreeSewing
via the API.
## Structure
Any option holding a scalar value is a constant option.
## Example
```js
options: {
collarFactor: 4.8,
fitCollar: false,
}
```
<Tip>
##### Why would you use this?
There are typically two use-cases for constant options:
- Rather than define constants in your code, it's good practice to set
them in your configuration file. This way, people who extend your
pattern can change them if they would like to.
- A constant option can be used as a feature-flag. Enabling or disabling
parts of the code beyond the control of the end user, but accessible to
developers.
</Tip>

View file

@ -1,29 +0,0 @@
---
title: counter
---
For a given number of things, use a counter option.
Counters are for integers only. Things like number of buttons and so on.
## Structure
Your counter option should be a plain object with these properties:
- `count` : The default integer value
- `min` : The minimal integer value that's allowed
- `max` : The maximum integer value that's allowed
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
## Example
```js
options: {
butttons: {
count: 7,
min: 4,
max: 12
}
}
```

View file

@ -1,28 +0,0 @@
---
title: degrees
---
For angles, use a degree option.
## Structure
Your degree option should be a plain object with these properties:
- `deg` : The default value in degrees
- `min` : The minimul that's allowed
- `max` : The maximum that's allowed
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
## Example
```js
options: {
collarAngle: {
deg: 85,
min: 60
max: 130
}
}
```

View file

@ -1,140 +0,0 @@
---
title: options
---
The design options stored under the `options` key in the pattern configuration
file give designers flexility to make one pattern with different variations.
## The use case for (design) options
One of the things that sets FreeSewing apart is that sewing patterns are not
static. Each pattern is generated on the spot to accommodate the input
provided by the user. Input that typically includes their measurments.
This _made-to-measure_ approach is sort of _our thing_ at FreeSewing,
but why stop there?
There's a lot of things that can be left up to the user and taken into
consideration when drafting the pattern. Things like how many buttons to use,
whether or not to include pockets, shape of the collar, and so on. The only
limit really is the creativity of the designer.
The `options` section in a pattern's configuration file is what makes this
possible.
## The five option types you should know
There are the five option types that an aspiring pattern designer should be
familiar with:
1. [**boolean** options][bool] are for yes/no choices
2. [**counter** options][count] are for integer values
3. [**degree** options][deg] are for degrees
4. [**list** options][list] are for a list of possible choices
5. [**percentage** options][pct] are for percentages
<Tip>
In parametric design, percentage options are by far the most common.
They also have the most features and flexibility.
</Tip>
<Related>
For the sake of completeness, here are the two other types of options:
6. [**constant** options][const] are used as
[feature flags](https://en.wikipedia.org/wiki/Feature_toggle)
7. [**millimeter** options][const] are **deprecated** (in favor of [snapped
percentage options][snapped])
</Related>
## Features all five option types share
The five options types listed above (and the millimeter option to be complete)
share the following features:
### Default value
Each option has a default value. If the user does not specify a preference
the default value is what will be used to draft the pattern.
<Note>
How you configure the default value depends on the option type
</Note>
### Optionally hide options by configuring a `hide()` method
<Note>
##### This section applies to frontend integration
When you use FreeSewing patterns via the API -- in a backend NodeJS system
or on the command line for example -- all options can be used.
The conditional display of options is intended for frontend integration.
It is what's used on FreeSewing.org and our development environment alike, but
it is not intended as a way to block access to a given option. It merely hides it.
</Note>
By default options are shown to the user when:
- They are not a constant option
- **and**
- They are included in an optionGroup
You can further control the optional display of options by adding a method
to the `hide` key under you option, as such:
```js
myOption: {
pct: 50,
min: 0,
max: 100,
hide: function(settings) {
if (settings.measurments.chest > 100) return true
else return false
}
}
```
Your `hide` method will receive one parameter that holds the run-time confguration
of your pattern, which we typically refer to as [the settings](/reference/api/settings).
It contains among other things all measurements and options chosen by the user.
So you can make a choice whether to show the option or not.
If it's not obvious from the name, your `hide()` method you should:
- Return `true` or a truthy value to hide the option
- Return `false` or a falsy value to show the option
<Tip>
##### A `hide()` method is always present on your option
If you do not specify a `hide()` method, it will be populated with the default
`hide()` method -- which always returns `false` thus always showing the option.
In other words, the `hide()` option is always there and will always get called
to determine whether an option should be shown or not.
</Tip>
[bool]: /reference/api/config/options/bool
[const]: /reference/api/config/options/const
[count]: /reference/api/config/options/counter
[deg]: /reference/api/config/options/deg
[list]: /reference/api/config/options/list
[pct]: /reference/api/config/options/pct
[snapped]: /reference/api/config/options/pct/snap

View file

@ -1,76 +0,0 @@
---
title: list
---
Use a list option when you want to offer an array of choices.
## Structure
Your list option should be a plain object with these properties:
- `dflt` : The default for this option
- `list` : An array of available values options
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
## Example
```js
options: {
cuffStyle: {
dflt: "angledBarrelCuff",
list: [
"roundedBarrelCuff",
"angledBarrelCuff"
"straightBarrelCuff"
"roundedFrenchCuff"
"angledFrenchCuff"
"straightFrenchCuff"
]
}
}
```
## Suppress translation
In the example above, you want the different `list` options to be translated.
But sometimes, there is no need for that, like in this example from Breanna:
```js
options: {
primaryBustDart: {
list: [
'06:00',
'07:00',
'08:00',
'09:00',
'10:00',
'11:00',
'11:30',
'12:00',
'12:30',
'13:00',
'13:30',
'14:00',
'15:00',
'16:00',
'17:00',
],
dflt: '06:00',
doNotTranslate: true,
},
// More here
}
```
As you can see above, you can set the `doNotTranslate` property to `true` and to indicate this.
<Note>
##### This is not a core feature
To be clear, setting this here does not do anything in core. It's merely extra
metadata you can add on the option to facilitate frontend integration.
</Note>

View file

@ -1,52 +0,0 @@
---
title: millimeter
---
While FreeSewing supports millimeter options, we recommend
using [percentage options][1] and will not accept
contributions that use millimeter options.
## Structure
A millimeter option should be a plain object with these properties:
- `mm` : The default value in millimeter
- `min` : The minimul that's allowed
- `max` : The maximum that's allowed
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
## Example
```js
options: {
elasticWidth: {
mm: 35,
min: 5,
max: 80
}
}
```
<Comment by="joost">
##### What's wrong with millimeter options?
Millimeter options do not scale.
Parametric design is the _raison d'être_ of FreeSewing and that core belief
that things should seamlessly adapt goes out the window when you use a `mm`
option because now you have a value that will not change based on the
input measurements.
You could argue that it's fine because _you can just lower the option_
but that breaks the principle of _sensible defaults_ (aka no surprises).
The fact that you can sidestep the bullet does not mean you're not creating
a footgun.
When you need a millimeter option, reach for a [snapped
percentage option][1] instead.
</Comment>
[1]: /reference/api/config/options/pct

View file

@ -1,63 +0,0 @@
---
title: percentage
---
Percentage options are the bread and butter of freesewing.
Almost all your options will most likely be percentage options as
they ensure that your pattern will scale regardless of size.
## Structure
Your percentage option should be a plain object with these properties:
- `pct` : The default percentage
- `min` : The minimum percentage that's allowed
- `max` : The maximum percentage that's allowed
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
- `fromAbs` <small>(optional)</small> : A method to [determine the percentage based on a value in millimeter][fromabs]
- `toAbs` <small>(optional)</small> : A method to [return the option value in millimeter][toabs]
- `snap` <small>(optional)</small> : The configuration to control [snapping of percentage options][snap]
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method
[fromabs]: /reference/api/config/options/pct/fromabs
[toabs]: /reference/api/config/options/pct/toabs
[snap]: /reference/api/config/options/pct/snap
<Note>
###### Percentage options will be divided by 100 when loaded
You specify percentages in your config file. For example, `50` means 50%.
When your configuration is loaded, those percentages will be divided by 100.
So a percentage of `50` in your config file will be `0.5` when you read out that option in your pattern.
###### Percentage options are not limited to the range 0-100
The minimum and maximum (and default) percentages are not restricted to the range from `0%` to `100%`.
A percentage option that spans from `-25%` to `135%` is just as valid.
</Note>
## Example
Below is a simple example:
```js
options: {
acrossBackFactor: {
pct: 97,
min: 93,
max: 100
}
}
```
## Advanced use
Percentage options have a few more tricks up their sleeve:
<ReadMore />

View file

@ -1,97 +0,0 @@
---
title: Setting a value in millimeter as a percentage option
---
Percentage options are great for parametric desing, but not always
very intuitive for the user. For example: A user may desire 13
centimeters (5 inches) of chest ease. But what percentage should
they set the `chestEase` option to to accomplish this?
To address this common grievance, FreeSewing allows you to add a
`fromAbs` method that should take a value in millimeter and
return the percentage the option should be set to to result in this
value.
<Note>
Note that this method will not change the percentage of the option.
It will merely return return a percentage value. It is up to the
frontend designer to then either set this value, or suggest it to
the user.
</Note>
## Structure
The `fromAbs` property should hold a function with the following
signature:
```js
function toAbs(millimeter, settings) {
// return a percentage here (0.5 is 50%)
}
```
The first parameter is the desired value in millimeter (for example
`130` for `13cm`).
The second parameter is the pattern's run-time configuration
or [settings](/reference/api/settings) which holds -- among other things -- the
measurements provided by the user.
## Example
In our example above, let's say that the `chestEase` option is
a simple percentage of the `chest` measurement. Our option
configuration could like like this:
```js
chestEase: {
pct: 8,
min: 0,
max: 20,
fromAbs: function(millimeter, settings) {
return millimeter / settings.measurements.chest
}
}
```
With object destructuring and fat-arrow notation,
you can write it a bit terser like this:
```js
fromAbs: (val, { measurements }) => val /measurements.chest
```
## Using pctBasedOn for simple measurement fractions
Many percentage options represent a simple fraction of a measurement
(chest circumference in the example above).
As this scenario is so common, `@freesewing/core` exports a `pctBasedOn` method
that will do the work for you:
```js
// First import the method
import { pctBasedOn } from '@freesewing/core'
const config = {
// ...
options: {
chestEase: {
pct: 8,
min: 0,
max: 20,
// Pass the measurement name as parameter
// and spread the return value into your option
...pctBasedOn('chest')
}
}
}
```
This will not only add an `fromAbs()` method to your option --
one that will return the percentage of any millimeter value passed into it --
it will also add a `toAbs()` method that does the inverse: return the
value in millimeter of whatever percentage the option is set to.
See [Reporting a percentage option value in
millimeter](/reference/api/config/options/pct/toabs) for details.

View file

@ -1,244 +0,0 @@
---
title: Snapped percentage options
---
Snapped percentage options are a hybrid between [list options][list] and
[percentage options][pct]. By combining traits of both, they create a
sort of _smart list option_ that will select the most appropriate value
from the list, and also allow a pure parametric value if no close match
is found.
## Structure
Your snapped percentage option should be a plain object with these properties:
- `pct` : The default percentage
- `min` : The minimum percentage that's allowed
- `max` : The maximum percentage that's allowed
- `snap`: Holds the snap configuration (see [Snap configuration](#))
- `toAbs`: a method returning the **millimeter value** of the option ([see `toAbs()`][toabs])
- `hide` <small>(optional)</small> : A method to [control the optional display of the option][hide]
## Snap configuration
A snapped percentage option requires a `snap` property that will determine
what values to snap to.
There are three different scenarios:
### snap holds a number
When `snap` holds a number, the option will be _snapped_ to a
multiple of this value.
In the example below, the absolute value of this option will be set to a multiple of `7`
(so one of `7`, `14`, `21`, `28`, `35`, ...).
```js
myOption: {
pct:5,
min: 0
max: 35,
snap: 7,
toAbs: (pct, { measurements }) => measurements.waistToFloor * pct
}
```
<Note>
In a case like this, the value will **always** be snapped,
because the snap points will be distributed equally across the entire range
of all possible inputs.
</Note>
### snap holds an array of numbers
When snap holds an array of numbers, the option will be _snapped_ to one of
the numbers unless it's further away than half the distance to its closest neighbor.
In the example below, if the absolute value returned by `toAbs()` is in the
region of influence -- in this example between 4.5 and 69.5 -- the nearest snap value
will be used. If instead it is outside the region of influence, the result of
`toAbs()` will be uses as-is.
```js
myOption: {
pct:5,
min: 0
max: 35,
snap: [7, 12, 21, 34, 53, 64 ]
toAbs: (pct, { measurements }) => measurements.waistToFloor * pct
}
```
### snap is a plain object with `metric` and `imperial` properties that each hold an array of numbers
In this case, the behavior is exaxtle the same as when `snap` holds an array
of numbers.
The differnce is that this allows you to supply a different list of snap values
for users using metric or imperial units.
In the example below, the value of [settings.units](/api/settings/units) will
determine which list of snap values gets used.
Then, if the absolute value returned by `toAbs()` is in the
region of influence -- in this example between 4.5 and 69.5 -- the nearest snap value
will be used. If instead it is outside the region of influence, the result of
`toAbs()` will be uses as-is.
```js
myOption: {
pct:5,
min: 0
max: 35,
snap: {
metric: [7, 12, 21, 34, 53, 64 ],
imperial: [25.4, 50.8, 76.3 ],
}
}
```
<Comment by="joost">
##### Read on for an in-depth look at snapped percentage options
While this information above tells you how to use snapped percentage options,
it does not explain why or when you should use them, or how they work.
Read on if you'd like to learn more about that.
</Comment>
## Example use-case
To understand the need that snapped percentage options are addressing,
we'll use an example use-case: We'll be designing a pajama pants pattern
with an elasticated waist.
In our design, the `waistbandWidth` option should match the width of the
elastic we're going to use so we can construct the waistband accordingly.
We have a few different ways we can approach this:
### Approach A: We use a percentage option
We use a percentage option based on a vertical measurement, like
`waistToFloor`.
The elastic width people end up with is something like 34.12mm for
user A and 27.83mm for user B.
Those are not widths for sale in the store, so that's not great.
### Approach B: We use a list option
We use a list option with a selection of standard elastic
widths to choose from: from half and inch to 3 inches
in 0.5 inch increments.
User A is a doll enthusiasts and 0.5 inch is too big.
User B is working on a giant to go on a float in a parade, and 3 inch
is way too small.
While it would probably work for most people somewhat in the middle,
our solution does not scale.
### Approach C: We use a snapped percentage option
We combine approaches A and B and configure a snapped percentage option
with:
- A percentage based on `waistToFloor`
- Our list of standard elastic widths as _snaps_
For typical humans, our options will _snap_ to the closest match in our
list and behave just like Approach A (with a list option).
For dolls and giants, the option will revert to the parametric value and
behave just like Approach B (with a percentage option).
Sweet!
## How snapped percentage options work
Before we wade into the details, let's first agree on terminology:
- The **percentage value** is the page passed by the user for the option.
Its value always represents a percentage.
- The **millimeter value** is the result of feeding the **percentage value** to
the `toAbs()` method. Its value always represents millimeters.
- The **snap values** are the values provided by the snap confguration.
Each of the values always represents millimeters.
Under the hood, and snapped percentage option will:
- Use `toAbs()` to calculate the **millimeter value** from the **percentage value**
- See whether the **millimeter value** approaches one of the **snap values**
- If so, use the snap value (in millimeter) as provided by one of the **snap values**
- If not, use the **millimeter value** as-is
If you're head's spinning, here's an image that will hopefully clarify things a bit:
![A visual guide to how snapped percentage options work](snap.png)
The gradient box represents the range of any given measurement,
from dolls all the way on the left, to giants all the way on the right.
The sort of middle green-colored region is what the designer had in mind
when designing the pattern, and they have set up snap values -- marked by
a red dot -- for values that they feel make sense.
The region of influence of any given snap point will extend 50% towards its
neighbor on both sides (indicated by the dashed lines).This means that the
region of snap points is continuous, once you're in, you're going to be
snapped to one of the snap points.
However, when you venture out into the area where the designer did not
configure any snap points, the absolute value will be used as-is, without
snapping, just as it would in a normal percentage option.
This system results in the best of both worlds:
- Things like elastic widths and so on can be configured to be fixed values,
of common elastic widths for example
- The absolute value will still scale up and down, but will snap to the closest
fixed value when appropriate.
- When the input measurements go somewhere the designer did not anticipate,
the option will just behave as a regular percentage option
## Using snapped percentage options in your pattern code
This is all well and good, but how do you use this?
Well, just like you can get the `options` object from our shorthand call,
you can now get the `absoluteOptions` object that holds absolute values
for those options with snaps configured.
In our paco example, what used to be:
```js
store.set('ankleElastic', measurements.waistToFloor * options.ankleElastic)
```
is now:
```js
store.set('ankleElastic', absoluteOptions.ankleElastic)
```
<Note>
There's really no added value in setting this in the store as `absoluteOptions`
is available everywhere, but we've changed as little as possible in the example
to clarify the difference.
</Note>
[toabs]: /reference/api/config/options/pct/toabs
[pct]: /reference/api/config/options/pct
[list]: /reference/api/config/options/list
[hide]: /reference/api/config/options#optionally-hide-options-by-configuring-a-hide-method

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View file

@ -1,86 +0,0 @@
---
title: Reporting a percentage option value in millimeter
---
Percentage options are great for parametric desing, but not always
very intuitive for the user. For example: Setting the `chestEase`
option to `9%` is not very meaningful unless you happen to know
what that percentage is based on.
To address this common grievance, FreeSewing allows you to add a
`toAbs` method that should return the value of the option in
millimeter.
## Structure
The `toAbs` property should hold a function with the following
signature:
```js
function toAbs(percentage, settings) {
// return value in millimeter here
}
```
The first parameter is the percentage value provided by the user (for example
`0.5` for `50%`).
The second parameter is the pattern's run-time configuration
or [settings](/reference/api/settings) which holds -- among other things -- the
measurements provided by the user.
## Example
In our example above, let's say that the `chestEase` option is
a simple percentage of the `chest` measurement. Our option
configuration could like like this:
```js
chestEase: {
pct: 8,
min: 0,
max: 20,
toAbs: function(value, settings) {
return settings.measurements.chest * value
}
}
```
With object destructuring and fat-arrow notation,
you can write it a bit terser like this:
```js
toAbs: (val, { measurements }) => measurements.chest * val
```
## Using pctBasedOn for simple measurement fractions
Many percentage options represent a simple fraction of a measurement
(chest circumference in the example above).
As this scenario is so common, `@freesewing/core` exports a `pctBasedOn` method
that will do the work for you:
```js
// First import the method
import { pctBasedOn } from '@freesewing/core'
const config = {
// ...
options: {
chestEase: {
pct: 8,
min: 0,
max: 20,
// Pass the measurement name as parameter
// and spread the return value into your option
...pctBasedOn('chest')
}
}
}
```
This will not only add an `toAbs()` method to your option -- one that will return
the value in millimeter of whatever percentage the option is set to -- it will
also add a `fromAbs()` method that does the inverse: return the percentage of
any millimeter value passed into it. See [Setting a value in millimeter as a
percentage option](/api/config/options/pct/fromabs) for details.

View file

@ -1,30 +0,0 @@
---
title: parts
---
The `parts` key in the pattern configuration file holds a list of
names of pattern parts.
<Tip>
###### This does not need to be an exhaustive list of all parts in your pattern.
This list of parts is needed for the `draft()` method to figure out what
parts need to be drafted.
So if parts are included in the `dependencies`, `inject`, or `hide` configuration,
there's no need to include them here, as we already know of their existence.
</Tip>
## Structure
An array of strings where the strings are part name.
## Example
```js
parts: [
"front",
"back"
]
```

View file

@ -1,9 +0,0 @@
---
title: code
---
```js
code: "Joost De Cock",
```
The name of the developer.

View file

@ -1,14 +0,0 @@
---
title: department
---
```js
department: "menswear",
```
One of the following:
- menswear
- womenswear
- unisex
- accessories

View file

@ -1,9 +0,0 @@
---
title: design
---
```js
design: "Joost De Cock",
```
The name of the designer.

View file

@ -1,9 +0,0 @@
---
title: difficulty
---
```js
difficulty: 3
```
A `1` to `5` difficulty score that indicates how hard it is to make the pattern.

View file

@ -1,13 +0,0 @@
---
title: UI configuration
---
Patterns also take these configuration options to facilitate UI integration:
<ReadMore list />
<Tip>
UI = User Interface. Like a website or mobile app
</Tip>

View file

@ -1,20 +0,0 @@
---
title: The advanced option group
---
Naming an option group `advanced` will hide it by default from the user
unless they enable _expert mode_.
```js
optionGroups: {
fit: ["chestEase", "hipsEase", "stretchFactor"],
style: ["armholeDrop", "backlineBend"],
advanced: [ "plutoniumCount" ]
}
```
<Tip>
The `advanced` option group can also have nested groups
</Tip>

View file

@ -1,28 +0,0 @@
---
title: optionGroups
---
Organises your pattern options in groups.
It expects an object where the key is the group title, and the value an array of options:
```js
optionGroups: {
fit: ["chestEase", "hipsEase", "stretchFactor"],
style: [
"armholeDrop",
"backlineBend",
"necklineBend",
"necklineDrop",
"shoulderStrapWidth",
"shoulderStrapPlacement",
"lengthBonus"
]
}
```
<Note>
Options that are not included in the `optionGroup` configuration won't be
exposed in the frontend and thus will be unavailable to the user.
</Note>

View file

@ -1,26 +0,0 @@
---
title: Nested option groups
---
You can create sub-groups within an option group:
```js
optionGroups: {
style: [
'hemStyle',
'hemCurve',
{
closure: [
'extraTopButton',
'buttons',
'buttonFreeLength'
]
},
]
}
```
<Warning>
Only create subgroups one level deep.
We do not support groups in groups in groups.
</Warning>

View file

@ -1,9 +0,0 @@
---
title: tags
---
```js
tags: ["underwear", "top", "basics"],
```
A set of tags to allow filtering of patterns on the website.

View file

@ -1,12 +0,0 @@
---
title: type
---
```js
type: "pattern",
```
One of the following:
- pattern
- block

View file

@ -1,15 +0,0 @@
---
title: version
---
The `version` key in the pattern configuration holds the version
of the pattern. For a pattern that is published as an NPM package
this is typically the same version as set in the `package.json` file.
## Structure
A string that holds a valid semantic version number.
## Example
version: "0.3.1"

View file

@ -1,5 +1,5 @@
---
title: "@freesewing/core API"
title: Core API
---
This is the documentation for FreeSewing's core library, published as `@freesewing/core` on NPM.

View file

@ -67,6 +67,13 @@ It just so happens that in most cases, there will be only one settings object in
</Note>
## Pattern attributes
- `Pattern.designConfig`: Holds the design's configuration
- `Pattern.patternConfig`: Holds the pattern's configuration
- `Pattern.store`: Holds the pattern-wide Store
- `Pattern.setStores`: Holds an array of stores, one for each set of settings.
## Pattern methods
<ReadMore list />

View file

@ -1,21 +0,0 @@
---
title: complete
---
The `complete` setting controls the level of details that's included on your pattern.
This has different uses, such as generating patterns to be cut out with a laser cutter.
The default `complete` setting is `true`.
Set this to `false` to draft a base outline of the pattern, rather than a fully detailed pattern.
<Note>
Setting this to `false` will force [sa](/reference/api/settings/sa) to be set to `false`.
</Note>
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
complete: false
})
```

View file

@ -1,27 +0,0 @@
---
title: embed
---
The `embed` setting controls the properties of the SVG document.
Set it to `true` to make SVG output suitable for embedding in a web page.
The default for `embed` is `false` which will include the `width` and `height`
attributes in the SVG tag, thereby making it suitable for printing.
When set to `true` the `width` and `height` attributes will not be added
which allows you to inject the SVG into an HTML document, where it will
responsively scale.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
embed: true
})
```
<Warning>
Do **not** use this for SVGs you want to print.
</Warning>

View file

@ -1,14 +0,0 @@
---
title: Settings
for: developers
about: Documents all the settings your pattern can receive, including the pattern options, measurmeents, and design options
---
Settings are what the user passes to your pattern at run-time.
Don't confuse them with the [pattern configuration](/reference/config/) that is determined by
the designer at build-time.
Below are the supported settings:
<ReadMore list />

View file

@ -1,18 +0,0 @@
---
title: locale
---
The `locale` setting allows you to specify the language of the pattern.
It should contain a 2-letter language code that indicates what language the user wants.
The default is `en`.
This will be used to set the `xml:lang` attribute in the `svg` tag when rendering to SVG,
and by [the i18n plugin](/reference/plugins/i18n/) to translate the pattern.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
locale: "es"
})
```

View file

@ -1,18 +0,0 @@
---
title: measurements
---
The `measurements` settings should hold and object with the
measurements to draft the pattern for.
The pattern configuration lists all required measurements.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
measurements: {
chestCircumference: 1080
shoulderSlope: 55
}
})
```

View file

@ -1,17 +0,0 @@
---
title: only
---
The `only` setting allows you to specify one or more parts to
draft/render, rather than the entire pattern.
It accepts either a single part name as a string, or an array of
one or more part names.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
only: ["front", "sleeve"]
})
```

View file

@ -1,24 +0,0 @@
---
title: options
---
The `options` setting allows you to specify values for the pattern-specific
options that have been implemented by the pattern designer.
The available options are listed in the pattern configuration.
Refer to the [the options section in the pattern configuration file][1] for
all details about using options in FreeSewing.
[1]: /reference/api/config/options
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
options: {
chestEase: 120
}
})
```
<Note>Unlike measurements, options come with defaults.</Note>

View file

@ -1,25 +0,0 @@
---
title: sa
---
The `sa` setting controls the seam allowance. It expects a value in mm
or `false` or `0` to disable seam allowance altogether.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
sa: 10
})
```
<Note>
This is ignored if [settings.complete](/reference/api/settings/complete) is `false`
<Comment by="joost">
Is it though?
I suspect this is not clearly enforced and we should clarify that.
</Comment>
</Note>

View file

@ -1,17 +0,0 @@
---
title: units
---
The `units` setting controls the units used on the pattern.
It can be either `metric` (the default) or `imperial`.
Note that this is only used to format the output.
Freesewing expects mm as input.
```js
import Brian from "@freesewing/brian";
const pattern = new Brian({
units: "imperial"
})
```

View file

@ -1,14 +0,0 @@
---
title: bnotch
---
The `bnotch` snippet is intended for notches at the back, or when you
need an alternative to the default `notch`.
It is provided by [plugin-notches](/reference/plugins/notches/).
```js
snippets.demo = new Snippet('bnotch', points.anchor)
```
<Example part="snippets_bnotch">An example of the bnotch snippet</Example>

View file

@ -1,14 +0,0 @@
---
title: button
---
The `button` snippet is used to mark button placement and is
provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('button', points.anchor)
```
<Example part="snippets_button">
An example of the button snippet
</Example>

View file

@ -1,26 +0,0 @@
---
title: buttonhole-end
---
The `buttonhole-end` snippet is used to mark buttonhole placement and is
provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('buttonhole-end', points.anchor)
```
<Example part="snippets_buttonhole_end">
An example of the buttonhole-end snippet
</Example>
<Note>
##### Aligning your buttons and buttonholes
We provide three buttonhole snippets with a different alignment:
- [buttonhole](/reference/api/snippets/buttonhole/): Anchor point is the middle of the buttonhole
- [buttonhole-start](/reference/api/snippets/buttonhole-start/): Anchor point is the start of the buttonhole
- [buttonhole-end](/reference/api/snippets/buttonhole-end/): Anchor point is the end of the buttonhole
</Note>

View file

@ -1,26 +0,0 @@
---
title: buttonhole-start
---
The `buttonhole-start` snippet is used to mark buttonhole placement and is
provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('buttonhole-start', points.anchor)
```
<Example part="snippets_buttonhole_start">
An example of the buttonhole-start snippet
</Example>
<Note>
##### Aligning your buttons and buttonholes
We provide three buttonhole snippets with a different alignment:
- [buttonhole](/reference/api/snippets/buttonhole/): Anchor point is the middle of the buttonhole
- [buttonhole-start](/reference/api/snippets/buttonhole-start/): Anchor point is the start of the buttonhole
- [buttonhole-end](/reference/api/snippets/buttonhole-end/): Anchor point is the end of the buttonhole
</Note>

View file

@ -1,26 +0,0 @@
---
title: buttonhole
---
The `buttonhole` snippet is used to mark buttonhole placement and is
provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('buttonhole', points.anchor)
```
<Example part="snippets_buttonhole">
An example of the buttonhole snippet
</Example>
<Note>
##### Aligning your buttons and buttonholes
We provide three buttonhole snippets with a different alignment:
- [buttonhole](/reference/api/snippets/buttonhole/): Anchor point is the middle of the buttonhole
- [buttonhole-start](/reference/api/snippets/buttonhole-start/): Anchor point is the start of the buttonhole
- [buttonhole-end](/reference/api/snippets/buttonhole-end/): Anchor point is the end of the buttonhole
</Note>

View file

@ -1,9 +0,0 @@
---
title: Snippets
for: developers
about: Complete list of all the available snippets
---
Snippets are provided by plugins. Follow the links below for more info on and an example of a specific snippet:
<ReadMore list />

View file

@ -1,14 +0,0 @@
---
title: logo
---
The `logo` snippet inserts the FreeSewing logo. It is
provided by [plugin-logo](/reference/plugins/logo/).
```js
snippets.demo = new Snippet('logo', points.anchor)
```
<Example part="snippets_logo">
An example of the logo snippet
</Example>

View file

@ -1,14 +0,0 @@
---
title: notch
---
The `notch` snippet is intended for notches and is
provided by [plugin-notches](/reference/plugins/notches/).
```js
snippets.demo = new Snippet('bnotch', points.anchor)
```
<Example part="snippets_notch">
An example of the notch snippet
</Example>

View file

@ -1,15 +0,0 @@
---
title: snap-socket
---
The `snap-socket` snippet is used to mark the socket part of a snap button.
It is provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('snap-socket', points.anchor)
```
<Example part="snippets_snapsocket">
An example of the snap-socket snippet
</Example>

View file

@ -1,15 +0,0 @@
---
title: snap-stud
---
The `snap-stud` snippet is used to mark the stud part of a snap button.
It is provided by [plugin-buttons](/reference/plugins/buttons/).
```js
snippets.demo = new Snippet('snap-stud', points.anchor)
```
<Example part="snippets_snapstud">
An example of the snap-stud snippet
</Example>

View file

@ -1,5 +1,6 @@
---
title: attributes
title: Svg.attributes
---
An [Attributes](/reference/api/attributes) instance that controls the attributes of the SVG tag.
The `Svg.attributes` property holds an [Attributes](/reference/api/attributes)
instance to control the attributes of the SVG document.

View file

@ -1,12 +1,12 @@
---
title: defs
title: Svg.defs
---
A string that will be rendered
as [the defs section](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs) of
the SVG document.
The `Svg.defs` property holds a string that will be rendered as [the defs
section](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs) of the
SVG document.
The defs attribute is where plugins will add additional snippets.
## Signature
```svg
<defs>
@ -14,9 +14,9 @@ The defs attribute is where plugins will add additional snippets.
</defs>
```
<Warning>
## Notes
###### Add, but don't overwrite
The defs attribute is where plugins will add additional snippets.
When adding your own defs, it's important not to
overwrite this property, but rather add your own.
@ -24,13 +24,11 @@ overwrite this property, but rather add your own.
In other words, do this:
```js
svg.defs += myDefs;
svg.defs += myDefs
```
and don't do this:
```js
svg.defs = myDefs;
svg.defs = myDefs
```
</Warning>

View file

@ -5,9 +5,10 @@ title: Svg
The `Svg` object in FreeSewing's core library represents an SVG document.
It is not directly exposed, but it is available as the `svg` attribute
of a [Pattern object](/reference/api/pattern/).
While the methods exposed by this object are only used internally,
its attributes are useful for situations where you
want to develop a plugin, or use a custom layout:
want to develop a plugin, or use a custom layout.
## Svg properties

View file

@ -1,34 +0,0 @@
---
title: head
---
A string that combines the `style`, `script`,
and `defs` sections and an opening tag for an SVG group.
```svg
<style type="text/css">
/* svg.style will be inserted */
</style>
<script type="text/javascript">
/* svg.script will be inserted */
</scripts>
<defs>
/* svg.defs will be inserted */
</defs>
<!-- Start of group #fs-container -->
<g id="fs-container">
```
<Note>
###### This does not include the opening SVG tag
Note that while [Pattern.svg.tail](/reference/api/pattern/svg/tail/) closes the SVG tag,
[Pattern.svg.head](/reference/api/pattern/head/) does not open it.
That's because the `width`, `height` and `viewBox` attributes will
depend on the main body of the SVG document.
</Note>

View file

@ -1,9 +1,11 @@
---
title: layout
title: Svg.layout
---
An object that holds rendered SVG for all parts, and a list of their transforms.
It is structured as follows:
The `Svg.layout` property holds an object that holds rendered SVG for all
parts, and a list of their transforms.
## Signature
```js
{

View file

@ -1,8 +1,11 @@
---
title: pattern
title: Svg.pattern
---
A reference to [the Pattern object](/reference/api/pattern/).
The `Svg.pattern` property holds a reference to [the Pattern
object](/reference/api/pattern/) itself.
This allows hooks that only receive the SVG object (such as the preRender and postRender hooks)
to still access the pattern object.
## Notes
This allows hooks that only receive the SVG object (such as the preRender and
postRender hooks) to still access the pattern object.

View file

@ -1,14 +0,0 @@
---
title: prefix
---
A string that will be rendered before the opening SVG tag.
Its default value is:
```svg
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
```
<Note>This only applies when rendering to SVG, not what
rendering a React component</Note>

View file

@ -1,34 +0,0 @@
---
title: script
---
A string that will be rendered as the script section of the SVG document.
We don't use this ourselves, but it's there if you need it.
```svg
<script type="text/javascript">
/* svg.script will be inserted */
</scripts>
```
<Warning>
###### Add, but don't overwrite
When adding your own script, it's important not to
overwrite this property, but rather add your own.
In other words, do this:
```js
svg.script += myScript;
```
and don't do this:
```js
svg.script = myScript;
```
</Warning>

View file

@ -1,10 +1,11 @@
---
title: style
title: Svg.style
---
A string that will be rendered as the style section of the SVG document.
The `Svg.style` property holds a string that will be rendered as the style
section of the SVG document.
The style attribute is where plugins will add additional snippets.
## Signature
```svg
<style type="text/css">
@ -12,9 +13,9 @@ The style attribute is where plugins will add additional snippets.
</style>
```
<Warning>
## Notes
###### Add, but don't overwrite
The style property is where plugins will add additional styling.
When adding your own styles, it's important not to
overwrite this property, but rather add your own.
@ -22,13 +23,11 @@ overwrite this property, but rather add your own.
In other words, do this:
```js
svg.style += myStyles;
svg.style += myStyles
```
and don't do this:
```js
svg.style = myStyles;
svg.style = myStyles
```
</Warning>

View file

@ -1,11 +0,0 @@
---
title: tail
---
A string that closes both the group opened by [Pattern.svg.head](/reference/api/pattern/svg/head/) and the SVG tag.
```svg
</g>
<!-- end of group #fs-container -->
</svg>
```

View file

@ -1,20 +1,23 @@
---
title: beamIntersectsCircle()
title: utils.beamIntersectsCircle()
---
The `utils.beamIntersectsCircle()` function finds the intersection between an
endless line through points `point1` and `point2` and a circle with its center
at point `center` and a radius of `radius` mm.
## Signature
```js
array | false utils.beamIntersectsCircle(
Point center,
float radius,
Point point1,
Point point1,
string sort = 'x'
string sort=x
)
```
Finds the intersection between an endless line through points `point1` and `point2`
and a circle with its center at point `center` and a radius of `radius` mm.
The 5th and last parameter controls the _sorting_ of the found intersections.
This will (almost) always return 2 intersections, and you can choose how
they are ordered in the returned array:
@ -24,59 +27,54 @@ Set sort to:
- `x` : The point with the lowest X-coordinate will go first (left to right)
- `y` : The point with the lowest Y-coordinate will go first (top to bottom)
<Example part="utils_beamintersectscircle">
A Utils.beamIntersectsCircle() example
## Example
<Example caption="A utils.beamIntersectsCircle() example">
```js
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(45, 45)
.addCircle(35, "Fabric")
points.B = new Point(5, 50)
points.C = new Point(25, 30)
points.D = new Point(5, 65)
points.E = new Point(65, 5)
points.F = new Point(15, 75)
points.G = new Point(75, 15)
paths.line1 = new Path().move(points.B).line(points.C)
paths.line2 = new Path().move(points.D).line(points.E)
paths.line3 = new Path().move(points.F).line(points.G)
let intersections1 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.C
)
let intersections2 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.D,
points.E,
"y"
)
let intersections3 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.F,
points.G
)
snippets.first1 = new Snippet("bnotch", intersections1[0])
snippets.second1 = new Snippet("notch", intersections1[1])
snippets.first2 = new Snippet("bnotch", intersections2[0])
snippets.second2 = new Snippet("notch", intersections2[1])
snippets.first3 = new Snippet("bnotch", intersections3[0])
snippets.second3 = new Snippet("notch", intersections3[1])
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.A = new Point(45, 45)
.attr("data-circle", 35)
.attr("data-circle-class", "fabric");
points.B = new Point(5, 50);
points.C = new Point(25, 30);
points.D = new Point(5, 65);
points.E = new Point(65, 5);
points.F = new Point(15, 75);
points.G = new Point(75, 15);
paths.line1 = new Path().move(points.B).line(points.C);
paths.line2 = new Path().move(points.D).line(points.E);
paths.line3 = new Path().move(points.F).line(points.G);
let intersections1 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.C
);
let intersections2 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.D,
points.E,
"y"
);
let intersections3 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.F,
points.G
);
snippets.first1 = new Snippet("bnotch", intersections1[0]);
snippets.second1 = new Snippet("notch", intersections1[1]);
snippets.first2 = new Snippet("bnotch", intersections2[0]);
snippets.second2 = new Snippet("notch", intersections2[1]);
snippets.first3 = new Snippet("bnotch", intersections3[0]);
snippets.second3 = new Snippet("notch", intersections3[1]);
```

View file

@ -1,38 +1,40 @@
---
title: beamIntersectsX()
title: utils.beamIntersectsX()
---
The `utils.beamIntersectsX()` function finds the intersection between an endless
line and a given X-value. Returns a [Point](/reference/api/point) object for
the intersection, or `false` there is no intersection.
## Signature
```js
Point | false utils.beamIntersectsX(Point A, Point B, float X)
```
Finds the intersection between an endless line and a given X-value. Returns a [Point](/reference/api/point) object
for the intersection, or `false` there is no intersection.
<Example part="utils_beamintersectsx">
A Utils.beamIntersectsX() example
</Example>
## Example
<Example caption="A Utils.beamIntersectsX() example">
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10);
points.B = new Point(90, 30);
points.A = new Point(10, 10)
points.B = new Point(90, 30)
paths.AB = new Path().move(points.A).line(points.B);
paths.AB = new Path().move(points.A).line(points.B)
snippets.x = new Snippet("notch", utils.beamIntersectsX(points.A, points.B, 40));
snippets.x = new Snippet(
"notch",
utils.beamIntersectsX(points.A, points.B, 40)
)
paths.help = new Path()
.move(new Point(40, 5))
.line(new Point(40, 35))
.attr("class", "note dashed");
.addClass("note dashed")
return part
}
```
</Example>

View file

@ -1,38 +1,40 @@
---
title: beamIntersectsY()
title: utils.beamIntersectsY()
---
The `utils.beamIntersectsY()` function finds the intersection between an endless
line and a given Y-value. Returns a [Point](/reference/api/point) object for
the intersection, or `false` there is no intersection.
## Signature
```js
Point | false utils.beamIntersectsY(Point A, Point B, float Y)
```
Finds the intersection between an endless line and a given Y-value. Returns a [Point](/reference/api/point) object
for the intersection, or `false` there is no intersection.
## Example
<Example part="utils_beamintersectsy">
A Utils.beamIntersectsY() example
<Example caption="A Utils.beamIntersectsY() example">
```js
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
paths.AB = new Path().move(points.A).line(points.B)
snippets.x = new Snippet(
"notch",
utils.beamIntersectsY(points.A, points.B, 30)
)
paths.help = new Path()
.move(new Point(0, 30))
.line(new Point(50, 30))
.attr("class", "note dashed")
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.A = new Point(10, 10);
points.B = new Point(50, 40);
paths.AB = new Path().move(points.A).line(points.B);
snippets.x = new Snippet("notch", utils.beamIntersectsY(points.A, points.B, 30));
paths.help = new Path()
.move(new Point(0, 30))
.line(new Point(50, 30))
.attr("class", "note dashed");
```

View file

@ -1,7 +1,13 @@
---
title: beamsIntersect()
title: uils.beamsIntersect()
---
The `utils.beamsIntersect()` function finds the intersection between two endless
lines (beams). Returns a [Point](/reference/api/point) object for the
intersection, or `false` if the lines don't intersect.
## Signature
```js
Point | false utils.beamsIntersect(
Point A,
@ -11,34 +17,27 @@ Point | false utils.beamsIntersect(
)
```
Finds the intersection between two endless lines (beams). Returns a [Point](/reference/api/point) object
for the intersection, or `false` if the lines don't intersect.
## Example
<Example part="utils_beamsintersect">
A Utils.beamIntersect() example
<Example caption="A Utils.beamIntersect() example">
```js
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
points.C = new Point(45, 20)
points.D = new Point(60, 15)
paths.AB = new Path().move(points.A).line(points.B)
paths.CD = new Path().move(points.C).line(points.D)
snippets.x = new Snippet(
"notch",
utils.beamsIntersect(points.A, points.B, points.C, points.D)
)
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.A = new Point(10, 10);
points.B = new Point(50, 40);
points.C = new Point(45, 20);
points.D = new Point(60, 15);
paths.AB = new Path().move(points.A).line(points.B);
paths.CD = new Path().move(points.C).line(points.D);
snippets.x = new Snippet(
"notch",
utils.beamsIntersect(points.A, points.B, points.C, points.D)
);
```

View file

@ -0,0 +1,19 @@
---
title: utils.capitalize()
---
The `utils.capitalize()` function returns the string you pass it with its first
letter turned into uppercase.
## Signature
```js
String utils.capitalize(String input)
```
## Example
```js
capitalize('hello') // returns 'Hello')
capitalize('hello there') // returns 'Hello there'
```

View file

@ -1,7 +1,12 @@
---
title: circlesIntersect()
title: utils.circlesIntersect()
---
The `utils.circlesIntersect()` function finds the intersections between two
circles described by their center point and radius.
## Signature
```js
array | false utils.circlesIntersect(
Point centerA,
@ -12,8 +17,6 @@ array | false utils.circlesIntersect(
)
```
Finds the intersections between two circles described by their center point and radius.
The 5th and last parameter controls the _sorting_ of the found intersections.
When this returns 2 intersections, you can choose how they are ordered in the returned array:
@ -22,42 +25,42 @@ Set sort to:
- `x` : The point with the lowest X-coordinate will go first (left to right)
- `y` : The point with the lowest Y-coordinate will go first (top to bottom)
<Example part="utils_circlesintersect">
A Utils.circlesIntersect() example
## Example
<Example caption="A Utils.circlesIntersect() example">
```js
({ Point, points, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
.addCircle(15, "fabric")
points.B = new Point(30, 30)
.addCircle(35, "fabric")
points.C = new Point(90, 10)
.addCircle(15, "various")
points.D = new Point(110, 30)
.addCircle(35, "various")
const intersections1 = utils.circlesIntersect(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.B.attributes.get("data-circle")
)
const intersections2 = utils.circlesIntersect(
points.C,
points.C.attributes.get("data-circle"),
points.D,
points.D.attributes.get("data-circle"),
"y"
)
snippets.first1 = new Snippet("bnotch", intersections1[0])
snippets.second1 = new Snippet("notch", intersections1[1])
snippets.first2 = new Snippet("bnotch", intersections2[0])
snippets.second2 = new Snippet("notch", intersections2[1])
return part
}
```
</Example>
```js
let { Point, points, Snippet, snippets, utils } = part.shorthand();
points.A = new Point(10, 10)
.attr("data-circle", 15)
.attr("data-circle-class", "fabric");
points.B = new Point(30, 30)
.attr("data-circle", 35)
.attr("data-circle-class", "fabric");
points.C = new Point(90, 10)
.attr("data-circle", 15)
.attr("data-circle-class", "various");
points.D = new Point(110, 30)
.attr("data-circle", 35)
.attr("data-circle-class", "various");
let intersections1 = utils.circlesIntersect(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.B.attributes.get("data-circle")
);
let intersections2 = utils.circlesIntersect(
points.C,
points.C.attributes.get("data-circle"),
points.D,
points.D.attributes.get("data-circle"),
"y"
);
snippets.first1 = new Snippet("bnotch", intersections1[0]);
snippets.second1 = new Snippet("notch", intersections1[1]);
snippets.first2 = new Snippet("bnotch", intersections2[0]);
snippets.second2 = new Snippet("notch", intersections2[1]);
```

View file

@ -1,7 +1,12 @@
---
title: curveIntersectsX()
title: utils.curveIntersectsX()
---
The `utils.curveIntersectsX()` function finds the point(s) where a curve
intersects a given X-value.
## Signature
```js
array | Point | false utils.curveIntersectsX(
Point start,
@ -11,61 +16,60 @@ array | Point | false utils.curveIntersectsX(
float x)
```
Finds the point(s) where a curve intersects a given X-value.
This is a low-level variant
of [`Path.intersectsX()`](/reference/api/path/intersectsx).
Instead of a path, you describe a single curve by passing the four
points that describes it.
This returns `false` if no intersections are found,
a [Point](/reference/api/point) object if
a single intersection is found, and an array
of [Point](/reference/api/point) objects if
multiple intersections are found.
<Example part="utils_curveintersectsx">A Utils.curveIntersectX() example</Example>
## Example
<Example caption="A Utils.curveIntersectX() example">
```js
let {
Point,
points,
Path,
paths,
utils,
snippets,
Snippet
} = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.start = new Point(10, 15);
points.cp1 = new Point(80, 10);
points.cp2 = new Point(-50, 80);
points.end = new Point(110, 70);
points.start = new Point(10, 15)
points.cp1 = new Point(80, 10)
points.cp2 = new Point(-50, 80)
points.end = new Point(110, 70)
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
for (let x of [30, 40]) {
points["from" + x] = new Point(x, 10)
points["to" + x] = new Point(x, 80)
paths["line" + x] = new Path()
.move(points["from" + x])
.line(points["to" + x])
.addClass("lining dashed")
}
snippets.i40 = new Snippet(
"notch",
utils.curveIntersectsX(points.start, points.cp1, points.cp2, points.end, 40)
)
for (let p of utils.curveIntersectsX(
points.start,
points.cp1,
points.cp2,
points.end,
30
))
snippets[p.y] = new Snippet("notch", p)
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end);
for (let x of [30, 40]) {
points["from" + x] = new Point(x, 10);
points["to" + x] = new Point(x, 80);
paths["line" + x] = new Path()
.move(points["from" + x])
.line(points["to" + x])
.attr("class", "lining dashed");
return part
}
snippets.i40 = new Snippet(
"notch",
utils.curveIntersectsX(points.start, points.cp1, points.cp2, points.end, 40)
);
for (let p of utils.curveIntersectsX(
points.start,
points.cp1,
points.cp2,
points.end,
30
))
snippets[p.y] = new Snippet("notch", p);
```
</Example>
## Notes
This is a low-level (and faster) variant
of [`Path.intersectsX()`](/reference/api/path/intersectsx).
Instead of a path, you describe a single curve by passing the four
points that describes it.

View file

@ -1,7 +1,12 @@
---
title: curveIntersectsY()
title: utils.curveIntersectsY()
---
The `utils.curveIntersectsX()` function finds the point(s) where a curve
intersects a given Y-value.
## Signature
```js
array | Point | false utils.curveIntersectsY(
Point start,
@ -11,61 +16,60 @@ array | Point | false utils.curveIntersectsY(
float y)
```
Finds the point(s) where a curve intersects a given Y-value.
This is a low-level variant
of [`Path.intersectsY()`](/reference/api/path/intersectsy).
Instead of a path, you describe a single curve by passing the four
points that describes it.
This returns `false` if no intersections are found,
a [Point](/reference/api/point/) object if
a single intersection is found, and an array
of [Point](/reference/api/point/) objects if
multiple intersections are found.
<Example part="utils_curveintersectsy">A Utils.curveIntersectY() example</Example>
## Example
<Example caption="A Utils.curveIntersectY() example">
```js
let {
Point,
points,
Path,
paths,
utils,
snippets,
Snippet
} = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.start = new Point(10, 45);
points.cp1 = new Point(50, 10);
points.cp2 = new Point(0, 80);
points.end = new Point(110, 70);
points.start = new Point(10, 45)
points.cp1 = new Point(50, 10)
points.cp2 = new Point(0, 80)
points.end = new Point(110, 70)
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
for (let y of [40, 50]) {
points["from" + y] = new Point(10, y)
points["to" + y] = new Point(110, y)
paths["line" + y] = new Path()
.move(points["from" + y])
.line(points["to" + y])
.addClass("lining dashed")
}
snippets.i50 = new Snippet(
"notch",
utils.curveIntersectsY(points.start, points.cp1, points.cp2, points.end, 50)
)
for (let p of utils.curveIntersectsY(
points.start,
points.cp1,
points.cp2,
points.end,
40
))
snippets[p.x] = new Snippet("notch", p)
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end);
for (let y of [40, 50]) {
points["from" + y] = new Point(10, y);
points["to" + y] = new Point(110, y);
paths["line" + y] = new Path()
.move(points["from" + y])
.line(points["to" + y])
.attr("class", "lining dashed");
return part
}
snippets.i50 = new Snippet(
"notch",
utils.curveIntersectsY(points.start, points.cp1, points.cp2, points.end, 50)
);
for (let p of utils.curveIntersectsY(
points.start,
points.cp1,
points.cp2,
points.end,
40
))
snippets[p.x] = new Snippet("notch", p);
```
</Example>
## Notes
This is a low-level (and faster) variant
of [`Path.intersectsY()`](/reference/api/path/intersectsy).
Instead of a path, you describe a single curve by passing the four
points that describes it.

View file

@ -1,7 +1,12 @@
---
title: curvesIntersect()
title: utils.curvesIntersect()
---
The `utils.curvesIntersect()` function finds the intersections between two curves
described by 4 points each.
## Signature
```js
array | false utils.curvesIntersect(
Point startA,
@ -14,49 +19,43 @@ array | false utils.curvesIntersect(
Point endB)
```
Finds the intersections between two curves described by 4 points each.
<Example part="utils_curvesintersect">
A Utils.curvesIntersect() example
</Example>
## Example
<Example caption="A Utils.curvesIntersect() example">
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, utils, getId, part }) => {
points.A = new Point(10, 10);
points.Acp = new Point(310, 40);
points.B = new Point(110, 70);
points.Bcp = new Point(-210, 40);
points.A = new Point(10, 10)
points.Acp = new Point(310, 40)
points.B = new Point(110, 70)
points.Bcp = new Point(-210, 40)
points.C = new Point(20, -5)
points.Ccp = new Point(60, 300)
points.D = new Point(100, 85)
points.Dcp = new Point(70, -220)
paths.curveA = new Path()
.move(points.A)
.curve(points.Acp, points.Bcp, points.B)
paths.curveB = new Path()
.move(points.C)
.curve(points.Ccp, points.Dcp, points.D)
for (const p of utils.curvesIntersect(
points.A,
points.Acp,
points.Bcp,
points.B,
points.C,
points.Ccp,
points.Dcp,
points.D
)) {
snippets[getId()] = new Snippet("notch", p)
}
points.C = new Point(20, -5);
points.Ccp = new Point(60, 300);
points.D = new Point(100, 85);
points.Dcp = new Point(70, -220);
paths.curveA = new Path()
.move(points.A)
.curve(points.Acp, points.Bcp, points.B);
paths.curveB = new Path()
.move(points.C)
.curve(points.Ccp, points.Dcp, points.D);
for (let p of utils.curvesIntersect(
points.A,
points.Acp,
points.Bcp,
points.B,
points.C,
points.Ccp,
points.Dcp,
points.D
)) {
snippets[part.getId()] = new Snippet("notch", p);
return part
}
```
</Example>

View file

@ -1,12 +1,16 @@
---
title: deg2rad()
title: utils.deg2rad()
---
The `utils.deg2read()` function returns the degrees you pass to it as radians.
## Signature
```js
float deg2rad(float degrees)
```
Returns the degrees you pass to it as radians.
## Notes
This is useful for when you use methods like `Math.cos()` that expects a corner
This is useful for when you use functions like `Math.cos()` that expect a corner
in radians, when we typically use degrees.

View file

@ -1,7 +1,10 @@
---
title: Utils
title: utils
---
The `Utils` object provides the following utility methods to facilitate your work:
The `utils` object is a plain object that bundles utility functions to
facilitate parametric design.
The following functions are provided by the `utils` object:
<ReadMore list />

View file

@ -1,7 +1,13 @@
---
title: lineIntersectsCircle()
title: utils.lineIntersectsCircle()
---
The `utils.lineIntersectsCircle()` function finds the intersection between a line
segment from point `from` to point `to` and a circle with its center at point
`center` and a radius of `radius` mm.
## Signature
```js
array | false utils.lineIntersectsCircle(
Point center,
@ -12,9 +18,6 @@ array | false utils.lineIntersectsCircle(
)
```
Finds the intersection between a line segment from point `from` to point `to`
and a circle with its center at point `center` and a radius of `radius` mm.
The 5th and last parameter controls the _sorting_ of the found intersections.
When this returns 2 intersections, you can choose how they are ordered in the returned array:
@ -23,58 +26,53 @@ Set sort to:
- `x` : The point with the lowest X-coordinate will go first (left to right)
- `y` : The point with the lowest Y-coordinate will go first (top to bottom)
<Example part="utils_lineintersectscircle">
A Utils.lineIntersectsCircle() example
## Example
<Example caption="A Utils.lineIntersectsCircle() example">
```js
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(95, 45)
.addCircle(35, "fabric")
points.B = new Point(55, 50)
points.C = new Point(75, 30)
points.D = new Point(55, 65)
points.E = new Point(115, 5)
points.F = new Point(65, 75)
points.G = new Point(125, 15)
paths.line1 = new Path().move(points.B).line(points.C)
paths.line2 = new Path().move(points.D).line(points.E)
paths.line3 = new Path().move(points.F).line(points.G)
const intersections1 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.C
)
const intersections2 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.D,
points.E,
"y"
)
const intersections3 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.F,
points.G
)
snippets.first1 = new Snippet("bnotch", intersections1[0])
snippets.first2 = new Snippet("bnotch", intersections2[0])
snippets.second2 = new Snippet("notch", intersections2[1])
snippets.first3 = new Snippet("bnotch", intersections3[0])
snippets.second3 = new Snippet("notch", intersections3[1])
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.A = new Point(95, 45)
.attr("data-circle", 35)
.attr("data-circle-class", "fabric");
points.B = new Point(55, 50);
points.C = new Point(75, 30);
points.D = new Point(55, 65);
points.E = new Point(115, 5);
points.F = new Point(65, 75);
points.G = new Point(125, 15);
paths.line1 = new Path().move(points.B).line(points.C);
paths.line2 = new Path().move(points.D).line(points.E);
paths.line3 = new Path().move(points.F).line(points.G);
let intersections1 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.B,
points.C
);
let intersections2 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.D,
points.E,
"y"
);
let intersections3 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get("data-circle"),
points.F,
points.G
);
snippets.first1 = new Snippet("bnotch", intersections1[0]);
snippets.first2 = new Snippet("bnotch", intersections2[0]);
snippets.second2 = new Snippet("notch", intersections2[1]);
snippets.first3 = new Snippet("bnotch", intersections3[0]);
snippets.second3 = new Snippet("notch", intersections3[1]);
```

View file

@ -1,7 +1,13 @@
---
title: lineIntersectsCurve()
title: utils.lineIntersectsCurve()
---
The `utils.lineIntersectsCurve()` function finds the intersection between a line
segment from point `from` to point `to` and a curve described by points
`start`, `cp1`, `cp2, and `end\`.
## Signature
```js
array | false utils.lineIntersectsCurve(
Point from,
@ -13,43 +19,36 @@ array | false utils.lineIntersectsCurve(
)
```
Finds the intersection between a line segment from point `from` to point `to`
and a curve described by points `start`, `cp1`, `cp2, and `end\`.
<Example part="utils_lineintersectscurve">
A Utils.lineIntersectsCurve() example
</Example>
## Example
<Example caption="A Utils.lineIntersectsCurve() example">
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, getId, utils, part }) => {
points.A = new Point(10, 10);
points.Acp = new Point(310, 40);
points.B = new Point(110, 70);
points.Bcp = new Point(-210, 40);
points.E = new Point(20, -5);
points.D = new Point(100, 85);
paths.curve = new Path()
.move(points.A)
.curve(points.Acp, points.Bcp, points.B);
paths.line = new Path().move(points.E).line(points.D);
points.A = new Point(10, 10)
points.Acp = new Point(310, 40)
points.B = new Point(110, 70)
points.Bcp = new Point(-210, 40)
points.E = new Point(20, -5)
points.D = new Point(100, 85)
paths.curve = new Path()
.move(points.A)
.curve(points.Acp, points.Bcp, points.B)
paths.line = new Path().move(points.E).line(points.D)
for (let p of utils.lineIntersectsCurve(
points.D,
points.E,
points.A,
points.Acp,
points.Bcp,
points.B
)) {
snippets[getId()] = new Snippet("notch", p)
}
for (let p of utils.lineIntersectsCurve(
points.D,
points.E,
points.A,
points.Acp,
points.Bcp,
points.B
)) {
snippets[part.getId()] = new Snippet("notch", p);
return part
}
```
</Example>

View file

@ -1,7 +1,13 @@
---
title: linesIntersect()
title: utils.linesIntersect()
---
The `utils.linesInersect()` function finds the intersection between two line
segments. Returns a [Point](../point) object for the intersection, or `false`
if the lines don't intersect.
## Signature
```js
Point | false utils.linesIntersect(
Point A,
@ -11,34 +17,27 @@ Point | false utils.linesIntersect(
)
```
Finds the intersection between two line segments. Returns a [Point](../point) object
for the intersection, or `false` if the lines don't intersect.
## Example
<Example part="utils_linesintersect">
A Utils.linesIntersect() example
<Example caption="A Utils.linesIntersect() example">
```js
({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
points.C = new Point(15, 30)
points.D = new Point(60, 15)
paths.AB = new Path().move(points.A).line(points.B)
paths.CD = new Path().move(points.C).line(points.D)
snippets.X = new Snippet(
"notch",
utils.linesIntersect(points.A, points.B, points.C, points.D)
)
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.A = new Point(10, 10);
points.B = new Point(50, 40);
points.C = new Point(15, 30);
points.D = new Point(60, 15);
paths.AB = new Path().move(points.A).line(points.B);
paths.CD = new Path().move(points.C).line(points.D);
snippets.X = new Snippet(
"notch",
utils.linesIntersect(points.A, points.B, points.C, points.D)
);
```

View file

@ -0,0 +1,35 @@
---
title: utils.pctBasedOn()
---
The `utils.pctBasedOn()` function is a helper function to be used when
configuring [snapped percentage
options](/reference/api/part/config/options/pct/snap).
## Signature
```js
object utils.pctBasedOn(String measurement)
```
## Example
```js
const options = {
example: {
pct: 12,
min: 5,
max: 18,
snap: 3,
...pctBasedOn('chest')
}
}
```
## Notes
This will return an object with `toAbs` and `fromAbs` properties that calculate
the option's absolute and relative values based on a measurment. Refer to
[snapped percentage options](/reference/api/part/config/options/pct/snap) for
more details.

View file

@ -1,7 +1,13 @@
---
title: pointOnBeam()
title: utils.pointOnBeam()
---
The `utils.pointOnBeam()` function returns `true` if the point `check` lies on
the endless line that goes through `point1` and `point2`.
## Signature
```js
bool utils.pointOnBeam(
Point point1,
@ -11,73 +17,64 @@ bool utils.pointOnBeam(
)
```
Returns `true` if the point `check` lies on the endless line that goes through `point1` and `point2`.
The fourth parameter controls the precision. Lower numbers make the check less precise.
<Note>
## Example
###### Tweak precision only when needed
<Example caption="A Utils.pointOnBeam() example">
```js
({ Point, points, Path, paths, Snippet, snippets, getId, utils, part }) => {
Typically, you don't need to worry about precision. But occasionally, you may get
unexpected results because of floating point errors, rounding errors, or
points.from1 = new Point(10, 10)
points.to1 = new Point(90, 60)
points.from2 = new Point(10, 30)
points.to2 = new Point(90, 80)
points.b1 = new Point(170, 110)
points.b2 = new Point(170, 130)
const scatter = []
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (utils.pointOnBeam(points.from1, points.to1, point)) snippet = "notch"
else snippet = "bnotch"
snippets[getId()] = new Snippet(snippet, point)
if (utils.pointOnBeam(points.from2, points.to2, point, 0.01)) {
snippet = "notch"
} else snippet = "bnotch"
snippets[getId()] = new Snippet(snippet, point)
}
paths.line1 = new Path()
.move(points.from1)
.line(points.to1)
.addClass("fabric stroke-lg")
paths.lne1 = new Path()
.move(points.to1)
.line(points.b1)
.addClass("fabric dashed")
paths.line2 = new Path()
.move(points.from2)
.line(points.to2)
.addClass("fabric stroke-lg")
paths.lne2 = new Path()
.move(points.to2)
.line(points.b2)
.addClass("fabric dashed")
return part
}
```
</Example>
## Notes
Typically, you don't need to worry about precision. But occasionally, you may
get unexpected results because of floating point errors, rounding errors, or
cubic bezier juggling.
When that happens, you can lower the precision so you get what you expect.
</Note>
<Example part="utils_pointonbeam">
A Utils.pointOnBeam() example
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.from1 = new Point(10, 10);
points.to1 = new Point(90, 60);
points.from2 = new Point(10, 30);
points.to2 = new Point(90, 80);
points.b1 = new Point(170, 110);
points.b2 = new Point(170, 130);
let scatter = [];
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10));
}
}
let snippet;
for (let point of scatter) {
if (utils.pointOnBeam(points.from1, points.to1, point)) snippet = "notch";
else snippet = "bnotch";
snippets[part.getId()] = new Snippet(snippet, point);
if (utils.pointOnBeam(points.from2, points.to2, point, 0.01)) {
snippet = "notch";
} else snippet = "bnotch";
snippets[part.getId()] = new Snippet(snippet, point);
}
paths.line1 = new Path()
.move(points.from1)
.line(points.to1)
.attr("class", "fabric stroke-lg");
paths.lne1 = new Path()
.move(points.to1)
.line(points.b1)
.attr("class", "fabric dashed");
paths.line2 = new Path()
.move(points.from2)
.line(points.to2)
.attr("class", "fabric stroke-lg");
paths.lne2 = new Path()
.move(points.to2)
.line(points.b2)
.attr("class", "fabric dashed");
```

View file

@ -1,7 +1,12 @@
---
title: pointOnCurve()
title: utils.pointOnCurve()
---
The `utils.pointOnCurve()` function returns `true` if the point `check` lies on a
curve described by points `start`, `cp1`, `cp2`, and `end`.
## Signature
```js
bool utils.pointOnCurve(
Point start,
@ -12,57 +17,49 @@ bool utils.pointOnCurve(
)
```
Returns `true` if the point `check` lies on a curve described by points `start`, `cp1`, `cp2`, and `end`.
## Example
<Note>
<Example caption="A Utils.pointOnCurve() example">
```js
({ Point, points, Path, paths, Snippet, snippets, getId, utils, part }) => {
Keep in mind that calculations with Bezier curves are often aproximations.
points.start = new Point(10, 10)
points.cp1 = new Point(90, 10)
points.cp2 = new Point(10, 60)
points.end = new Point(90, 60)
const scatter = []
for (let i = 1; i < 19; i++) {
for (let j = 1; j < 14; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (
utils.pointOnCurve(
points.start,
points.cp1,
points.cp2,
points.end,
point
)
) {
snippet = "notch"
} else snippet = "bnotch"
snippets[getId()] = new Snippet(snippet, point)
}
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
.addClass("fabric stroke-lg")
</Note>
<Example part="utils_pointoncurve">
A Utils.pointOnCurve() example
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.start = new Point(10, 10);
points.cp1 = new Point(90, 10);
points.cp2 = new Point(10, 60);
points.end = new Point(90, 60);
## Notes
let scatter = [];
for (let i = 1; i < 19; i++) {
for (let j = 1; j < 14; j++) {
scatter.push(new Point(i * 10, j * 10));
}
}
let snippet;
for (let point of scatter) {
if (
utils.pointOnCurve(
points.start,
points.cp1,
points.cp2,
points.end,
point
)
) {
snippet = "notch";
} else snippet = "bnotch";
snippets[part.getId()] = new Snippet(snippet, point);
}
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
.attr("class", "fabric stroke-lg");
```
Keep in mind that calculations with Bezier curves are often aproximations.

View file

@ -1,7 +1,12 @@
---
title: pointOnLine()
title: utils.pointOnLine()
---
The `utils.pointOnLine()` function returns `true` if the point `check` lies on a
line segment from point `from` to point `to`.
## Signature
```js
bool utils.pointOnLine(
Point from,
@ -11,62 +16,57 @@ bool utils.pointOnLine(
)
```
Returns `true` if the point `check` lies on a line segment from point `from` to point `to`.
The fourth parameter controls the precision.
See [pointOnBeam](/reference/api/utils/pointonbeam).
The fourth parameter controls the precision. See [pointOnBeam](/reference/api/utils/pointonbeam).
## Example
<Example part="utils_pointonline">
A Utils.pointOnLine() example
<Example caption="A Utils.pointOnLine() example">
```js
({ Point, points, Path, paths, Snippet, snippets, getId, utils, part }) => {
points.from1 = new Point(10, 10)
points.to1 = new Point(90, 60)
points.from2 = new Point(10, 30)
points.to2 = new Point(90, 80)
points.b1 = new Point(170, 110)
points.b2 = new Point(170, 130)
const scatter = []
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (utils.pointOnLine(points.from1, points.to1, point)) snippet = "notch"
else snippet = "bnotch"
snippets[getId()] = new Snippet(snippet, point)
if (utils.pointOnLine(points.from2, points.to2, point, 0.01)) {
snippet = "notch"
} else snippet = "bnotch"
snippets[getId()] = new Snippet(snippet, point)
}
paths.line1 = new Path()
.move(points.from1)
.line(points.to1)
.addClass("fabric stroke-lg")
paths.lne1 = new Path()
.move(points.to1)
.line(points.b1)
.addClass("fabric dashed")
paths.line2 = new Path()
.move(points.from2)
.line(points.to2)
.addClass("fabric stroke-lg")
paths.lne2 = new Path()
.move(points.to2)
.line(points.b2)
.addClass("fabric dashed")
return part
}
```
</Example>
```js
let {
Point,
points,
Path,
paths,
Snippet,
snippets,
utils
} = part.shorthand();
points.from1 = new Point(10, 10);
points.to1 = new Point(90, 60);
points.from2 = new Point(10, 30);
points.to2 = new Point(90, 80);
points.b1 = new Point(170, 110);
points.b2 = new Point(170, 130);
let scatter = [];
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10));
}
}
let snippet;
for (let point of scatter) {
if (utils.pointOnLine(points.from1, points.to1, point)) snippet = "notch";
else snippet = "bnotch";
snippets[part.getId()] = new Snippet(snippet, point);
if (utils.pointOnLine(points.from2, points.to2, point, 0.01)) {
snippet = "notch";
} else snippet = "bnotch";
snippets[part.getId()] = new Snippet(snippet, point);
}
paths.line1 = new Path()
.move(points.from1)
.line(points.to1)
.attr("class", "fabric stroke-lg");
paths.lne1 = new Path()
.move(points.to1)
.line(points.b1)
.attr("class", "fabric dashed");
paths.line2 = new Path()
.move(points.from2)
.line(points.to2)
.attr("class", "fabric stroke-lg");
paths.lne2 = new Path()
.move(points.to2)
.line(points.b2)
.attr("class", "fabric dashed");
```

View file

@ -1,9 +1,12 @@
---
title: rad2deg()
title: utils.rad2deg()
---
The `utils.rad2dag()` function returns the radians you pass to it as degrees.
## Signature
```js
float rad2deg(float radians)
```
Returns the radians you pass to it as degrees.

View file

@ -1,12 +1,18 @@
---
title: round()
title: utils.round()
---
The `utils.round()` function rounds a value to two decimals.
## Signature
```js
float utils.round(float value)
```
Rounds a value to two decimals. For example:
## Example
- 0.1234 becomes 0.12
- 5.6789 becomes 5.68
```js
utils.round(0.1234) // 0.12
utils.round(5.6789) // 5.68
```

View file

@ -0,0 +1,81 @@
---
title: utils.splitCurve()
---
The `utils.splitCurve()` function splits a curve defined by 4 points `start`,
`cp1`, `cp2`, and `end` on the point `split` and returns an array holding both
halves.
## Signature
```js
array utils.splitCurve(
Point start,
Point cp1,
Point cp2,
Point end,
Point check,
)
```
## Example
<Example caption="A Utils.splitCurve() example">
```js
({ Point, points, Path, paths, utils, part }) => {
points.start = new Point(10, 10)
points.cp1 = new Point(100, 80)
points.cp2 = new Point(100, 0)
points.end = new Point(10, 50)
paths.example = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
.addClass('dotted stroke-xs')
points.split = paths.example.shiftFractionAlong(0.4)
const halves = utils.splitCurve(
points.start,
points.cp1,
points.cp2,
points.end,
points.split
)
for (let i=0; i<2; i++) {
const { start, cp1, cp2, end } = halves[i]
console.log({start, cp1, cp2,end})
paths[`segment${i}`] = new Path()
.move(start)
.curve(cp1, cp2, end)
.addClass('stroke-xl')
.attr('style', 'stroke-opacity: 0.5;')
}
paths.segment0.addClass('note')
paths.segment1.addClass('lining')
return part
}
```
</Example>
## Notes
The returned object has this signature:
```js
[
{
start: Point,
cp1: Point,
cp2: Point,
end: Point,
},
{
start: Point,
cp1: Point,
cp2: Point,
end: Point,
},
]

View file

@ -1,18 +1,25 @@
---
title: stretchToScale()
title: utils.stretchToScale()
---
The `utils.stretchToScale()` function calculates the scale for a given amount of
stretch.
## Signature
```js
float utils.stretchToScale(float stretch)
```
## Notes
The way people measure stretch intuitively is different from the way we handle stretch in code.
When people say _25% stretch_ they mean that 10cm fabric gets stretched to 12.5cm fabric.
In code and on our patterns, that means we need to scale things by 80%.
This method does that by returning:
This function does that by returning:
```js
1 / (1 + parseFloat(stretch));
1 / (1 + parseFloat(stretch))
```

View file

@ -1,18 +1,19 @@
---
title: units()
title: utils.units()
---
The `utils.units()` function converts the units `value` you pass it into a
formatted string for the `format` you pass it.
## Signature
```js
string utils.units(float value, string format = 'metric')
```
Converts the units `value` you pass it into a formatted string for the `format` you pass it.
Format must be either `imperial` or `metric` (the default).
<Tip>
## Notes
The [Part.shorthand](/reference/api/part/shorthand/) call provides a context-aware
`unit()` method that will call this method and pass it the units requested by the user.
</Tip>
A [part's `draft()` function](/reference/api/part/draft) receives a context-aware
`unit()` function that will call this function and pass it the units requested by the user.

View file

@ -6,13 +6,3 @@ order: zdd
You can find a list of all FreeSewing reference documentation below:
<ReadMore recurse />
<Related>
##### What makes a guide a guide?
Reference materials hold technical descriptions of the underlying technology and how to make use of it.
For more details, refer to [How we structure our documentation](/guides/docs).
</Related>

View file

@ -1,10 +1,9 @@
---
title: Hooks API
for: developers
about: Documents the available lifecycle hooks in Core and how to use them
title: Lifecycle hooks
---
A **hook** is a lifecycle event.
FreeSewing has **lifecycle hooks** that allow you extend its functionality by
hooking into a lifecycle event.
You can register a method for a hook. When the hook is triggered, your method will be
called. It will receive two parameters:

Some files were not shown because too many files have changed in this diff Show more